Sự khác biệt giữa const int *, const int * const và int const * là gì?


1356

Tôi luôn luôn lộn xộn lên làm thế nào để sử dụng const int*, const int * constint const *chính xác. Có một bộ quy tắc xác định những gì bạn có thể và không thể làm?

Tôi muốn biết tất cả những việc cần làm và tất cả các quy định về nhiệm vụ, chuyển đến các chức năng, v.v.


175
Bạn có thể sử dụng "Quy tắc theo chiều kim đồng hồ / xoắn ốc" để giải mã hầu hết các khai báo C và C ++.
James McNellis

52
cdecl.org là một trang web tuyệt vời tự động dịch các khai báo C cho bạn.
Dave Gallagher

6
@Calmarius: bắt đầu nơi tên loại / nên là, di chuyển sang phải khi bạn có thể, sang trái khi bạn cần . int *(*)(char const * const). Bắt đầu bên phải dấu ngoặc đơn *sau đó chúng ta phải di chuyển sang trái : pointer. Bên ngoài parens, chúng ta có thể di chuyển sang phải : pointer to function of .... Sau đó, chúng tôi phải di chuyển sang trái : pointer to function of ... that returns pointer to int. Lặp lại để mở rộng tham số (the ...) : pointer to function of (constant pointer to constant char) that returns pointer to int. Khai báo một dòng tương đương sẽ là gì trong một ngôn ngữ dễ đọc như Pascal?
Mark K Cowan

1
@MarkKCowan Trong Pascal, nó sẽ giống như thế function(x:^char):^int. Có các loại hàm ngụ ý một con trỏ tới một hàm nên không cần chỉ định nó và Pascal không thực thi tính chính xác const. Nó có thể được đọc từ trái sang phải.
Calmarius

5
Điều đầu tiên bên trái của "const" là những gì không đổi. Nếu "const" là thứ xa nhất bên trái, thì thứ đầu tiên bên phải của nó là thứ không đổi.
Cupcake

Câu trả lời:


2208

Đọc ngược lại (như được điều khiển bởi Quy tắc theo chiều kim đồng hồ / xoắn ốc ):

  • int* - con trỏ tới int
  • int const * - con trỏ tới const int
  • int * const - const con trỏ tới int
  • int const * const - const con trỏ tới const int

Bây giờ đầu tiên constcó thể ở hai bên của loại vì vậy:

  • const int * == int const *
  • const int * const == int const * const

Nếu bạn muốn phát điên, bạn có thể làm những việc như thế này:

  • int ** - con trỏ tới con trỏ tới int
  • int ** const - một con trỏ const tới một con trỏ tới int
  • int * const * - một con trỏ tới một con trỏ const tới một int
  • int const ** - một con trỏ tới một con trỏ tới const int
  • int * const * const - một con trỏ const tới một con trỏ const tới một int
  • ...

Và để chắc chắn rằng chúng tôi rõ ràng về ý nghĩa của const:

int a = 5, b = 10, c = 15;

const int* foo;     // pointer to constant int.
foo = &a;           // assignment to where foo points to.

/* dummy statement*/
*foo = 6;           // the value of a can´t get changed through the pointer.

foo = &b;           // the pointer foo can be changed.



int *const bar = &c;  // constant pointer to int 
                      // note, you actually need to set the pointer 
                      // here because you can't change it later ;)

*bar = 16;            // the value of c can be changed through the pointer.    

/* dummy statement*/
bar = &a;             // not possible because bar is a constant pointer.           

foolà một con trỏ biến thành một số nguyên không đổi. Điều này cho phép bạn thay đổi những gì bạn trỏ đến nhưng không phải là giá trị mà bạn trỏ đến. Thông thường, điều này được nhìn thấy với các chuỗi kiểu C trong đó bạn có một con trỏ tới a const char. Bạn có thể thay đổi chuỗi nào bạn trỏ đến nhưng bạn không thể thay đổi nội dung của các chuỗi này. Điều này rất quan trọng khi chính chuỗi nằm trong phân đoạn dữ liệu của chương trình và không nên thay đổi.

barlà một con trỏ cố định hoặc cố định đến một giá trị có thể thay đổi. Đây giống như một tài liệu tham khảo mà không cần thêm cú pháp. Vì thực tế này, thông thường bạn sẽ sử dụng một tham chiếu trong đó bạn sẽ sử dụng một T* constcon trỏ trừ khi bạn cần cho phép các NULLcon trỏ.


482
Tôi muốn nối thêm quy tắc ngón tay cái có thể giúp bạn nhớ cách khám phá xem 'const' áp dụng cho con trỏ hay dữ liệu nhọn: tách câu lệnh ở dấu asterix, sau đó, nếu từ khóa const xuất hiện ở phần bên trái (như trong 'const int * foo') - nó thuộc về dữ liệu nhọn, nếu nó nằm ở phần bên phải ('int * const bar') - đó là về con trỏ.
Michael

14
@Michael: Kudos nói với Michael về một quy tắc đơn giản như vậy để ghi nhớ / hiểu quy tắc const.
sivabudh

10
@Jeffrey: đọc ngược lại hoạt động tốt miễn là không có dấu ngoặc đơn. Sau đó, hãy ... sử dụng typedefs
Vịt Mooing

12
+1, mặc dù tóm tắt tốt hơn sẽ là: đọc các khai báo con trỏ ngược , điều đó có nghĩa là, gần với tuyên bố của @Michael: dừng việc đọc từ trái sang phải bình thường ở dấu hoa thị đầu tiên .
Sói

3
@gedamial nó hoạt động, nó hoạt động tốt, nhưng bạn phải gán nó cùng lúc bạn khai báo nó (vì bạn không thể gán lại một "con trỏ const"). const int x = 0; const int *const px = &x; const int *const *const p = &px;hoạt động tốt
RastaJedi

356

Đối với những người không biết về Quy tắc theo chiều kim đồng hồ / xoắn ốc: Bắt đầu từ tên của biến, di chuyển đồng hồ chính xác (trong trường hợp này, di chuyển lùi) sang con trỏ hoặc loại tiếp theo . Lặp lại cho đến khi biểu thức kết thúc.

Đây là một bản demo:

con trỏ tới int

con trỏ const đến int const

con trỏ tới int const

con trỏ tới const int

con trỏ const đến int


8
@Jan liên kết cho ví dụ phức tạp không có quyền. bạn có thể đăng nó trực tiếp ở đây, hoặc loại bỏ các hạn chế xem?
R71

8
@Rog nó từng có tất cả các quyền truy cập mở ... Thật không may, tôi đã không viết bài viết và không có quyền truy cập bản thân mình, thật không may. Tuy nhiên, đây là phiên bản lưu trữ của bài viết vẫn hoạt động: archive.is/SsfMX
Jan Rüegg

8
Ví dụ phức tạp vẫn chỉ từ phải sang trái, nhưng bao gồm việc giải quyết dấu ngoặc đơn theo cách thông thường. Toàn bộ điều xoắn ốc theo chiều kim đồng hồ không làm cho điều đó dễ dàng hơn.
Matthew đọc

4
Ví dụ cuối cùng: void (*signal(int, void (*fp)(int)))(int);từ archive.is/SsfMX
naXa

3
Đừng dựa vào quy tắc này. Đây không phải là phổ quát. Có một số trường hợp nó thất bại.
haccks

150

Tôi nghĩ mọi thứ đã được trả lời ở đây rồi, nhưng tôi chỉ muốn nói thêm rằng bạn nên cẩn thận với typedefs! Họ KHÔNG chỉ thay thế văn bản.

Ví dụ:

typedef char *ASTRING;
const ASTRING astring;

Các loại astringchar * const, không const char *. Đây là một lý do tôi luôn có xu hướng đặt constbên phải của loại, và không bao giờ bắt đầu.


20
Và đối với tôi đây là lý do để không bao giờ đánh máy con trỏ. Tôi không thấy lợi ích trong những thứ như typedef int* PINT(tôi cho rằng thứ gì đó xuất phát từ thực tiễn trong C và nhiều nhà phát triển tiếp tục làm điều đó). Tuyệt vời, tôi đã thay thế *bằng một P, nó không tăng tốc độ gõ, cộng với việc giới thiệu vấn đề bạn đề cập.
Mephane

1
@Mephane - Tôi có thể thấy điều đó. Tuy nhiên, đối với tôi, có vẻ hơi lạc hậu để tránh một tính năng ngôn ngữ hay để tiếp tục sử dụng quy tắc cú pháp đặc biệt (về vị trí "const"), thay vì tránh sử dụng quy tắc cú pháp đặc biệt để bạn có thể sử dụng tính năng ngôn ngữ này một cách an toàn .
TED

6
@Mephane PINTthực sự là một cách sử dụng khá ngu ngốc của một typedef, đặc biệt là vì nó khiến tôi nghĩ rằng các cửa hàng hệ thống sử dụng bia cho bộ nhớ. typedef s khá hữu ích để xử lý các con trỏ tới các hàm.
Tiếp

5
@KazDragon CẢM ƠN! Nếu không có nó, tôi sẽ rối tung với tất cả những thứ được đánh máy PVOID, LPTSTRthứ trong Win32 api!
David Lee

2
@Mephane: Tôi đã phải sử dụng một vài lần khi sử dụng một số macro kế thừa nhất định được viết để chấp nhận một loại, nhưng sẽ bị phá vỡ nếu loại đó không phải là một định danh chữ và số. :)
Groo

56

Giống như khá nhiều người chỉ ra:

Sự khác biệt giữa const X* p, X* const pconst X* const p?

Bạn phải đọc khai báo con trỏ từ phải sang trái.

  • const X* p có nghĩa là "p trỏ đến X là const": đối tượng X không thể thay đổi thông qua p.

  • X* const p có nghĩa là "p là một con trỏ const thành X không phải là const": bạn không thể tự thay đổi con trỏ p, nhưng bạn có thể thay đổi đối tượng X thông qua p.

  • const X* const p có nghĩa là "p là con trỏ const thành X là const": bạn không thể tự thay đổi con trỏ p, cũng như không thể thay đổi đối tượng X qua p.


3
Đừng quên rằng const X* p;== X const * p;như trong"p points to an X that is const": the X object can't be changed via p.
Jesse Chisholm

đơn giản và tốt đẹp khám phá!
Edison Lo

50
  1. Tham khảo liên tục:

    Một tham chiếu đến một biến (ở đây int), là hằng số. Chúng tôi chuyển biến như một tham chiếu là chủ yếu, bởi vì các tham chiếu có kích thước nhỏ hơn giá trị thực, nhưng có một tác dụng phụ và đó là vì nó giống như một bí danh cho biến thực. Chúng tôi có thể vô tình thay đổi biến chính thông qua quyền truy cập đầy đủ vào bí danh, vì vậy chúng tôi làm cho nó không đổi để ngăn chặn tác dụng phụ này.

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
  2. Con trỏ liên tục

    Khi một con trỏ không đổi trỏ đến một biến thì nó không thể trỏ đến bất kỳ biến nào khác.

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
  3. Con trỏ đến hằng số

    Một con trỏ thông qua đó người ta không thể thay đổi giá trị của một biến mà nó trỏ được gọi là một con trỏ thành hằng số.

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
  4. Con trỏ không đổi đến một hằng

    Một con trỏ không đổi thành hằng số là một con trỏ không thể thay đổi địa chỉ mà nó trỏ đến và cũng không thể thay đổi giá trị được giữ tại địa chỉ đó.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error

20

Nguyên tắc chung là consttừ khóa áp dụng cho những gì đi trước nó ngay lập tức. Ngoại lệ, một khởi đầu constáp dụng cho những gì sau.

  • const int*là giống như int const*và có nghĩa là "con trỏ đến hằng int" .
  • const int* constgiống như int const* constvà có nghĩa là "con trỏ không đổi đến hằng int" .

Chỉnh sửa: Đối với Dos và Don'ts, nếu câu trả lời này không đủ, bạn có thể chính xác hơn về những gì bạn muốn không?


19

Câu hỏi này cho thấy chính xác lý do tại sao tôi thích làm mọi thứ theo cách tôi đã đề cập trong câu hỏi của mình là const sau khi loại id được chấp nhận?

Nói tóm lại, tôi thấy cách dễ nhất để ghi nhớ quy tắc là "const" đi sau điều nó áp dụng. Vì vậy, trong câu hỏi của bạn, "int const *" có nghĩa là int không đổi, trong khi "int * const" có nghĩa là con trỏ không đổi.

Nếu ai đó quyết định đặt nó ở phía trước (ví dụ: "const int *"), như một ngoại lệ đặc biệt trong trường hợp đó, nó sẽ áp dụng cho điều sau nó.

Nhiều người thích sử dụng ngoại lệ đặc biệt đó vì họ nghĩ rằng nó trông đẹp hơn. Tôi không thích nó, bởi vì nó là một ngoại lệ, và do đó nhầm lẫn mọi thứ.


2
Tôi bị rách về vấn đề này. Theo logic nó có ý nghĩa. Tuy nhiên, hầu hết các nhà phát triển c ++ sẽ viết const T*và nó đã trở nên tự nhiên hơn. Mức độ thường xuyên bạn sử dụng một cách nào đó T* const, thường thì một tài liệu tham khảo sẽ làm tốt. Tôi đã nhận được một chút bởi tất cả điều này một lần khi muốn boost::shared_ptr<const T>và thay vào đó đã viết const boost::shared_ptr<T>. Vấn đề tương tự trong một bối cảnh hơi khác nhau.
Matt Giá

Trên thực tế, tôi sử dụng con trỏ liên tục thường xuyên hơn tôi sử dụng hằng số. Ngoài ra, bạn phải suy nghĩ về cách bạn sẽ phản ứng khi có con trỏ tới con trỏ (v.v.) Phải thừa nhận rằng những thứ đó hiếm hơn, nhưng sẽ rất tốt khi nghĩ về mọi thứ theo cách bạn có thể xử lý những tình huống này bằng cách sử dụng.
TED

1
Một lợi thế tuyệt vời khác của việc đặt const ở bên phải của kiểu đó là bây giờ mọi thứ ở bên trái của bất kỳ constđều là kiểu của const, và mọi thứ ở bên phải của nó là cái thực sự là const. Lấy int const * const * p;một ví dụ. Không, tôi thường không viết như vậy, đây chỉ là một ví dụ. Đầu tiên const: gõ int, và int là const là nội dung của con trỏ const là nội dung của p. constConst thứ hai: type là con trỏ tới int, const constect là nội dung củap
dgnuff 15/03/18

18

Sử dụng đơn giản const.

Việc sử dụng đơn giản nhất là khai báo một hằng số được đặt tên. Để làm điều này, người ta khai báo một hằng số như thể nó là một biến nhưng thêm vào consttrước nó. Người ta phải khởi tạo nó ngay lập tức trong hàm tạo bởi vì, tất nhiên, người ta không thể đặt giá trị sau này vì điều đó sẽ làm thay đổi nó. Ví dụ:

const int Constant1=96; 

sẽ tạo một hằng số nguyên, được gọi một cách đơn giản Constant1, với giá trị 96.

Các hằng số như vậy rất hữu ích cho các tham số được sử dụng trong chương trình nhưng không cần thay đổi sau khi chương trình được biên dịch. Nó có một lợi thế cho các lập trình viên so với #definelệnh tiền xử lý C ở chỗ nó được hiểu và sử dụng bởi chính trình biên dịch, không chỉ được thay thế vào văn bản chương trình bởi bộ tiền xử lý trước khi đến trình biên dịch chính, vì vậy các thông báo lỗi sẽ hữu ích hơn nhiều.

Nó cũng hoạt động với các con trỏ nhưng người ta phải cẩn thận constđể xác định xem con trỏ hoặc những gì nó trỏ đến là không đổi hoặc cả hai. Ví dụ:

const int * Constant2 

khai báo đó Constant2là con trỏ biến thành một số nguyên không đổi và:

int const * Constant2

là một cú pháp thay thế làm tương tự, trong khi

int * const Constant3

khai báo đó Constant3là con trỏ không đổi cho một số nguyên biến và

int const * const Constant4

tuyên bố đó Constant4là con trỏ không đổi đến một số nguyên không đổi. Về cơ bản, "const" áp dụng cho bất cứ điều gì ở bên trái của nó (trừ khi không có gì trong trường hợp đó, nó áp dụng cho bất cứ điều gì là quyền ngay lập tức của nó).

ref: http://duramecho.com/ComputerIn information / WhyHowCppConst.html


9

Tôi đã có cùng nghi ngờ với bạn cho đến khi tôi bắt gặp cuốn sách này của C ++ Guru Scott Meyers. Tham khảo Mục thứ ba trong cuốn sách này, nơi ông nói chi tiết về việc sử dụng const.

Chỉ cần làm theo lời khuyên này

  1. Nếu từ constxuất hiện ở bên trái dấu hoa thị, những gì được chỉ ra là không đổi
  2. Nếu từ constxuất hiện ở bên phải dấu hoa thị, thì chính con trỏ không đổi
  3. Nếu constxuất hiện ở cả hai phía, cả hai đều không đổi

7

Nó đơn giản nhưng khó khăn. Xin lưu ý rằng chúng ta có thể trao đổi các constvòng loại với bất kỳ loại dữ liệu ( int, char, float, vv).

Hãy xem các ví dụ dưới đây.


const int *p==> *plà chỉ đọc [ plà một con trỏ tới một số nguyên không đổi]

int const *p==> *plà chỉ đọc [ plà một con trỏ tới một số nguyên không đổi]


int *p const==> Tuyên bố sai . Trình biên dịch ném lỗi cú pháp.

int *const p==> plà chỉ đọc [ plà một con trỏ không đổi đến một số nguyên]. Vì con trỏ pở đây là chỉ đọc, nên khai báo và định nghĩa phải ở cùng một chỗ.


const int *p const ==> Tuyên bố sai . Trình biên dịch ném lỗi cú pháp.

const int const *p ==> *plà chỉ đọc

const int *const p1 ==> *ppchỉ đọc [ plà một con trỏ không đổi đến một số nguyên không đổi]. Vì con trỏ pở đây là chỉ đọc, nên khai báo và định nghĩa phải ở cùng một chỗ.


int const *p const ==> Tuyên bố sai . Trình biên dịch ném lỗi cú pháp.

int const int *p ==> Tuyên bố sai . Trình biên dịch ném lỗi cú pháp.

int const const *p ==> *plà chỉ đọc và tương đương vớiint const *p

int const *const p ==> *ppchỉ đọc [ plà một con trỏ không đổi đến một số nguyên không đổi]. Vì con trỏ pở đây là chỉ đọc, nên khai báo và định nghĩa phải ở cùng một chỗ.


6

Có nhiều điểm tinh tế khác xung quanh tính chính xác của const trong C ++. Tôi cho rằng câu hỏi ở đây chỉ đơn giản là về C, nhưng tôi sẽ đưa ra một số ví dụ liên quan vì thẻ là C ++:

  • Bạn thường truyền các đối số lớn như các chuỗi TYPE const &để ngăn đối tượng bị sửa đổi hoặc sao chép. Thí dụ :

    TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }

    Nhưng TYPE & constlà vô nghĩa vì tài liệu tham khảo luôn là const.

  • Bạn phải luôn luôn gắn nhãn các phương thức lớp không sửa đổi lớp là const, nếu không bạn không thể gọi phương thức từ TYPE const &tham chiếu. Thí dụ :

    bool TYPE::operator==(const TYPE &rhs) const { ... }

  • Có những tình huống phổ biến trong đó cả giá trị trả về và phương thức nên là const. Thí dụ :

    const TYPE TYPE::operator+(const TYPE &rhs) const { ... }

    Trong thực tế, các phương thức const không được trả về dữ liệu của lớp bên trong dưới dạng tham chiếu đến không-const.

  • Kết quả là, người ta thường phải tạo cả phương thức const và phương thức không const bằng cách sử dụng quá tải const. Ví dụ: nếu bạn xác định T const& operator[] (unsigned i) const;, thì có lẽ bạn cũng sẽ muốn phiên bản không phải được cung cấp bởi:

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

Afaik, không có hàm const trong C, các hàm không phải là thành viên không thể là const trong C ++, các phương thức const có thể có tác dụng phụ và trình biên dịch không thể sử dụng các hàm const để tránh các lệnh gọi hàm trùng lặp. Trong thực tế, ngay cả một int const &tài liệu tham khảo đơn giản cũng có thể chứng kiến ​​giá trị mà nó đề cập đến được thay đổi ở nơi khác.


6

Cú pháp khai báo C và C ++ đã nhiều lần được mô tả là một thử nghiệm thất bại, bởi các nhà thiết kế ban đầu.

Thay vào đó, hãy đặt tên cho con trỏ kiểu kiểu thành tên lửa Type; Tôi sẽ gọi nó Ptr_:

template< class Type >
using Ptr_ = Type*;

Bây giờ Ptr_<char>là một con trỏ đến char.

Ptr_<const char>là một con trỏ tới const char.

const Ptr_<const char>là một constcon trỏ đến const char.

Đó

nhập mô tả hình ảnh ở đây


3
Bạn có một trích dẫn cho câu đầu tiên?
sp2danny

@ sp2danny: Cú pháp của Google Coogling C không thành công trong thí nghiệm, chỉ ho một số cuộc phỏng vấn với Bjarne Stroustrup, nơi anh bày tỏ ý kiến của mình theo hướng đó, ví dụ, tôi coi cú pháp khai báo C là một thử nghiệm thất bại trong cuộc phỏng vấn Slashdot. Vì vậy, tôi không có tài liệu tham khảo cho tuyên bố về quan điểm của các nhà thiết kế ban đầu của C. Tôi đoán nó có thể được tìm thấy bởi một nỗ lực nghiên cứu đủ mạnh, hoặc có thể từ chối chỉ bằng cách hỏi họ, nhưng tôi nghĩ nó tốt hơn bây giờ. với phần đó của yêu sách, vẫn chưa quyết định và có khả năng đúng :)
Chúc mừng và hth. - Alf

1
"Cú pháp khai báo C và C ++ đã nhiều lần được mô tả là một thử nghiệm thất bại, bởi các nhà thiết kế ban đầu." sai cho C xin vui lòng thay đổi câu của bạn về C hoặc cung cấp một số trích dẫn.
Starg Nghiệp dư

3
@Starg Nghiệp dư: Rõ ràng bạn đã đọc các bình luận trước đó và tìm thấy một cái gì đó bạn có thể tận dụng cho ngành sư phạm. Chúc may mắn với cuộc sống của bạn. Dù sao, những người thời xưa như tôi nhớ rất nhiều điều mà chúng ta không thể chứng minh được nếu không tham gia vào nghiên cứu rất tốn thời gian. Bạn chỉ có thể lấy từ của tôi.
Chúc mừng và hth. - Alf


6

Đối với tôi, vị trí consttức là cho dù nó xuất hiện TRÁI hoặc PHẢI hoặc trên cả TRÁI và PHẢI liên quan đến việc *giúp tôi tìm ra ý nghĩa thực tế.

  1. A constđến TRÁI *chỉ ra rằng đối tượng được trỏ bởi con trỏ là một constđối tượng.

  2. A constđến QUYỀN *chỉ ra rằng con trỏ là constcon trỏ.

Bảng dưới đây được lấy từ Trình đọc khóa học Phòng thí nghiệm lập trình C ++ của Stanford CS106L.

nhập mô tả hình ảnh ở đây


3

Điều này chủ yếu giải quyết dòng thứ hai: thực tiễn tốt nhất, bài tập, tham số chức năng, vv

Luyện tập chung. Cố gắng làm mọi thứ constmà bạn có thể. Hoặc để nói theo cách khác, làm cho mọi thứ constbắt đầu, và sau đó loại bỏ chính xác bộ consts tối thiểu cần thiết để cho phép chương trình hoạt động. Đây sẽ là một trợ giúp lớn trong việc đạt được tính chính xác và sẽ giúp đảm bảo rằng các lỗi tinh vi không được giới thiệu khi mọi người thử và gán vào những thứ mà họ không cần phải sửa đổi.

Tránh const_cast <> như bệnh dịch hạch. Có một hoặc hai trường hợp sử dụng hợp pháp cho nó, nhưng chúng rất ít và xa giữa. Nếu bạn đang cố gắng thay đổi một constđối tượng, bạn sẽ làm tốt hơn rất nhiều để tìm bất cứ ai tuyên bố nó constở tốc độ đầu tiên và nói chuyện với họ để đạt được sự đồng thuận về những gì sẽ xảy ra.

Mà dẫn rất gọn gàng vào bài tập. Bạn chỉ có thể gán vào một cái gì đó nếu nó không phải là const. Nếu bạn muốn gán vào một cái gì đó là const, xem ở trên. Hãy nhớ rằng trong các tuyên bố int const *foo;int * const bar;những điều khác nhau là const- các câu trả lời khác ở đây đã đề cập đến vấn đề đó một cách đáng ngưỡng mộ, vì vậy tôi sẽ không đi sâu vào nó.

Các tham số chức năng:

Truyền theo giá trị: ví dụ: void func(int param)bạn không quan tâm cách này hay cách khác tại trang web gọi điện. Đối số có thể được đưa ra là có các trường hợp sử dụng để khai báo hàm như void func(int const param)nhưng điều đó không có tác dụng đối với người gọi, chỉ đối với chính hàm đó, trong đó bất kỳ giá trị nào được truyền đều không thể thay đổi bởi hàm trong suốt cuộc gọi.

Chuyển qua tham chiếu: ví dụ: void func(int &param)Bây giờ nó làm cho một sự khác biệt. Như vừa tuyên bố funcđược phép thay đổi param, và bất kỳ trang web gọi điện nào cũng nên sẵn sàng để giải quyết hậu quả. Thay đổi tuyên bố để void func(int const &param)thay đổi hợp đồng và đảm bảo rằng funcbây giờ không thể thay đổi param, có nghĩa là những gì được thông qua là những gì sẽ trở lại. Như những người khác đã lưu ý rằng điều này rất hữu ích cho việc vượt qua một đối tượng lớn mà bạn không muốn thay đổi. Truyền tham chiếu rẻ hơn rất nhiều so với chuyển một đối tượng lớn theo giá trị.

Đi ngang qua con trỏ: ví dụ void func(int *param)void func(int const *param)Hai là khá nhiều đồng nghĩa với các đối tác tham khảo của họ, với sự báo trước đó được gọi là chức năng hiện nay cần phải kiểm tra xem có nullptrtrừ một số đảm bảo việc bảo đảm hợp đồng khác funcmà nó sẽ không bao giờ nhận được một nullptrtrong param.

Ý kiến ​​về chủ đề đó. Chứng minh sự đúng đắn trong trường hợp như thế này là cực kỳ khó khăn, thật quá dễ để phạm sai lầm. Vì vậy, đừng nắm bắt cơ hội và luôn kiểm tra các tham số con trỏ nullptr. Bạn sẽ tự cứu mình khỏi nỗi đau và đau khổ và khó tìm ra lỗi trong thời gian dài. Và đối với chi phí kiểm tra, nó rất rẻ và trong trường hợp phân tích tĩnh được tích hợp trong trình biên dịch có thể quản lý nó, trình tối ưu hóa sẽ bỏ qua nó. Bật Tạo mã thời gian liên kết cho MSVC hoặc WOPR (tôi nghĩ) cho GCC và bạn sẽ có được chương trình rộng, tức là ngay cả trong các lệnh gọi chức năng vượt qua ranh giới mô-đun mã nguồn.

Vào cuối ngày, tất cả những điều trên tạo nên một trường hợp rất chắc chắn để luôn thích tham chiếu đến con trỏ. Họ chỉ an toàn hơn tất cả các vòng.


3

Các const với int ở hai bên sẽ làm cho con trỏ thành int int :

const int *ptr=&i;

hoặc là:

int const *ptr=&i;

constsau *sẽ tạo con trỏ liên tục đến int :

int *const ptr=&i;

Trong trường hợp này, tất cả chúng là con trỏ tới số nguyên không đổi , nhưng không có cái nào trong số này là con trỏ không đổi:

 const int *ptr1=&i, *ptr2=&j;

Trong trường hợp này, tất cả đều là con trỏ tới số nguyên không đổi và ptr2 là con trỏ không đổi thành số nguyên không đổi . Nhưng ptr1 không phải là con trỏ không đổi:

int const *ptr1=&i, *const ptr2=&j;

3
  • nếu constở phía bên trái của *, nó đề cập đến giá trị (nó không quan trọng cho dù đó là const inthay int const)
  • nếu constở bên phải của *nó, nó đề cập đến chính con trỏ
  • nó có thể là cả hai cùng một lúc

Một điểm quan trọng: const int *p không có nghĩa là giá trị bạn đang đề cập là không đổi !! . Điều đó có nghĩa là bạn không thể thay đổi nó thông qua con trỏ đó (nghĩa là bạn không thể gán $ * p = ... `). Giá trị có thể được thay đổi theo những cách khác. Ví dụ

int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error 

Điều này có nghĩa là được sử dụng chủ yếu trong chữ ký hàm, để đảm bảo rằng hàm không thể vô tình thay đổi các đối số được truyền.


2

Chỉ vì mục đích hoàn chỉnh cho C theo những giải thích khác, không chắc chắn cho C ++.

  • pp - con trỏ tới con trỏ
  • con trỏ p
  • dữ liệu - điều được chỉ ra, trong các ví dụ x
  • in đậm - biến chỉ đọc

Con trỏ

  • dữ liệu p - int *p;
  • dữ liệu p -int const *p;
  • dữ liệu p -int * const p;
  • dữ liệu p -int const * const p;

Con trỏ tới con trỏ

  1. dữ liệu pp p - int **pp;
  2. dữ liệu pp p -int ** const pp;
  3. dữ liệu pp p -int * const *pp;
  4. dữ liệu pp p -int const **pp;
  5. dữ liệu pp p -int * const * const pp;
  6. dữ liệu pp p -int const ** const pp;
  7. dữ liệu pp p -int const * const *pp;
  8. dữ liệu pp p -int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

N-Dereference

Cứ tiếp tục, nhưng có thể nhân loại sẽ thông báo cho bạn.

int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;

printf("%d \n", ****pppp);

0
  1. const int*- con trỏ đến intđối tượng không đổi .

Bạn có thể thay đổi giá trị của con trỏ; bạn không thể thay đổi giá trị của intđối tượng, con trỏ trỏ tới.


  1. const int * const- Con trỏ không đổi đến intđối tượng không đổi .

Bạn không thể thay đổi giá trị của con trỏ cũng như giá trị của intđối tượng mà con trỏ trỏ tới.


  1. int const *- con trỏ đến intđối tượng không đổi .

Câu lệnh này tương đương với 1. const int*- Bạn có thể thay đổi giá trị của con trỏ nhưng bạn không thể thay đổi giá trị của intđối tượng, con trỏ trỏ tới.


Trên thực tế, có một lựa chọn thứ 4:

  1. int * const- Con trỏ không đổi đến intđối tượng.

Bạn có thể thay đổi giá trị của đối tượng mà con trỏ trỏ tới nhưng bạn không thể thay đổi giá trị của chính con trỏ. Con trỏ sẽ luôn trỏ đến cùng một intđối tượng nhưng giá trị này của intđối tượng này có thể được thay đổi.


Nếu bạn muốn xác định một loại cấu trúc C hoặc C ++ nhất định, bạn có thể sử dụng Quy tắc theo chiều kim đồng hồ / xoắn ốc do David Anderson thực hiện; nhưng không nhầm lẫn với Quy tắc của Anderson được thực hiện bởi Ross J. Anderson, một điều khá khác biệt.

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.