Tại sao dấu hoa thị trước tên biến, thay vì sau loại?


187

Tại sao hầu hết các lập trình viên C đặt tên biến như thế này:

int *myVariable;

thay vì như thế này:

int* myVariable;

Cả hai đều hợp lệ. Dường như với tôi rằng dấu hoa thị là một phần của loại, không phải là một phần của tên biến. Bất cứ ai có thể giải thích logic này?


1
Phong cách thứ hai nói chung có vẻ trực quan hơn, nhưng trước đây là cách để tránh các lỗi liên quan đến loại mã. Nếu bạn thực sự gắn bó với phong cách thứ hai, bạn luôn có thể đi theo typedefs, nhưng điều đó sẽ thêm sự phức tạp không cần thiết, IMHO.
Đám mây

Câu trả lời:


250

Họ là tương đương chính xác. Tuy nhiên, trong

int *myVariable, myVariable2;

Rõ ràng là myVariable có kiểu int * , trong khi myVariable2 có kiểu int . Trong

int* myVariable, myVariable2;

Có vẻ như rõ ràng là cả hai đều thuộc kiểu int * , nhưng điều đó không chính xác như myVariable2có kiểu int .

Do đó, phong cách lập trình đầu tiên là trực quan hơn.


135
có lẽ nhưng tôi sẽ không trộn và kết hợp các loại trong một tuyên bố.
BobbyShaftoe

15
@BulkShaftoe Đồng ý. Ngay cả sau khi đọc mọi lý lẽ ở đây, tôi vẫn gắn bó với int* someVarcác dự án cá nhân. Nó có ý nghĩa hơn.
Alyssa Haroldsen

30
@Kupiakos Nó chỉ có ý nghĩa hơn cho đến khi bạn học cú pháp khai báo của C dựa trên "khai báo theo sử dụng". Các khai báo sử dụng chính xác cú pháp sử dụng các biến được gõ giống nhau. Khi bạn khai báo một mảng ints, nó không giống như : int[10] x. Đây đơn giản không phải là cú pháp của C. Ngữ pháp phân tích rõ ràng là: int (*x)và không phải là (int *) x, do đó, việc đặt dấu hoa thị bên trái chỉ đơn giản là gây hiểu lầm và dựa trên sự hiểu lầm về cú pháp khai báo C.
Peaker

8
Sửa lỗi: do đó, bạn không bao giờ nên khai báo nhiều hơn một biến trên một dòng. Nói chung, bạn không nên thúc đẩy một kiểu mã hóa nhất định dựa trên một số kiểu mã hóa không liên quan, xấu và nguy hiểm khác.
Lundin

6
Đây là lý do tại sao tôi dính vào một biến trên mỗi khai báo con trỏ. Hoàn toàn không có sự nhầm lẫn nếu bạn làm int* myVariable; int myVariable2;thay thế.
Patrick Roberts

122

Nếu bạn nhìn nó theo một cách khác, *myVariablelà loại int, điều này có ý nghĩa.


6
Đây là lời giải thích yêu thích của tôi và hoạt động tốt bởi vì nó giải thích các yêu cầu khai báo của C nói chung - ngay cả cú pháp con trỏ hàm ghê tởm và sởn gai ốc.
Benjamin Pollack

12
Thật là gọn gàng, vì bạn có thể tưởng tượng không có bất kỳ loại con trỏ thực tế nào. Chỉ có các biến mà khi được tham chiếu hoặc hủy đăng ký một cách thích hợp, sẽ cung cấp cho bạn một trong các loại nguyên thủy.
biozinc

1
Trên thực tế, '* myVariable' có thể thuộc loại NULL. Để làm cho vấn đề tồi tệ hơn, nó chỉ có thể là một vị trí bộ nhớ ngẫu nhiên.
qonf

4
qonf: NULL không phải là một loại. myVariablecó thể là NULL, trong trường hợp này *myVariablegây ra lỗi phân đoạn, nhưng không có loại NULL.
Daniel Roethlisberger

28
Điểm này có thể gây hiểu nhầm trong ngữ cảnh như vậy : int x = 5; int *pointer = &x;, bởi vì nó gợi ý chúng ta đặt int *pointerthành một giá trị nào đó, chứ không phải pointerchính nó.
rafalcieslak

40

Bởi vì * trong dòng đó liên kết chặt chẽ hơn với biến hơn là kiểu:

int* varA, varB; // This is misleading

Như @Lundin chỉ ra dưới đây, const thêm nhiều sự tinh tế hơn để suy nghĩ. Bạn hoàn toàn có thể bỏ qua điều này bằng cách khai báo một biến trên mỗi dòng, điều này không bao giờ mơ hồ:

int* varA;
int varB;

Sự cân bằng giữa mã rõ ràng và mã ngắn gọn rất khó để thực hiện - hàng tá dòng dự phòng int a;cũng không tốt. Tuy nhiên, tôi mặc định một khai báo trên mỗi dòng và lo lắng về việc kết hợp mã sau này.


2
Vâng, ví dụ đầu tiên sai lệch là trong mắt tôi một lỗi thiết kế. Nếu tôi có thể, tôi sẽ loại bỏ hoàn toàn cách khai báo đó khỏi C và làm cho nó sao cho cả hai đều thuộc kiểu int *.
Adam Bajger

1
"the * liên kết chặt chẽ hơn với biến hơn là kiểu" Đây là một đối số ngây thơ. Hãy xem xét int *const a, b;. Trường hợp * "ràng buộc"? Loại aint* const, vậy làm thế nào bạn có thể nói rằng * thuộc về biến khi nó là một phần của chính loại đó?
Lundin

1
Cụ thể cho câu hỏi, hoặc bao gồm tất cả các trường hợp: chọn một. Tôi sẽ ghi chú, nhưng đây là một lập luận tốt khác cho đề xuất cuối cùng của tôi: một tuyên bố trên mỗi dòng làm giảm các cơ hội để làm hỏng điều này.
ojrac

36

Một điều chưa ai đề cập ở đây cho đến nay là dấu sao này thực sự là " toán tử quy ước " trong C.

*a = 10;

Dòng trên không có nghĩa là tôi muốn gán 10cho a, nó có nghĩa là tôi muốn gán 10cho bất kỳ vị trí bộ nhớ nào atrỏ đến. Và tôi chưa từng thấy ai viết

* a = 10;

có bạn không Vì vậy, toán tử dereference được viết khá nhiều mà không có khoảng trắng. Điều này có lẽ là để phân biệt nó với một phép nhân bị phá vỡ trên nhiều dòng:

x = a * b * c * d
  * e * f * g;

Ở đây *esẽ gây hiểu lầm, phải không?

Được rồi, bây giờ dòng thực sự có nghĩa là gì:

int *a;

Hầu hết mọi người sẽ nói:

Nó có nghĩa alà một con trỏ đến một intgiá trị.

Điều này đúng về mặt kỹ thuật, hầu hết mọi người đều thích nhìn / đọc nó theo cách đó và đó là cách mà các tiêu chuẩn C hiện đại sẽ định nghĩa nó (lưu ý rằng chính ngôn ngữ C có trước tất cả các tiêu chuẩn ANSI và ISO). Nhưng đó không phải là cách duy nhất để xem xét nó. Bạn cũng có thể đọc dòng này như sau:

Giá trị ađược loại bỏ là loại int.

Vì vậy, trên thực tế, dấu hoa thị trong tuyên bố này cũng có thể được xem như là một toán tử quy ước, điều này cũng giải thích vị trí của nó. Và đó alà một con trỏ hoàn toàn không được tuyên bố, thực tế là nó là thứ duy nhất bạn thực sự có thể thực hiện là một con trỏ.

Tiêu chuẩn C chỉ định nghĩa hai nghĩa cho *toán tử:

  • toán tử gián tiếp
  • toán tử nhân

Và gián tiếp chỉ là một ý nghĩa duy nhất, có ý nghĩa không phải trả thêm cho tuyên bố một con trỏ, đó chỉ là gián tiếp, đó là những gì các hoạt động dereference không, nó thực hiện một truy cập gián tiếp, vì vậy cũng trong một tuyên bố như int *a;đây là một gián tiếp truy cập ( *phương tiện truy cập gián tiếp) và do đó, tuyên bố thứ hai ở trên gần với tiêu chuẩn hơn nhiều so với tuyên bố đầu tiên.


6
Cảm ơn đã cứu tôi khỏi viết một câu trả lời khác ở đây. BTW Tôi thường đọc int a, *b, (*c)();như một cái gì đó như "khai báo các đối tượng sau đây là int: đối tượng a, đối tượng được trỏ đến bvà đối tượng được trả về từ hàm được trỏ bởi c".
Antti Haapala

1
Cái *trong int *a;không phải là một nhà điều hành và nó không phải là hội nghị a(mà thậm chí còn chưa được xác định)
MM

1
@MM Vui lòng đặt tên trang và số dòng của bất kỳ tiêu chuẩn ISO C nào trong đó tiêu chuẩn này nói rằng dấu hoa thị có thể là một cái gì đó khác hơn là nhân hoặc không xác định. Nó chỉ hiển thị "khai báo con trỏ" bằng ví dụ, không nơi nào định nghĩa nghĩa thứ ba cho dấu hoa thị (và nó định nghĩa không có nghĩa, đó sẽ không phải là toán tử). Ồ, không nơi nào tôi tuyên bố bất cứ điều gì là "được xác định", bạn đã thực hiện điều đó. Hoặc như chiếc mũ của Jonathan Leffler, trong tiêu chuẩn C, * luôn là "ngữ pháp", nó không phải là một phần của các công cụ khai báo được liệt kê (vì vậy nó không phải là một phần của một tuyên bố, do đó nó phải là một toán tử)
Mecki

1
@MM 6.7.6.1 chỉ hiển thị khai báo bằng ví dụ, chính xác như tôi đã nói, nó không có ý nghĩa*(nó chỉ mang một ý nghĩa cho tổng biểu thức, không phải *trong biểu thức!). Nó nói rằng "int a;" khai báo một con trỏ, mà nó không bao giờ yêu cầu khác, nhưng không có nghĩa *, được đọc là * giá trị được quy định của amột int vẫn hoàn toàn hợp lệ, vì điều đó có cùng ý nghĩa thực tế. Không có gì, thực sự không có gì được viết trong 6.7.6.1 sẽ mâu thuẫn với tuyên bố đó.
Mecki

2
Không có "biểu thức tổng". int *a;là một tuyên bố, không phải là một biểu thức. akhông được quy định bởi int *a;. athậm chí không tồn tại nhưng tại thời điểm *nó đang được xử lý. Bạn có nghĩ int *a = NULL;là một lỗi bởi vì nó hủy bỏ một con trỏ null?
MM

17

Tôi sẽ đi ra ngoài một chi ở đây và nói rằng có một câu trả lời thẳng cho câu hỏi này , cả cho khai báo biến và các loại tham số và trả về, đó là dấu hoa thị nên đi bên cạnh tên : int *myVariable;. Để đánh giá cao lý do tại sao, hãy xem cách bạn khai báo các loại biểu tượng khác trong C:

int my_function(int arg); cho một chức năng;

float my_array[3] cho một mảng.

Mẫu chung, được gọi là khai báo sau khi sử dụng , là loại biểu tượng được chia thành phần trước tên và các phần xung quanh tên và các phần xung quanh tên bắt chước cú pháp bạn sẽ sử dụng để lấy giá trị của loại bên trái:

int a_return_value = my_function(729);

float an_element = my_array[2];

và : int copy_of_value = *myVariable;.

C ++ ném cờ lê trong công việc với các tham chiếu, vì cú pháp tại điểm bạn sử dụng tham chiếu giống hệt với các loại giá trị, do đó bạn có thể lập luận rằng C ++ có cách tiếp cận khác với C. Mặt khác, C ++ vẫn giữ nguyên hành vi của C trong trường hợp con trỏ, vì vậy các tham chiếu thực sự đứng như một số lẻ trong khía cạnh này.


12

Đó chỉ là vấn đề ưu tiên.

Khi bạn đọc mã, việc phân biệt giữa các biến và con trỏ sẽ dễ dàng hơn trong trường hợp thứ hai, nhưng nó có thể dẫn đến sự nhầm lẫn khi bạn đặt cả hai biến và con trỏ của một loại chung vào một dòng (điều này thường không được khuyến khích bởi các hướng dẫn dự án, vì giảm khả năng đọc).

Tôi thích khai báo các con trỏ với dấu tương ứng bên cạnh tên loại, vd

int* pMyPointer;

3
Câu hỏi là về C, nơi không có tài liệu tham khảo.
Antti Haapala

Cảm ơn bạn đã chỉ ra rằng, mặc dù câu hỏi không phải là về con trỏ hoặc tham chiếu, mà về cơ bản là định dạng mã.
macbirdie

8

Một bậc thầy vĩ đại đã từng nói "Đọc nó theo cách của trình biên dịch, bạn phải."

http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835

Cấp điều này là về chủ đề của vị trí const, nhưng quy tắc tương tự áp dụng ở đây.

Trình biên dịch đọc nó dưới dạng:

int (*a);

không phải là:

(int*) a;

Nếu bạn có thói quen đặt ngôi sao bên cạnh biến, nó sẽ giúp cho việc khai báo của bạn dễ đọc hơn. Nó cũng tránh các mắt như:

int* a[10];

-- Biên tập --

Để giải thích chính xác những gì tôi muốn nói khi tôi nói nó được phân tích cú pháp int (*a), điều đó có nghĩa là *liên kết chặt chẽ ahơn so với nó int, theo cách mà trong biểu thức 4 + 3 * 7 3liên kết chặt chẽ 7hơn so với nó 4do ưu tiên cao hơn *.

Với lời xin lỗi cho nghệ thuật ascii, một bản tóm tắt của AST để phân tích cú pháp int *atrông giống như thế này:

      Declaration
      /         \
     /           \
Declaration-      Init-
Secifiers       Declarator-
    |             List
    |               |
    |              ...
  "int"             |
                Declarator
                /       \
               /        ...
           Pointer        \
              |        Identifier
              |            |
             "*"           |
                          "a"

Như đã được chỉ ra rõ ràng, *liên kết chặt chẽ hơn với atổ tiên chung của chúng là Declarator, trong khi bạn cần đi lên cây Declarationđể tìm một tổ tiên chung có liên quan đến int.


Không, trình biên dịch chắc chắn đọc kiểu như (int*) a.
Lundin

@Lundin Đây là sự hiểu lầm lớn mà hầu hết các lập trình viên C ++ hiện đại đều mắc phải. Phân tích một khai báo biến đi một cái gì đó như thế này. Bước 1. Đọc mã thông báo đầu tiên, trở thành "loại cơ sở" của khai báo. inttrong trường hợp này. Bước 2. Đọc một tờ khai, bao gồm bất kỳ loại trang trí. *atrong trường hợp này. Bước 3 Đọc ký tự tiếp theo. Nếu dấu phẩy, tiêu thụ nó và quay lại bước 2. Nếu dấu chấm phẩy dừng lại. Nếu bất cứ điều gì khác ném một lỗi cú pháp. ...
thức

@Lundin ... Nếu trình phân tích cú pháp đọc nó theo cách bạn đang đề xuất, thì chúng tôi sẽ có thể viết int* a, b;và nhận được một cặp con trỏ. Điểm tôi đang làm là các *liên kết với biến và được phân tích cú pháp với nó, không phải với kiểu để tạo thành "kiểu cơ sở" của khai báo. Đó cũng là một phần lý do mà typedefs được giới thiệu để cho phép typedef int *iptr; iptr a, b;tạo ra một vài gợi ý. Bằng cách sử dụng một typedef, bạn có thể liên kết *với int.
thức

1
... Chắc chắn, trình biên dịch "kết hợp" loại cơ sở từ Declaration-Specifiervới "trang trí" trong Declaratorđể đến loại cuối cùng cho mỗi biến. Tuy nhiên, nó không "di chuyển" các trang trí đến công cụ xác định Tuyên bố nếu không int a[10], b;sẽ tạo ra kết quả hoàn toàn vô lý, Nó phân tích int *a, b[10];như int *a , b[10] ;. Không có cách nào khác để mô tả nó có ý nghĩa.
thức

1
Vâng, phần quan trọng ở đây không thực sự là cú pháp hoặc thứ tự phân tích cú pháp trình biên dịch, mà là loại int*acuối cùng được trình biên dịch đọc là: " acó loại int*". Đó là những gì tôi muốn nói với nhận xét ban đầu của tôi.
Lundin

3

Bởi vì nó có ý nghĩa hơn khi bạn có các khai báo như:

int *a, *b;

5
Đây thực sự là một ví dụ về việc cầu xin câu hỏi. Không, nó không có ý nghĩa hơn theo cách đó. "int * a, b" cũng có thể khiến cả hai con trỏ đều như vậy.
MichaelGG

1
@MichaelGG Bạn đã có cái đuôi vẫy con chó ở đó. Chắc chắn K & R có thể đã chỉ định int* a, b;tạo ra bmột con trỏ tới int. Nhưng họ đã không làm thế. Và với lý do chính đáng. Theo hệ thống đề xuất của bạn, loại bkhai báo nào sau đây : int* a[10], b;?
thức

2

Để khai báo nhiều con trỏ trong một dòng, tôi thích int* a, * b;khai báo trực quan hơn "a" như một con trỏ cho một số nguyên và không trộn lẫn các kiểu khi khai báo tương tự "b." Giống như ai đó đã nói, tôi sẽ không khai báo hai loại khác nhau trong cùng một tuyên bố.


2

Những người thích int* x; đang cố gắng buộc mã của họ vào một thế giới hư cấu nơi loại nằm ở bên trái và mã định danh (tên) ở bên phải.

Tôi nói "hư cấu" bởi vì:

Trong C và C ++, trong trường hợp chung, định danh khai báo được bao quanh bởi thông tin loại.

Điều đó nghe có vẻ điên rồ, nhưng bạn biết đó là sự thật. Dưới đây là một số ví dụ:

  • int main(int argc, char *argv[])có nghĩa là " mainlà một hàm lấy một intvà một mảng các con trỏ tới charvà trả về một int." Nói cách khác, hầu hết các thông tin loại ở bên phải. Một số người cho rằng khai báo hàm không được tính bởi vì chúng bằng cách nào đó "đặc biệt". OK, chúng ta hãy thử một biến.

  • void (*fn)(int)có nghĩa fnlà một con trỏ đến một hàm lấy intvà không trả về gì cả.

  • int a[10]tuyên bố 'a' là một mảng 10 intgiây.

  • pixel bitmap[height][width].

  • Rõ ràng, tôi đã chọn các ví dụ anh đào có nhiều thông tin loại bên phải để đưa ra quan điểm của mình. Có rất nhiều khai báo trong đó hầu hết - nếu không phải tất cả - thuộc loại nằm bên trái, như thế struct { int x; int y; } center.

Cú pháp khai báo này phát triển từ mong muốn của K & R để có các tuyên bố phản ánh việc sử dụng. Đọc các khai báo đơn giản là trực quan và đọc các khai báo phức tạp hơn có thể được làm chủ bằng cách học quy tắc phải-trái-phải (đôi khi gọi quy tắc xoắn ốc hoặc chỉ là quy tắc phải-trái).

C đủ đơn giản để nhiều lập trình viên C nắm lấy phong cách này và viết các khai báo đơn giản như int *p.

Trong C ++, cú pháp phức tạp hơn một chút (với các lớp, tham chiếu, mẫu, lớp enum) và, như một phản ứng với sự phức tạp đó, bạn sẽ thấy nỗ lực hơn trong việc tách loại từ định danh trong nhiều khai báo. Nói cách khác, bạn có thể thấy nhiều int* pkhai báo kiểu hơn nếu bạn kiểm tra một lượng lớn mã C ++.

Trong cả hai ngôn ngữ, bạn luôn có thể có kiểu ở bên trái của khai báo biến bởi (1) không bao giờ khai báo nhiều biến trong cùng một câu lệnh và (2) sử dụng typedefs (hoặc khai báo bí danh, trong đó, trớ trêu thay, đặt bí danh định danh ở bên trái của các loại). Ví dụ:

typedef int array_of_10_ints[10];
array_of_10_ints a;

Câu hỏi nhỏ, tại sao không thể void (* fn) (int) có nghĩa là "fn là một hàm chấp nhận một int và trả về (void *)?
Soham Dongargaonkar

1
@ a3y3: Bởi vì dấu ngoặc đơn (*fn)giữ con trỏ được liên kết với fnchứ không phải kiểu trả về.
Adrian McCarthy

Hiểu rồi. Tôi nghĩ rằng nó đủ quan trọng để được thêm vào câu trả lời của bạn, tôi sẽ đề nghị chỉnh sửa sớm.
Soham Dongargaonkar

1
Tôi nghĩ rằng clutter câu trả lời mà không thêm nhiều giá trị. Đây chỉ đơn giản là cú pháp của ngôn ngữ. Hầu hết mọi người hỏi về lý do tại sao cú pháp theo cách này có thể đã biết các quy tắc. Bất cứ ai khác bối rối về điểm này có thể thấy sự rõ ràng trong những bình luận này.
Adrian McCarthy

1

Tôi đã trả lời cho một câu hỏi tương tự trong CP, và vì không ai đề cập đến điều đó, nên ở đây tôi phải chỉ ra rằng C là ngôn ngữ định dạng miễn phí , bất kể kiểu nào bạn chọn đều ổn trong khi trình phân tích cú pháp có thể phân biệt từng mã thông báo. Tính đặc thù này của C dẫn đến một loại rất đặc biệt của cuộc thi gọi là cuộc thi obfuscation C .


1

Rất nhiều đối số trong chủ đề này là chủ quan rõ ràng và lập luận về "ngôi sao liên kết với tên biến" là ngây thơ. Đây là một vài lập luận không chỉ là ý kiến:


Vòng loại loại con trỏ bị lãng quên

Chính thức, "ngôi sao" không thuộc loại cũng như tên biến, nó là một phần của mục ngữ pháp riêng có tên con trỏ . Cú pháp C chính thức (ISO 9899: 2018) là:

(6.7) khai báo:
khai báo-specifier init-kê khai-list opt ;

Trong đó bộ xác định khai báo chứa loại (và lưu trữ) và danh sách init-kê khai chứa con trỏ và tên biến. Mà chúng ta thấy nếu chúng ta mổ xẻ cú pháp danh sách khai báo này thêm:

(6.7.6) declarator:
con trỏ opt trực declarator
...
(6.7.6) con trỏ:
* type-vòng-list lựa chọn
* kiểu vòng loại danh sách lựa chọn con trỏ

Trong đó một người khai báo là toàn bộ khai báo, một người khai báo trực tiếp là mã định danh (tên biến) và một con trỏ là ngôi sao theo sau là một danh sách vòng loại loại tùy chọn thuộc về chính con trỏ.

Điều làm cho các đối số kiểu khác nhau về "ngôi sao thuộc về biến" không nhất quán, là chúng đã quên về các vòng loại loại con trỏ. int* const x, int *const xHoặc int*const x?

Hãy xem xét int *const a, b;, các loại avà là bgì? Không quá rõ ràng rằng "ngôi sao thuộc về biến" nữa. Thay vào đó, người ta sẽ bắt đầu suy nghĩ về nơi constthuộc về.

Bạn chắc chắn có thể đưa ra một đối số âm thanh rằng ngôi sao thuộc về vòng loại loại con trỏ, nhưng không vượt quá điều đó.

Danh sách vòng loại cho con trỏ có thể gây ra sự cố cho những người sử dụng int *akiểu. Những người sử dụng con trỏ bên trong một typedef(mà chúng ta không nên, thực hành rất tệ!) Và nghĩ rằng "ngôi sao thuộc về tên biến" có xu hướng viết ra lỗi rất tinh vi này:

    /*** bad code, don't do this ***/
    typedef int *bad_idea_t; 
    ...
    void func (const bad_idea_t *foo);

Điều này biên dịch sạch sẽ. Bây giờ bạn có thể nghĩ rằng mã được thực hiện const chính xác. Không phải vậy! Mã này vô tình là một const giả chính xác.

Loại foothực tế int*const*- hầu hết các con trỏ bên ngoài được tạo ở chế độ chỉ đọc, không phải là trỏ vào dữ liệu. Vì vậy, bên trong chức năng này, chúng ta có thể làm**foo = n; và nó sẽ thay đổi giá trị biến trong trình gọi.

Điều này là do trong biểu thức const bad_idea_t *foo, *không thuộc về tên biến ở đây! Trong mã giả, khai báo tham số này sẽ được đọc const (bad_idea_t *) fookhông phải(const bad_idea_t) *foo. Ngôi sao thuộc loại con trỏ ẩn trong trường hợp này - loại là con trỏ và con trỏ đủ điều kiện const được viết là *const.

Nhưng sau đó, gốc rễ của vấn đề trong ví dụ trên là thực hành ẩn con trỏ đằng sau một typedefvà không phải là *phong cách.


Về khai báo nhiều biến trên một dòng

Khai báo nhiều biến trên một dòng được công nhận rộng rãi là thông lệ xấu 1) . CERT-C tổng hợp nó một cách độc đáo như:

DCL04-C. Không khai báo nhiều hơn một biến trên mỗi khai báo

Chỉ cần đọc tiếng Anh, sau đó thông thường đồng ý rằng một tuyên bố nên là một tuyên bố.

Và nó không quan trọng nếu các biến là con trỏ hay không. Khai báo từng biến trên một dòng làm cho mã rõ ràng hơn trong hầu hết mọi trường hợp.

Vì vậy, tranh luận về việc lập trình viên bị nhầm lẫn int* a, blà xấu. Căn nguyên của vấn đề là việc sử dụng nhiều người khai báo, không phải là vị trí của *. Bất kể phong cách, bạn nên viết điều này thay vào đó:

    int* a; // or int *a
    int b;

Một lý do khác âm nhưng chủ quan sẽ được rằng cho int* aloại alà không có câu hỏiint* và vì vậy sao thuộc với kiểu vòng loại.

Nhưng về cơ bản kết luận của tôi là nhiều lập luận được đăng ở đây chỉ mang tính chủ quan và ngây thơ. Bạn thực sự không thể đưa ra một lập luận hợp lệ cho một trong hai phong cách - đó thực sự là vấn đề sở thích cá nhân chủ quan.


1) CERT-C DCL04-C .


Khá thú vị, nếu bạn đọc bài báo mà tôi đã liên kết, có một phần ngắn về chủ đề typedef int *bad_idea_t; void func(const bad_idea_t bar); Như nhà tiên tri vĩ đại Dan Saks dạy "Nếu bạn luôn đặt constcàng xa bên phải càng tốt, mà không thay đổi ý nghĩa ngữ nghĩa" thì điều này hoàn toàn chấm dứt là một vấn đề Nó cũng làm cho consttuyên bố của bạn phù hợp hơn để đọc. "Mọi thứ ở bên phải của const const là const, mọi thứ ở bên trái là kiểu của nó." Điều này sẽ áp dụng cho tất cả các consts trong một tuyên bố. Hãy thử vớiint const * * const x;
dgnuff

-1

Xem xét

int *x = new int();

Điều này không dịch sang

int *x;
*x = new int();

Nó thực sự dịch

int *x;
x = new int();

Điều này làm cho int *xký hiệu có phần không nhất quán.


newlà một toán tử C ++. Câu hỏi của anh ấy được gắn thẻ "C" chứ không phải "C ++".
Sapphire_Brick
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.