'const int' so với 'int const' làm tham số hàm trong C ++ và C


116

Xem xét:

int testfunc1 (const int a)
{
  return a;
}

int testfunc2 (int const a)
{
  return a;
}

Hai chức năng này giống nhau ở mọi khía cạnh hay có sự khác biệt?

Tôi quan tâm đến câu trả lời cho ngôn ngữ C, nhưng nếu có điều gì đó thú vị trong ngôn ngữ C ++, tôi cũng muốn biết.


Có từ khóa const trong C bây giờ không? Trước đây chưa từng có nhưng tôi không quá quen thuộc với tiêu chuẩn C 99.
Onorio Catenacci

8
Bạn không cần phải như vậy. C90 là đủ. Tuy nhiên, nó không có trong K&R C ban đầu.
Mark Baker

3
Nó là một từ khóa trong C89 và ANSI. Tôi không biết liệu đó có phải là một từ khóa trong thời Kerningham và Richie hay không.
Nils Pipenbrinck

7
Trang web này dịch "C gibberish" sang tiếng Anh cdecl.org
Motti

5
Tôi muốn nói "C gibberish sang tiếng Anh vô nghĩa", nhưng vẫn tốt :)
Kos

Câu trả lời:


175

const TT constgiống hệt nhau. Với các loại con trỏ, nó trở nên phức tạp hơn:

  1. const char* là một con trỏ đến một hằng số char
  2. char const* là một con trỏ đến một hằng số char
  3. char* const là một con trỏ hằng đến một (có thể thay đổi) char

Nói cách khác, (1) và (2) giống hệt nhau. Cách duy nhất để tạo con trỏ (thay vì con trỏ) constlà sử dụng hậu tố- const.

Đây là lý do tại sao nhiều người thích luôn đặt constở bên phải của kiểu (kiểu “East const”): nó làm cho vị trí của nó so với kiểu nhất quán và dễ nhớ (theo giai thoại, nó cũng có vẻ giúp dễ dạy hơn cho người mới bắt đầu ).


2
C có const, cho trước: static const char foo [] = "foo"; bạn tốt hơn không nên thay đổi foo.
James Antill

4
K&R C không có const; C90 (và C99) không. Nó hơi hạn chế so với C ++, nhưng nó rất hữu ích.
Mark Baker

điều này cũng đúng cho tài liệu tham khảo?
Ken

1
@Ken Vâng, nó giống nhau.
Konrad Rudolph

1
@ étale-cohomology Điểm tốt, đã thêm. Lẽ ra phải ở đó suốt.
Konrad Rudolph

339

Bí quyết là đọc ngược lại khai báo (từ phải sang trái):

const int a = 1; // read as "a is an integer which is constant"
int const a = 1; // read as "a is a constant integer"

Cả hai đều giống nhau. Vì thế:

a = 2; // Can't do because a is constant

Thủ thuật đọc ngược đặc biệt hữu ích khi bạn xử lý các khai báo phức tạp hơn như:

const char *s;      // read as "s is a pointer to a char that is constant"
char c;
char *const t = &c; // read as "t is a constant pointer to a char"

*s = 'A'; // Can't do because the char is constant
s++;      // Can do because the pointer isn't constant
*t = 'A'; // Can do because the char isn't constant
t++;      // Can't do because the pointer is constant

5
còn "char const * u" thì sao? Điều này có nội dung "Một con trỏ đến một ký tự không đổi" hoặc "một con trỏ không đổi đến một ký tự"? Có vẻ mơ hồ. Tiêu chuẩn nói trước đây, nhưng để tìm ra điều này, bạn phải tính đến các quy tắc ưu tiên và liên kết.
Panayiotis Karabassis

5
@PanayiotisKarabassis Tất cả phải được coi như một chuỗi các tính từ, không hoán đổi vị trí cho nhau. char const *, đọc từ trái sang phải là: "pointer, const, char". Nó là một con trỏ đến const char. Khi bạn nói "một con trỏ không đổi", tính từ "hằng số" nằm trên con trỏ. Vì vậy, đối với trường hợp đó, danh sách các tính từ của bạn thực sự phải là: "const, pointer, char". Nhưng bạn nói đúng, có sự mơ hồ đối với thủ thuật này. Đó thực sự là một "thủ thuật", hơn cả một "quy tắc" dứt khoát.
Ates Goral

5
Khi bạn khai báo sự kết hợp hoang dã của mảng, hàm, con trỏ và con trỏ hàm, tính năng đọc ngược sẽ không hoạt động nữa (đáng buồn là). Tuy nhiên, bạn có thể đọc các khai báo lộn xộn này theo hình xoắn ốc . Những người khác đã quá thất vọng vì họ đã phát minh ra cờ vây.
Martin JH

@Martin JH: Không lẽ chúng bị chia nhỏ bằng typedefs? Và / hoặc sử dụng tham chiếu để loại bỏ chuyển hướng?
Peter Mortensen

14

Không có sự khác biệt. Cả hai đều khai báo "a" là một số nguyên không thể thay đổi.

Nơi mà sự khác biệt bắt đầu xuất hiện là khi bạn sử dụng con trỏ.

Cả hai điều này:

const int *a
int const *a

khai báo "a" là một con trỏ đến một số nguyên không thay đổi. "a" có thể được gán cho, nhưng "* a" thì không.

int * const a

khai báo "a" là một con trỏ hằng đến một số nguyên. "* a" có thể được gán cho, nhưng "a" không thể.

const int * const a

khai báo "a" là một con trỏ hằng đến một số nguyên không đổi. Không thể gán "a" và "* a" cho.

static int one = 1;

int testfunc3 (const int *a)
{
  *a = 1; /* Error */
  a = &one;
  return *a;
}

int testfunc4 (int * const a)
{
  *a = 1;
  a = &one; /* Error */
  return *a;
}

int testfunc5 (const int * const a)
{
  *a = 1;   /* Error */
  a = &one; /* Error */
  return *a;
}

Ví dụ cuối cùng là giải thích đơn giản nhất, tuyệt vời!
xuất hiện

7

Prakash đúng là các khai báo đều giống nhau, mặc dù có thể giải thích thêm một chút về trường hợp con trỏ.

"const int * p" là một con trỏ đến một int không cho phép thay đổi int thông qua con trỏ đó. "int * const p" là một con trỏ đến một int không thể thay đổi để trỏ tới một int khác.

Xem https://isocpp.org/wiki/faq/const-correctness#const-ptr-vs-ptr-const .


Mỏ neo ("faq-18.5.) Bị hỏng. Nó nên tham chiếu đến cái nào (có một số với" const "và" * ")?
Peter Mortensen

@PeterMortensen: Vâng, thối liên kết. Cảm ơn. Tôi đã cập nhật liên kết.
Fred Larson

5

const intgiống hệt với int const, cũng như đúng với tất cả các kiểu vô hướng trong C. Nói chung, việc khai báo một tham số hàm vô hướng constlà không cần thiết, vì ngữ nghĩa gọi theo giá trị của C có nghĩa là bất kỳ thay đổi nào đối với biến là cục bộ đối với hàm bao quanh của nó.


4

Đây không phải là một câu trả lời trực tiếp mà là một mẹo liên quan. Để giữ cho mọi thứ thẳng, tôi luôn sử dụng đối lưu "đặt constở bên ngoài", trong đó "bên ngoài" tôi có nghĩa là xa bên trái hoặc bên phải. Bằng cách đó không có sự nhầm lẫn - hằng số áp dụng cho thứ gần nhất (hoặc loại hoặc *). Ví dụ,



int * const foo = ...; // Pointer cannot change, pointed to value can change
const int * bar = ...; // Pointer can change, pointed to value cannot change
int * baz = ...; // Pointer can change, pointed to value can change
const int * const qux = ...; // Pointer cannot change, pointed to value cannot change

6
Bạn có thể tốt hơn nên sử dụng quy tắc "const làm cho const bất cứ điều gì còn lại của nó". Ví dụ: "int * const foo" làm cho con trỏ "const", vì con trỏ được để cho nó. Tuy nhiên, bạn sẽ viết dòng thứ hai "int const * bar", làm cho int const, vì nó được để cho nó. "int const * const * qux", làm cho cả hai, int và con trỏ const, vì một trong hai đã được để lại cho nó.
Mecki

4

Chúng giống nhau, nhưng trong C ++, có một lý do chính đáng để luôn sử dụng const ở bên phải. Bạn sẽ nhất quán ở mọi nơi vì các hàm thành viên const phải được khai báo theo cách này:

int getInt() const;

Nó thay đổi thiscon trỏ trong hàm từ Foo * constthành Foo const * const. Xem tại đây.


3
Đây là một loại const hoàn toàn khác.
Justin Meiners

1
Tại sao điều này lại hoàn toàn khác? Đủ khác biệt để kiếm được một phiếu giảm giá.
Nick Westgate

1
Có, câu hỏi là về sự khác biệt giữa "const int" và "int const", câu trả lời của bạn không liên quan gì đến nó.
Justin Meiners

1
Tôi đã nói chúng giống nhau. Chưa hết, các câu trả lời được chấp nhận và được bình chọn nhiều nhất cũng cung cấp thêm thông tin về các loại con trỏ. Bạn cũng đã tán thành những điều đó?
Nick Westgate

3

Vâng, chúng giống nhau chỉ vì int

và khác nhau cho int*


5
(const int *) và (int const *) giống nhau, chúng chỉ khác với (int * const).
James Antill

3

Tôi nghĩ trong trường hợp này, chúng giống nhau, nhưng đây là một ví dụ mà thứ tự quan trọng:

const int* cantChangeTheData;
int* const cantChangeTheAddress;

2
Thật vậy, nhưng int const * giống với thứ nhất, vì vậy thứ tự của int và const không quan trọng, nó chỉ là thứ tự của * và const mà thôi.
Mark Baker
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.