Vị trí của dấu hoa thị trong khai báo con trỏ


92

Gần đây tôi đã quyết định rằng cuối cùng tôi cũng phải học C / C ++, và có một điều tôi không thực sự hiểu về con trỏ hay chính xác hơn là định nghĩa của chúng.

Làm thế nào về các ví dụ này:

  1. int* test;
  2. int *test;
  3. int * test;
  4. int* test,test2;
  5. int *test,test2;
  6. int * test,test2;

Bây giờ, theo sự hiểu biết của tôi, ba trường hợp đầu tiên đều hoạt động giống nhau: Kiểm tra không phải là một int, mà là một con trỏ tới một.

Bộ ví dụ thứ hai phức tạp hơn một chút. Trong trường hợp 4, cả test và test2 sẽ là con trỏ đến một int, trong khi trong trường hợp 5, chỉ test là một con trỏ, trong khi test2 là int "thực". Còn trường hợp 6 thì sao? Tương tự như trường hợp 5?


10
Trong C / C ++, khoảng trắng không thay đổi ý nghĩa.
Sulthan

19
7. int*test;?
Jin Kwon

3
+1 vì tôi chỉ nghĩ hỏi khoảng 1 - 3. Đọc câu hỏi này đã dạy tôi điều gì đó về 4 - 6 mà tôi chưa bao giờ nghĩ đến.
growlysuperiorman

@Sulthan Điều đó đúng 99%, nhưng không phải lúc nào cũng vậy. Trên đỉnh đầu của tôi có loại kiểu mẫu trong yêu cầu không gian kiểu mẫu (trước C ++ 11). Trong Foo<Bar<char>>những >>phải được viết ra > >như vậy là không được đối xử như một quyền ca.
AnorZaken,

3
@AnorZaken Bạn nói đúng, đó là một nhận xét khá cũ. Có nhiều trường hợp khi một khoảng trắng sẽ thay đổi ý nghĩa, ví dụ: ++toán tử tăng dần không thể được chia bởi một khoảng trắng, các số nhận dạng không thể được chia bởi một khoảng trắng (và kết quả có thể vẫn hợp pháp đối với trình biên dịch nhưng với hành vi thời gian chạy không xác định). Các tình huống chính xác là rất khó xác định nếu xét đến sự lộn xộn về cú pháp của C / C ++.
Sulthan

Câu trả lời:


129

4, 5 và 6 giống nhau, chỉ có thử nghiệm là một con trỏ. Nếu bạn muốn có hai con trỏ, bạn nên sử dụng:

int *test, *test2;

Hoặc, thậm chí tốt hơn (để làm cho mọi thứ rõ ràng):

int* test;
int* test2;

3
Vậy Trường hợp 4 thực sự là một cái bẫy chết chóc? Có thông số kỹ thuật hoặc đọc thêm nào giải thích tại sao int * test, test2 chỉ làm cho biến đầu tiên là một con trỏ không?
Michael Stum

8
@ Michael Stum Đó là C ++, vậy bạn có thực sự nghĩ rằng có một lời giải thích hợp lý không?
Joe Phillips

6
Đọc K&R (Ngôn ngữ lập trình C). Nó giải thích tất cả điều này rất rõ ràng.
Ferruccio

8
Trường hợp 4, 5 và 6 là "bẫy tử thần". Đây là một lý do tại sao nhiều chuyên gia phong cách C / C ++ chỉ đề xuất một khai báo cho mỗi câu lệnh.
Michael Burr

14
Khoảng trắng là không đáng kể đối với trình biên dịch C (bỏ qua trình tiền xử lý). Vì vậy, bất kể có hoặc không có bao nhiêu khoảng trắng giữa dấu hoa thị và môi trường xung quanh, nó đều có cùng một ý nghĩa.
ephemient

45

Khoảng trắng xung quanh dấu hoa thị không có ý nghĩa. Cả ba đều có nghĩa giống nhau:

int* test;
int *test;
int * test;

" int *var1, var2" Là một cú pháp xấu chỉ nhằm mục đích gây nhầm lẫn cho mọi người và nên tránh. Nó mở rộng thành:

int *var1;
int var2;

16
Đừng quên int*test.
Mateen Ulhaq,

1
khoảng trống trước hay sau dấu hoa thị chỉ là vấn đề thẩm mỹ. Tuy nhiên, tiêu chuẩn Mã hóa của Google đi cùng với int *test( google-styleguide.googlecode.com/svn/trunk/… ). Chỉ cần nhất quán

@SebastianRaschka Hướng dẫn Kiểu Google C ++ cho phép rõ ràng vị trí dấu hoa thị. Có lẽ nó đã thay đổi kể từ khi bạn đọc nó.
Jared Beck

33

Nhiều hướng dẫn mã hóa khuyên bạn chỉ nên khai báo một biến trên mỗi dòng . Điều này tránh bất kỳ sự nhầm lẫn nào về loại mà bạn đã có trước khi đặt câu hỏi này. Hầu hết các lập trình viên C ++ mà tôi đã từng làm việc dường như dính vào điều này.


Tôi biết một chút sang một bên, nhưng điều tôi thấy hữu ích là đọc ngược lại các khai báo.

int* test;   // test is a pointer to an int

Điều này bắt đầu hoạt động rất tốt, đặc biệt là khi bạn bắt đầu khai báo con trỏ const và khó biết đó là con trỏ đó là const, hay thứ mà con trỏ đang trỏ vào đó là const.

int* const test; // test is a const pointer to an int

int const * test; // test is a pointer to a const int ... but many people write this as  
const int * test; // test is a pointer to an int that's const

Mặc dù "một biến trên mỗi dòng" có vẻ hữu ích, chúng tôi vẫn chưa giải quyết được hoàn toàn trường hợp dấu hoa thị ở bên trái nhiều hơn ở bên phải. Tôi khá chắc chắn rằng trong mã ngoài tự nhiên, một biến thể chiếm ưu thế; hơi giống một số quốc gia lái xe bên phải và những quốc gia khác lái xe sai hướng, chẳng hạn như Vương quốc Anh. ;-)
shevy

Thật không may, từ những cuộc phiêu lưu đến nơi hoang dã, tôi thấy có rất nhiều phong cách cả hai. Trong nhóm của tôi, chúng tôi hiện sử dụng định dạng tiếng kêu với phong cách mà chúng tôi đã thống nhất. Điều này ít nhất có nghĩa là tất cả mã mà nhóm của chúng tôi tạo ra đều có cùng một kiểu cho khoảng trắng đi.
Scott Langham

33

Sử dụng "Quy tắc xoắn ốc theo chiều kim đồng hồ" để giúp phân tích cú pháp các khai báo C / C ++;

Có ba bước đơn giản để làm theo:

  1. Bắt đầu với phần tử chưa biết, di chuyển theo hướng xoắn ốc / chiều kim đồng hồ; khi gặp các yếu tố sau, hãy thay thế chúng bằng các câu lệnh tiếng Anh tương ứng:

    [X]hoặc []: Kích thước mảng X của ... hoặc Kích thước không xác định của mảng ...

    (type1, type2): hàm truyền kiểu1 và kiểu2 trả về ...

    *: (các) con trỏ tới ...

  2. Tiếp tục làm điều này theo hướng xoắn ốc / chiều kim đồng hồ cho đến khi tất cả các thẻ đã được bao phủ.
  3. Luôn giải quyết bất cứ điều gì trong ngoặc đơn trước!

Ngoài ra, khai báo nên ở trong các câu lệnh riêng biệt khi có thể (điều này đúng trong phần lớn thời gian).


4
Điều đó trông có vẻ khó khăn và khá kinh khủng, xin lỗi phải nói.
Joe Phillips

7
nó, nhưng có vẻ như khá một lời giải thích tốt cho một số các cấu trúc phức tạp hơn
Michael Stum

@ d03boy: Không có câu hỏi nào - Khai báo C / C ++ có thể là một cơn ác mộng.
Michael Burr

2
"Xoắn ốc" không có ý nghĩa gì, ít hơn nhiều so với "chiều kim đồng hồ". Tôi muốn đặt tên nó là "quy tắc phải-trái", vì cú pháp không làm cho bạn nhìn từ phải-dưới-trái-trên, mà chỉ nhìn phải-trái.
Ruslan

Tôi học được điều này là quy tắc "phải-trái-phải". Những người làm C ++ thường thích giả sử tất cả thông tin kiểu nằm ở bên trái, điều này dẫn đến int* x;kiểu hơn là int *x;kiểu truyền thống . Tất nhiên, khoảng cách không quan trọng đối với trình biên dịch, nhưng nó ảnh hưởng đến con người. Việc phủ nhận cú pháp thực tế dẫn đến các quy tắc văn phong có thể gây khó chịu và khó hiểu cho người đọc.
Adrian McCarthy

12

Như những người khác đã đề cập, 4, 5 và 6 giống nhau. Thông thường, mọi người sử dụng những ví dụ này để đưa ra đối số rằng *thuộc về biến thay vì kiểu. Trong khi đó là một vấn đề về phong cách, có một số cuộc tranh luận về việc liệu bạn có nên nghĩ ra và viết nó theo cách này hay không:

int* x; // "x is a pointer to int"

hoặc theo cách này:

int *x; // "*x is an int"

FWIW Tôi đang ở trại đầu tiên, nhưng lý do những người khác đưa ra lập luận cho dạng thứ hai là nó (chủ yếu) giải quyết được vấn đề cụ thể này:

int* x,y; // "x is a pointer to int, y is an int"

mà có khả năng gây hiểu lầm; thay vào đó bạn sẽ viết

int *x,y; // it's a little clearer what is going on here

hoặc nếu bạn thực sự muốn hai con trỏ,

int *x, *y; // two pointers

Cá nhân tôi nói rằng hãy giữ nó ở một biến trên mỗi dòng, sau đó không quan trọng bạn thích phong cách nào.


6
đây là không có thật, bạn gọi là int *MyFunc(void)gì? a *MyFunclà một hàm trả về một int? Không. Rõ ràng là chúng ta nên viết int* MyFunc(void), và nói MyFunclà một hàm trả về a int*. Vì vậy, đối với tôi điều này rõ ràng, các quy tắc phân tích cú pháp ngữ pháp C và C ++ chỉ đơn giản là sai đối với khai báo biến. họ nên bao gồm chứng chỉ con trỏ như một phần của loại được chia sẻ cho toàn bộ chuỗi dấu phẩy.
v.oddou

1
Nhưng *MyFunc() một int. Vấn đề với cú pháp C được trộn tiền tốhậu tố cú pháp - nếu chỉ postfix đã được sử dụng, thì sẽ không có sự nhầm lẫn.
Antti Haapala

1
Trại thứ nhất đấu tranh với cú pháp của ngôn ngữ, dẫn đến những cấu trúc khó hiểu như int const* x;, mà tôi thấy cũng dễ gây hiểu lầm a * x+b * y.
Adrian McCarthy


5

Trong 4, 5 và 6, testluôn luôn là một con trỏ và test2không phải là một con trỏ. Khoảng trắng (hầu như) không bao giờ quan trọng trong C ++.


3

Theo tôi, câu trả lời là CẢ HAI, tùy trường hợp. Nói chung, IMO, tốt hơn là đặt dấu hoa thị bên cạnh tên con trỏ, thay vì đặt loại. So sánh ví dụ:

int *pointer1, *pointer2; // Fully consistent, two pointers
int* pointer1, pointer2;  // Inconsistent -- because only the first one is a pointer, the second one is an int variable
// The second case is unexpected, and thus prone to errors

Tại sao trường hợp thứ hai không nhất quán? Vì vd int x,y;khai báo hai biến cùng kiểu nhưng kiểu chỉ được đề cập một lần trong khai báo. Điều này tạo ra một tiền lệ và hành vi được mong đợi. Và int* pointer1, pointer2;không phù hợp với điều đó vì nó khai báo pointer1như một con trỏ, nhưng pointer2là một biến số nguyên. Rõ ràng dễ mắc lỗi và do đó, nên tránh (bằng cách đặt dấu hoa thị bên cạnh tên con trỏ, thay vì loại).

Tuy nhiên , có một số trường hợp ngoại lệ mà bạn không thể đặt dấu hoa thị bên cạnh tên đối tượng (và vị trí quan trọng bạn đặt nó ở đâu) mà không nhận được kết quả không mong muốn - ví dụ:

MyClass *volatile MyObjName

void test (const char *const p) // const value pointed to by a const pointer

Cuối cùng, trong một số trường hợp, có thể rõ ràng hơn nếu đặt dấu hoa thị bên cạnh tên loại , ví dụ:

void* ClassName::getItemPtr () {return &item;} // Clear at first sight


2

Cơ sở lý luận trong C là bạn khai báo các biến theo cách bạn sử dụng chúng. Ví dụ

char *a[100];

nói rằng đó *a[42]sẽ là một char. Và a[42]một con trỏ char. Và do đó alà một mảng con trỏ char.

Điều này bởi vì những người viết trình biên dịch ban đầu muốn sử dụng cùng một trình phân tích cú pháp cho các biểu thức và khai báo. (Không phải là một lý do rất hợp lý cho sự lựa chọn thiết kế hành lang)


Tuy nhiên, viết char* a[100]; cũng suy ra rằng *a[42];sẽ là một chara[42];một con trỏ char.
yyny

Tất cả chúng ta đều suy ra những kết luận giống nhau, chỉ có thứ tự là khác nhau.
Michel Billaud

Trích dẫn: "nói rằng * a [42] sẽ là một ký tự. Và [42] một con trỏ char". Bạn có chắc là nó không phải là ngược lại?
deLock

Nếu bạn thích cách khác, giả sử a[42]là một charcon trỏ và *a[42]là một ký tự.
Michel Billaud

2

Tôi sẽ nói rằng quy ước ban đầu là đặt ngôi sao ở phía tên con trỏ (phía bên phải của khai báo

  • trong ngôn ngữ lập trình c của Dennis M. Ritchie, các ngôi sao nằm ở phía bên phải của khai báo.

  • bằng cách nhìn vào mã nguồn linux tại https://github.com/torvalds/linux/blob/master/init/main.c, chúng ta có thể thấy rằng ngôi sao cũng nằm ở phía bên phải.

Bạn có thể tuân theo các quy tắc tương tự, nhưng sẽ không phải là vấn đề lớn nếu bạn đặt các ngôi sao ở phía loại. Hãy nhớ rằng sự nhất quán là quan trọng, vì vậy hãy luôn luôn nhưng ngôi sao ở cùng một phía bất kể bạn chọn bên nào.


Chà - trình phân tích cú pháp dường như cho phép một trong hai biến thể, nhưng nếu Dennis và Linus nói rằng nó phải ở phía bên phải, điều đó khá hấp dẫn. Tuy nhiên, chúng tôi vẫn thiếu một số cơ sở lý luận, và sau đó cũng là lời giải thích tại sao điều này được thực hiện. Nó hơi giống tình huống tab so với không gian - ngoại trừ tình huống đã được giải quyết, bởi vì những người sử dụng dấu cách thay vì tab, kiếm được nhiều tiền hơn, theo StackOverflow ... :-)
shevy

1

Con trỏ là một bổ ngữ cho kiểu. Tốt nhất bạn nên đọc chúng từ phải sang trái để hiểu rõ hơn cách dấu hoa thị sửa đổi loại. 'int *' có thể được đọc là "con trỏ tới int". Trong nhiều khai báo, bạn phải chỉ định rằng mỗi biến là một con trỏ hoặc nó sẽ được tạo như một biến chuẩn.

1,2 và 3) Kiểm tra có kiểu (int *). Khoảng trắng không quan trọng.

4,5 và 6) Kiểm tra có kiểu (int *). Test2 có kiểu int. Một lần nữa khoảng trắng là không quan trọng.


-3

Một nguyên tắc chung, rất nhiều người dường như nắm bắt được những khái niệm này bởi: Trong C ++, rất nhiều ý nghĩa ngữ nghĩa được hình thành bởi sự ràng buộc bên trái của các từ khóa hoặc số nhận dạng.

Lấy ví dụ:

int const bla;

Hằng số áp dụng cho từ "int". Tương tự với dấu hoa thị của con trỏ, chúng áp dụng cho từ khóa bên trái của chúng. Và tên biến thực tế? Yup, điều đó được tuyên bố bởi những gì còn lại của nó.


1
Điều này không trả lời câu hỏi. Tệ hơn nữa, nếu chúng ta cố gắng suy ra câu trả lời từ nó, thì nó ngụ ý dấu hoa thị liên kết với loại ở bên trái của nó, như mọi người đã nói, là sai. Nó liên kết với một tên biến duy nhất ở bên phải của nó.
underscore_d
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.