int a [] = {1,2,}; Dấu phẩy lạ cho phép. Bất kỳ lý do cụ thể nào?


334

Có thể tôi không đến từ hành tinh này, nhưng dường như sau đây tôi sẽ bị lỗi cú pháp:

int a[] = {1,2,}; //extra comma in the end

Nhưng nó không phải là. Tôi đã rất ngạc nhiên khi mã này được biên dịch trên Visual Studio, nhưng tôi đã học được cách không tin tưởng trình biên dịch MSVC khi có liên quan đến các quy tắc C ++, vì vậy tôi đã kiểm tra tiêu chuẩn và nó cũng được cho phép theo tiêu chuẩn. Bạn có thể xem 8.5.1 cho các quy tắc ngữ pháp nếu bạn không tin tôi.

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

Tại sao điều này được cho phép? Đây có thể là một câu hỏi vô dụng ngu ngốc nhưng tôi muốn bạn hiểu lý do tại sao tôi hỏi. Nếu đó là một trường hợp phụ của quy tắc ngữ pháp chung, tôi sẽ hiểu - họ quyết định không làm cho ngữ pháp chung trở nên khó khăn hơn chỉ để không cho phép một dấu phẩy thừa ở cuối danh sách khởi tạo. Nhưng không, dấu phẩy bổ sung được cho phép rõ ràng . Ví dụ: không được phép có dấu phẩy thừa ở cuối danh sách đối số gọi hàm (khi hàm thực hiện ...), điều này là bình thường .

Vì vậy, một lần nữa, có bất kỳ lý do cụ thể nào dấu phẩy thừa này được cho phép rõ ràng ?


10
Mọi người dường như đồng ý 'dễ dàng thêm dòng mới' - nhưng những người định nghĩa thông số kỹ thuật ngôn ngữ có thực sự bận tâm về những điều như vậy không? Nếu họ thực sự hiểu điều đó thì tại sao họ không bỏ qua việc mất tích ;khi mã thông báo rõ ràng tiếp theo thực sự là một tuyên bố tiếp theo.
YetAntherUser

35
@YetAntherUser: Vâng, các nhà thiết kế ngôn ngữ xem xét những điều như vậy. Cho phép bạn bỏ dấu chấm phẩy sẽ có tác động lớn hơn nhiều và sẽ rất mơ hồ trong nhiều phần của ngôn ngữ (hãy nhớ rằng, khoảng trắng không phải là ngữ nghĩa trong C). Dấu phẩy thừa là trường hợp này không mơ hồ. Một dấu chấm phẩy thêm hầu như không bao giờ mơ hồ, và vì vậy cũng được cho phép. Trong trường hợp không rõ ràng (sau một for()ví dụ), việc thêm nó sẽ đưa ra một cảnh báo trình biên dịch.
Rob Napier

5
@Tomalak: Nó mơ hồ đối với người đọc và thường là một sai lầm. Đó là lý do tại sao nó ném một cảnh báo. Tương tự như vậy if (x = 1)không mơ hồ trong ngữ pháp, nhưng nó rất mơ hồ đối với con người, và do đó đưa ra một cảnh báo.
Rob Napier

12
@Rob: ifVí dụ của bạn cũng không mơ hồ. Tôi không nghĩ "mơ hồ" có nghĩa là những gì bạn nghĩ nó có nghĩa!
Các cuộc đua nhẹ nhàng trong quỹ đạo

5
Miễn là chúng tôi đồng ý rằng nó là thứ gì đó hữu ích cho trình biên dịch để bảo vệ chúng tôi, trong khi dấu phẩy trong khai báo mảng không phải là thứ hữu ích cho trình biên dịch để bảo vệ chúng tôi khỏi.
Rob Napier

Câu trả lời:


435

Nó giúp việc tạo mã nguồn dễ dàng hơn và cũng có thể viết mã có thể dễ dàng gia hạn vào một ngày sau đó. Xem xét những gì cần thiết để thêm một mục bổ sung vào:

int a[] = {
   1,
   2,
   3
};

... bạn phải thêm dấu phẩy vào dòng hiện có thêm một dòng mới. So sánh với trường hợp cả ba đã có dấu phẩy sau nó, trong đó bạn chỉ cần thêm một dòng. Tương tự như vậy, nếu bạn muốn xóa một dòng, bạn có thể làm như vậy mà không cần lo lắng về việc đó có phải là dòng cuối cùng hay không và bạn có thể sắp xếp lại các dòng mà không phải loay hoay với dấu phẩy. Về cơ bản, điều đó có nghĩa là có sự đồng nhất trong cách bạn đối xử với các dòng.

Bây giờ hãy nghĩ về việc tạo mã. Một cái gì đó như (mã giả):

output("int a[] = {");
for (int i = 0; i < items.length; i++) {
    output("%s, ", items[i]);
}
output("};");

Không cần phải lo lắng về việc mục hiện tại bạn viết ra là đầu tiên hay cuối cùng. Đơn giản hơn nhiều.


89
Ngoài ra, khi sử dụng VCS, "diff" giữa hai phiên bản sẽ sạch hơn vì chỉ một dòng thay đổi khi một mục được thêm hoặc xóa.
Kevin Panko

47
@ Néstor: Tại sao "không may"? Nhược điểm ở đây là gì? Chỉ vì một số xem xét đã được đưa ra để tạo mã (và thao tác dễ dàng) cho một phần nhỏ của ngôn ngữ không có nghĩa nó phải là động lực chính đằng sau tất cả các quyết định trong ngôn ngữ. Kiểu suy luận, loại bỏ dấu chấm phẩy v.v ... có ý nghĩa rất lớn đối với ngôn ngữ. Bạn đang thiết lập một sự phân đôi giả ở đây, IMO.
Jon Skeet

18
@ Néstor: Đó là nơi chủ nghĩa thực dụng chiến thắng chủ nghĩa giáo điều: tại sao nó phải hoàn toàn là một thứ hoặc hoàn toàn khác, khi nó hữu ích hơn khi là một hỗn hợp của cả hai? Làm thế nào để nó thực sự cản trở, có thể thêm dấu phẩy vào cuối? Đây có phải là một sự mâu thuẫn đã từng cản trở bạn trong bất kỳ ý nghĩa nào? Nếu không, vui lòng cân nhắc sự không liên quan không liên quan đó với lợi ích thiết thực của việc cho phép dấu phẩy ở cuối.
Jon Skeet

8
@Mrchief: Đây không phải là vấn đề về tốc độ gõ - đó là vấn đề đơn giản, khi sao chép, xóa hoặc sắp xếp lại các mục. Nó làm cho cuộc sống của tôi đơn giản hơn chỉ ngày hôm qua. Không có nhược điểm, tại sao không làm cho cuộc sống dễ dàng hơn? Đối với việc cố gắng chỉ tay vào MS, tôi cực kỳ nghi ngờ điều này đã có trong C kể từ trước khi Microsoft tồn tại ... Bạn nói rằng sự biện minh này có vẻ kỳ lạ, nhưng tôi cá rằng nó mang lại lợi ích cho hàng ngàn nhà phát triển trên hàng trăm công ty mỗi ngày. Đó không phải là một lời giải thích tốt hơn là tìm kiếm một cái gì đó có lợi cho các nhà văn trình biên dịch?
Jon Skeet

6
Đây là trong K & R C.
Ferruccio

126

Thật hữu ích nếu bạn làm một cái gì đó như thế này:

int a[] = {
  1,
  2,
  3, //You can delete this line and it's still valid
};

6
JavaScript hỗ trợ cú pháp này : var a = [1, 2,];, hầu hết các ngôn ngữ khác mà tôi biết ... ActionScript, Python, PHP.
Sean Fujiwara

14
@Sean Điều đó sẽ gây ra lỗi phân tích cú pháp trong IE JavaScript, vì vậy hãy cẩn thận!
Skilldrick

10
Nó không dành cho tôi trong IE9. Nhưng nó làm một cái gì đó kỳ lạ ... nó tạo ra một yếu tố null. Tôi sẽ cẩn thận.
Sean Fujiwara

5
@Sean Xin lỗi, bạn đã đúng - đó không phải là lỗi phân tích cú pháp trong IE, nhưng nó sẽ chèn một phần tử phụ được đặt thành undefined.
Skilldrick

3
Bực bội nhất, JSON không hỗ trợ cú pháp này.
Timmmm

38

Dễ sử dụng cho các nhà phát triển, tôi sẽ nghĩ.

int a[] = {
            1,
            2,
            2,
            2,
            2,
            2, /*line I could comment out easily without having to remove the previous comma*/
          }

Ngoài ra, nếu vì bất kỳ lý do gì bạn có một công cụ tạo mã cho bạn; công cụ không phải quan tâm đến việc đó có phải là mục cuối cùng trong quá trình khởi tạo hay không.


32

Tôi luôn cho rằng việc gắn các yếu tố phụ dễ dàng hơn:

int a[] = {
            5,
            6,
          };

đơn giản trở thành:

int a[] = { 
            5,
            6,
            7,
          };

vào một ngày sau đó


3
Tôi không nghĩ việc chỉnh sửa nhanh hơn một chút là lý do chính đáng để làm rối cú pháp. IMHO đây chỉ là một tính năng kỳ lạ khác của C ++.
Giorgio

3
@Giorgio: Chà, nó được thừa hưởng từ C. Hoàn toàn có thể đó chỉ là sự giám sát trong đặc tả ngôn ngữ gốc, điều đó có tác dụng phụ hữu ích.
Oliver Charlesworth

Ok, tôi không biết rằng nó đến từ C. Tôi cũng đã kiểm tra rằng nó cũng được cho phép trong Java. Mặc dù vậy, nó cảm thấy kỳ lạ: trong trực giác của tôi, dấu phẩy là dấu phân cách không phải là dấu chấm dứt. Hơn nữa, có thể bỏ qua dấu phẩy cuối cùng. Vì vậy, nó là một terminator, một phân tách, hoặc cả hai? Nhưng OK, tính năng này có sẵn và nó là tốt để biết.
Giorgio

11
@Giorgio - mã nguồn dành cho con người, không phải máy móc. Những điều nhỏ nhặt như thế này để ngăn chúng ta khỏi những lỗi chuyển vị đơn giản là một phước lành, không phải là một sự giám sát. Để tham khảo, nó cũng hoạt động theo cách này trong PHP và ECMAScript (và do đó là JavaScript và ActionScript), mặc dù nó không hợp lệ trong ký hiệu đối tượng JavaScript (JSON) (ví dụ: [1,2,3,]OK nhưng {a:1, b:2, c:3,}không phải).
Đã ra mắt

1
@Groky: Càng nghĩ về nó, tôi càng tin rằng cú pháp của ngôn ngữ lập trình phải đơn giản và nhất quán nhất có thể và càng ít ngoại lệ càng tốt: điều này giúp bạn học ngôn ngữ dễ dàng hơn (nhớ càng ít quy tắc ). Ưu điểm của việc lưu một hoặc hai lần nhấn phím khi thêm / xóa một mục vào / ra khỏi danh sách (nhân tiện, tôi không làm điều đó thường so với tổng thời gian tôi dành cho việc viết mã) có vẻ khá tầm thường so với tôi có một cú pháp được xác định rõ ràng.
Giorgio

21

Mọi thứ mọi người đều nói về việc dễ dàng thêm / xóa / tạo dòng là chính xác, nhưng vị trí thực sự của cú pháp này là khi hợp nhất các tệp nguồn với nhau. Hãy tưởng tượng bạn đã có mảng này:

int ints[] = {
    3,
    9
};

Và giả sử bạn đã kiểm tra mã này vào một kho lưu trữ.

Sau đó, bạn của bạn chỉnh sửa nó, thêm vào cuối:

int ints[] = {
    3,
    9,
    12
};

Và bạn đồng thời chỉnh sửa nó, thêm vào đầu:

int ints[] = {
    1,
    3,
    9
};

Về mặt ngữ nghĩa các loại hoạt động này (thêm vào đầu, thêm vào cuối) nên hoàn toàn hợp nhất an toàn và phần mềm phiên bản của bạn (hy vọng git) sẽ có thể tự động hóa. Đáng buồn thay, đây không phải là trường hợp vì phiên bản của bạn không có dấu phẩy sau số 9 và bạn của bạn cũng vậy. Trong khi đó, nếu phiên bản gốc có 9 dấu, chúng sẽ tự động hóa.

Vì vậy, quy tắc ngón tay cái của tôi là: sử dụng dấu phẩy nếu danh sách kéo dài nhiều dòng, không sử dụng nó nếu danh sách nằm trên một dòng.


15

Dấu phẩy tôi tin là được phép vì lý do tương thích ngược. Có rất nhiều mã hiện có, chủ yếu được tạo tự động, đặt dấu phẩy. Nó làm cho nó dễ dàng hơn để viết một vòng lặp mà không có điều kiện đặc biệt ở cuối. ví dụ

for_each(my_inits.begin(), my_inits.end(),
[](const std::string& value) { std::cout << value << ",\n"; });

Thực sự không có bất kỳ lợi thế nào cho lập trình viên.

PS Mặc dù việc tự động mã hóa theo cách này dễ dàng hơn, tôi thực sự luôn chú ý không đặt dấu phẩy, các nỗ lực là tối thiểu, khả năng đọc được cải thiện và điều đó quan trọng hơn. Bạn viết mã một lần, bạn đọc nó nhiều lần.


5
Tôi hoàn toàn không đồng ý; [Theo ý kiến ​​của tôi rằng] nó đã tìm được đường vào nhiều ngôn ngữ được tạo ra sau C một cách chính xác bởi vì nó có lợi cho lập trình viên có thể thay đổi nội dung của mảng, nhận xét các dòng willy-nilly, v.v. mà không phải lo lắng về lỗi cú pháp chuyển vị ngớ ngẩn. Có phải chúng ta chưa đủ căng thẳng?
Đã ra mắt

12
@ Đã phát hành - theo cùng một logic, tại sao không nên cho phép theo dõi (bất cứ điều gì), làm thế nào int a = b + c +;hoặc if(a && b &&);sẽ dễ dàng hơn khi chỉ sao chép và dán bất cứ thứ gì ở cuối và dễ dàng hơn để viết trình tạo mã. Vấn đề này là tầm thường và chủ quan, trong những trường hợp như vậy, luôn luôn tốt để làm những gì tốt nhất cho trình đọc mã.
Gene Bushuyev

1
@Gene Bushuyev: Chính xác! Tôi thường có các biểu thức dài với + hoặc &&, với toán tử ở cuối dòng và tất nhiên, tôi phải dành thêm thời gian khi tôi muốn xóa toán hạng cuối cùng của biểu thức. Tôi nghĩ cú pháp dấu phẩy này thực sự kỳ quặc!
Giorgio

2
@GeneBushuyev - Tôi không đồng ý với những điều đó. Mặc dù cho phép dấu phẩy trong các mảng và tương tự là một tính năng loại bỏ lỗi và làm cho cuộc sống của bạn trở thành một lập trình viên dễ dàng hơn, tôi sẽ hoàn toàn có thể đọc các biện pháp để loại bỏ các câu lệnh AND (&&), kìm và các toán tử linh tinh khác khỏi điều kiện các câu lệnh. Nó chỉ đơn giản là xấu xí, IMO.
Sune Rasmussen

2
Về &&toán tử, đôi khi tôi thực hiện các điều kiện như if (true \n && b1 \n && b2)vậy để tôi có thể thêm và xóa các dòng khi tôi cần.
Christian Mann

12

Một trong những lý do điều này được cho phép theo như tôi biết là nó đơn giản để tự động tạo mã; bạn không cần bất kỳ xử lý đặc biệt nào cho phần tử cuối cùng.


11

Nó làm cho các trình tạo mã phun ra các mảng hoặc liệt kê dễ dàng hơn.

Hãy tưởng tượng:

std::cout << "enum Items {\n";
for(Items::iterator i(items.begin()), j(items.end); i != j; ++i)
    std::cout << *i << ",\n";
std::cout << "};\n";

Tức là, không cần phải xử lý đặc biệt của mục đầu tiên hoặc cuối cùng để tránh nhổ dấu phẩy.

Ví dụ, nếu trình tạo mã được viết bằng Python, thì có thể dễ dàng tránh việc tạo dấu phẩy bằng cách sử dụng str.join()hàm:

print("enum Items {")
print(",\n".join(items))
print("}")

10

Tôi ngạc nhiên sau tất cả thời gian này, không ai trích dẫn Tài liệu tham khảo C ++ được chú thích ( ARM ), nó nói như sau về [dcl.init] với phần nhấn mạnh của tôi:

Rõ ràng có quá nhiều ký hiệu cho việc khởi tạo, nhưng mỗi ký hiệu dường như phục vụ tốt một phong cách sử dụng cụ thể. Ký hiệu = {initizer_list, opt} được kế thừa từ C và phục vụ tốt cho việc khởi tạo cấu trúc dữ liệu và mảng. [...]

mặc dù ngữ pháp đã phát triển kể từ khi ARM được viết, nguồn gốc vẫn còn.

và chúng ta có thể đi đến lý do C99 để xem tại sao điều này được cho phép trong C và nó nói:

K & R cho phép dấu phẩy trong bộ khởi tạo ở cuối danh sách khởi tạo. Tiêu chuẩn đã giữ lại cú pháp này, vì nó cung cấp sự linh hoạt trong việc thêm hoặc xóa các thành viên khỏi danh sách khởi tạo và đơn giản hóa việc tạo ra các danh sách đó.


1
Upvote cho câu trả lời được sao lưu nhiều nhất bởi văn học, và nguồn gốc thực sự của tính năng này.
Marko

10

Tôi thấy một trường hợp sử dụng không được đề cập trong các câu trả lời khác, Macros yêu thích của chúng tôi:

int a [] = {
#ifdef A
    1, //this can be last if B and C is undefined
#endif
#ifdef B
    2,
#endif
#ifdef C
    3,
#endif
};

Thêm macro để xử lý cuối cùng ,sẽ là nỗi đau lớn. Với sự thay đổi nhỏ trong cú pháp này, đây là chuyện nhỏ để quản lý. Và điều này quan trọng hơn mã do máy tạo ra bởi vì thường rất dễ thực hiện trong Turing langue hoàn chỉnh hơn so với preprocesor rất hạn chế.


7

Ngôn ngữ duy nhất mà nó - trong thực tế * - không được phép là Javascript và nó gây ra vô số vấn đề. Ví dụ: nếu bạn sao chép và dán một dòng từ giữa mảng, dán nó ở cuối và quên xóa dấu phẩy thì trang web của bạn sẽ bị hỏng hoàn toàn cho khách truy cập IE của bạn.

* Về lý thuyết thì được phép nhưng Internet Explorer không tuân theo tiêu chuẩn và coi đó là lỗi


"Mảng" của JavaScript (vốn chỉ là các đối tượng có thuộc tính chiều dài kỳ diệu) dù sao cũng khá bất thường: var x = [,,,]là hợp pháp (ngoại trừ trong IE <9, nhưng thông số kỹ thuật cho biết đó là hợp pháp)
Peter C

Theo đặc tả ECMAScript, nó hoàn toàn hợp lệ; về mặt lý thuyết, nó sẽ hoạt động trong bất kỳ trình duyệt nào triển khai JavaScript theo thông số kỹ thuật đã nói, đặc biệt là một phần của đặc tả được tìm thấy ở đây .
Đã ra mắt

1
Thật không may, JavaScript là tất cả về việc tạo các ứng dụng cho công chúng. Vì vậy, không hoàn toàn hợp lệ khi ~ 50% người dùng gặp sự cố khi sử dụng ứng dụng của bạn. Và vâng, nếu tôi có thể cấm IE <9 - chỉ mất quá nhiều giờ để tạo ra mã tốt hoạt động ở đó ...
kgadek

@Dere: vâng, tôi đã nói nhiều như vậy trong câu trả lời của mình =)
Thomas Bonini

@Derelease microsoft phát minh ra các thông số kỹ thuật và mệnh lệnh của riêng mình mà ít nhất người khác tuân theo tâm lý đó đang thay đổi (cảm ơn chúa)
Chris McGrath

7

Nó dễ dàng hơn cho các máy, tức là phân tích cú pháp và tạo mã. Nó cũng dễ dàng hơn cho con người, tức là sửa đổi, bình luận và thanh lịch bằng hình ảnh thông qua tính nhất quán.

Giả sử C, bạn sẽ viết như sau?

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    puts("Line 1");
    puts("Line 2");
    puts("Line 3");

    return EXIT_SUCCESS
}

Không. Không chỉ bởi vì tuyên bố cuối cùng là một lỗi, mà còn bởi vì nó không nhất quán. Vậy tại sao làm tương tự với các bộ sưu tập? Ngay cả trong các ngôn ngữ cho phép bạn bỏ qua dấu chấm phẩy và dấu phẩy cuối cùng, cộng đồng thường không thích nó. Ví dụ, cộng đồng Perl dường như không thích bỏ dấu chấm phẩy, thanh một lớp. Họ áp dụng điều đó cho dấu phẩy quá.

Đừng bỏ qua dấu phẩy trong các bộ sưu tập nhiều dòng vì cùng một lý do bạn không sử dụng dấu chấm phẩy cho các khối mã đa dòng. Ý tôi là, bạn sẽ không làm điều đó ngay cả khi ngôn ngữ cho phép, phải không? Đúng?


Có những ngôn ngữ (ví dụ Pascal) cho phép điều đó. Tức là bạn phải lựa chọn giữa; như một dấu kết thúc (C) hoặc như một dấu phân cách (Pascal). Giống với ','. Sẽ là ổn đối với tôi nếu ',' là dấu kết thúc, nhưng sau đó {1, 2, 3} phải là lỗi cú pháp.
Giorgio

6

Lý do là tầm thường: dễ dàng thêm / xóa dòng.

Hãy tưởng tượng đoạn mã sau:

int a[] = {
   1,
   2,
   //3, // - not needed any more
};

Giờ đây, đôi khi bạn có thể dễ dàng thêm / xóa các mục vào danh sách mà không cần phải thêm / xóa dấu phẩy.

Ngược lại với các câu trả lời khác, tôi thực sự không nghĩ rằng việc dễ dàng tạo danh sách là một lý do hợp lệ: xét cho cùng, việc đặt mã cho trường hợp đặc biệt là dòng cuối cùng (hoặc đầu tiên) là chuyện nhỏ. Trình tạo mã được viết một lần và sử dụng nhiều lần.


6

Nó cho phép mọi dòng theo cùng một hình thức. Thứ nhất, điều này giúp dễ dàng thêm các hàng mới và có hệ thống kiểm soát phiên bản theo dõi sự thay đổi một cách có ý nghĩa và nó cũng cho phép bạn phân tích mã dễ dàng hơn. Tôi không thể nghĩ ra một lý do kỹ thuật.


5

Điều này được phép bảo vệ khỏi những sai lầm gây ra bởi các yếu tố di chuyển xung quanh trong một danh sách dài.

Ví dụ: giả sử chúng ta có một mã trông như thế này.

#include <iostream>
#include <string>
#include <cstddef>
#define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array))
int main() {
    std::string messages[] = {
        "Stack Overflow",
        "Super User",
        "Server Fault"
    };
    size_t i;
    for (i = 0; i < ARRAY_SIZE(messages); i++) {
        std::cout << messages[i] << std::endl;
    }
}

Và thật tuyệt vời, vì nó cho thấy bộ ba trang web Stack Exchange ban đầu.

Stack Overflow
Super User
Server Fault

Nhưng có một vấn đề với nó. Bạn thấy, phần chân trang trên trang web này hiển thị Lỗi máy chủ trước Siêu người dùng. Sửa chữa tốt hơn trước khi bất cứ ai thông báo.

#include <iostream>
#include <string>
#include <cstddef>
#define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array))
int main() {
    std::string messages[] = {
        "Stack Overflow",
        "Server Fault"
        "Super User",
    };
    size_t i;
    for (i = 0; i < ARRAY_SIZE(messages); i++) {
        std::cout << messages[i] << std::endl;
    }
}

Rốt cuộc, việc di chuyển xung quanh không thể khó đến thế, phải không?

Stack Overflow
Server FaultSuper User

Tôi biết, không có trang web nào gọi là "Máy chủ FaultSuper", nhưng trình biên dịch của chúng tôi tuyên bố nó tồn tại. Bây giờ, vấn đề là C có tính năng nối chuỗi, cho phép bạn viết hai chuỗi trích dẫn kép và nối chúng không sử dụng gì (vấn đề tương tự cũng có thể xảy ra với số nguyên, vì -dấu có nhiều nghĩa).

Bây giờ nếu mảng ban đầu có dấu phẩy vô dụng thì sao? Chà, các dòng sẽ được di chuyển xung quanh, nhưng lỗi như vậy sẽ không xảy ra. Thật dễ dàng để bỏ lỡ một cái gì đó nhỏ như dấu phẩy. Nếu bạn nhớ đặt dấu phẩy sau mỗi phần tử mảng, lỗi đó không thể xảy ra. Bạn sẽ không muốn lãng phí bốn giờ để gỡ lỗi một cái gì đó, cho đến khi bạn thấy dấu phẩy là nguyên nhân gây ra vấn đề của bạn .


4

Giống như nhiều thứ khác, dấu phẩy trong bộ khởi tạo mảng là một trong những thứ C ++ được thừa hưởng từ C (và sẽ phải hỗ trợ mãi mãi). Một quan điểm hoàn toàn khác với những quan điểm được đặt ở đây được đề cập trong cuốn sách "Bí mật Deep C" .

Trong một ví dụ có nhiều hơn một "nghịch lý dấu phẩy":

char *available_resources[] = {
"color monitor"           ,
"big disk"                ,
"Cray"                      /* whoa! no comma! */
"on-line drawing routines",
"mouse"                   ,
"keyboard"                ,
"power cables"            , /* and what's this extra comma? */
};

chúng tôi đọc :

... mà dấu dấu phẩy sau initializer thức không phải là một lỗi đánh máy, nhưng là một đốm sáng trong cú pháp chuyển sang từ thổ dân C . Sự hiện diện hay vắng mặt của nó được cho phép nhưng không có ý nghĩa . Sự biện minh được tuyên bố trong lý do ANSI C là nó làm cho việc tạo C tự động dễ dàng hơn. Khiếu nại sẽ đáng tin cậy hơn nếu dấu phẩy được cho phép trong mọi danh sách được xếp hạng bằng dấu phẩy , chẳng hạn như trong khai báo enum hoặc nhiều khai báo biến trong một khai báo. Họ không phải.

... với tôi điều này có ý nghĩa hơn


2
Việc cấm dấu phẩy trong enumtrường hợp này có phần thú vị, vì đó là trường hợp dấu phẩy bị mất sẽ gây ra sự mơ hồ ít nhất. Cho trước struct foo arr[] = {{1,2,3,4,5}, {3,4,5,6,7}, }; có hai ý nghĩa hợp lý mà ngôn ngữ có thể gán: tạo một mảng hai phần tử hoặc tạo một mảng ba phần tử trong đó mục cuối cùng có các giá trị mặc định. Nếu C đã thông qua cách giải thích sau này, tôi có thể thấy việc cấm enum foo {moe, larry, curly, };theo nguyên tắc rằng chỉ nên có một cách để viết tuyên bố (không có dấu phẩy), nhưng ...
supercat

1
... Cho rằng C sẵn sàng bỏ qua dấu phẩy trong trường hợp nó có thể được (nhưng không) được gán một ý nghĩa quan trọng (sẽ là một lập luận mạnh mẽ ủng hộ việc cấm nó ở đó), nó tò mò rằng nó không phải là trong trường hợp dấu phẩy không thể có nghĩa [ngay cả khi người ta hiểu enum foo {moe,,larry,curly,};là bỏ qua một số giữa moelarry, nói chung sẽ không có vấn đề gì nếu dấu phẩy được xử lý hoặc bỏ qua. Trường hợp duy nhất có thể có vấn đề là nếu mục cuối cùng là giá trị tối đa cho loại khai báo của nó và ...
supercat

1
... có thể được xử lý bằng cách đơn giản nói rằng tràn xảy ra sau giá trị liệt kê được gán cuối cùng sẽ bị bỏ qua.
supercat

@supercat Có các ngôn ngữ, như C #, trong đó một nghiên cứu thiết kế tiên nghiệm đi xa đến mức xem xét các tính năng và tích hợp IDE khi phá hủy ngôn ngữ. C không phải (và không thể có) một trong những ngôn ngữ này.
Nikos Athanasiou

Ngay cả với các ngôn ngữ như C #, việc thay đổi mục tiêu thiết kế đã dẫn đến một số mâu thuẫn thiết kế khá nghiêm trọng. Ví dụ, ngôn ngữ không được hỗ trợ bất kỳ hình thức nạp chồng kiểu trả về nào cho các phương thức và toán tử thông thường (mặc dù khung cơ bản có thể hỗ trợ nó) vì nó được xem là trái với mục tiêu có ngôn ngữ đơn giản để biên dịch, nhưng đánh giá lambda bao gồm các quy tắc suy luận kiểu có độ phân giải NP-đầy đủ. Thêm quy tắc nạp chồng phương thức / toán tử mới có thể phá vỡ mã hiện có (mặc dù tôi nghĩ rằng các quy tắc tốt có thể giảm thiểu nguy hiểm như vậy) ...
supercat

2

Ngoài việc tạo mã và chỉnh sửa dễ dàng, nếu bạn muốn triển khai trình phân tích cú pháp, loại ngữ pháp này đơn giản và dễ thực hiện hơn. C # tuân theo quy tắc này ở một số nơi có danh sách các mục được phân tách bằng dấu phẩy, như các mục trong enumđịnh nghĩa.


1

Nó giúp việc tạo mã dễ dàng hơn khi bạn chỉ cần thêm một dòng và không cần xử lý thêm mục cuối như thể đó là trường hợp đặc biệt. Điều này đặc biệt đúng khi sử dụng macro để tạo mã. Có một nỗ lực để cố gắng loại bỏ sự cần thiết của macro từ ngôn ngữ, nhưng rất nhiều ngôn ngữ đã phát triển cùng với các macro có sẵn. Dấu phẩy thừa cho phép các macro như sau được xác định và sử dụng:

#define LIST_BEGIN int a[] = {
#define LIST_ENTRY(x) x,
#define LIST_END };

Sử dụng:

LIST_BEGIN
   LIST_ENTRY(1)
   LIST_ENTRY(2)
LIST_END

Đó là một ví dụ rất đơn giản, nhưng thường mẫu này được các macro sử dụng để xác định những thứ như công văn, thông báo, sự kiện hoặc bản đồ dịch và bảng. Nếu dấu phẩy không được phép ở cuối, chúng ta sẽ cần một đặc biệt:

#define LIST_LAST_ENTRY(x) x

và điều đó sẽ rất khó sử dụng.


0

Vì vậy, khi hai người thêm một mục mới vào danh sách trên các nhánh riêng biệt, Git có thể hợp nhất các thay đổi, vì Git hoạt động trên cơ sở dòng.


-4

Nếu bạn sử dụng một mảng không có độ dài được chỉ định, VC ++ 6.0 có thể tự động xác định độ dài của nó, vì vậy nếu bạn sử dụng "int a [] = {1,2,};" thì độ dài của a là 3, nhưng chiều dài cuối cùng là ' Được khởi tạo, bạn có thể sử dụng "cout <


Đây có phải là một lỗi cho VC6 không phù hợp với tiêu chuẩn không?
Thomson
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.