Điều gì chính () trở lại trong C và C ++?


694

28
Tôi vẫn nghĩ nó khá mơ hồ. Xác định "hiệu quả nhất" đối với tôi. Hiệu quả theo nghĩa nào? Theo nghĩa chiếm ít bộ nhớ? Theo nghĩa chạy nhanh hơn? Tôi có thể thấy các câu trả lời hữu ích nhưng tôi vẫn nghĩ rằng câu hỏi được đặt ra khá kém.
Onorio Catenacci

7
Pish posh, bối cảnh hiệu quả là rõ ràng ở đây, đặc biệt là với các ví dụ (có khả năng ở đó để làm rõ định nghĩa của "hiệu quả"). Hy vọng rằng bộ đệm kém đã không bò vào một lỗ và hoàn toàn hối tiếc câu hỏi. Người ta có thể nói, bất kể khoảng trống hay int, một giá trị được trả về, do đó, nó không có tác động đến kích thước tệp, các hoạt động được thực hiện, cũng như bộ nhớ được phân bổ. Và mọi người, trên hầu hết các hệ điều hành, có xu hướng trả về 0 khi thành công và một cái gì đó khác - thành công hoặc thất bại - nhưng không có tiêu chuẩn. Cuối cùng, không có sự khác biệt về hiệu quả theo bất kỳ cách rõ ràng nào.
Kit10

"chính xác (hiệu quả nhất)" không có ý nghĩa. Hiệu quả là một chuyện, đúng là một chuyện khác. mainđược gọi một lần (và trong C ++ chỉ có thể được gọi một lần: không có đệ quy). Nếu bạn không muốn thực thi dành nhiều thời gian main, thì đừng gọi chương trình nhiều lần: làm cho chương trình thực hiện việc lặp lại.
Kaz

2
Tôi thấy thú vị rằng không có câu trả lời nào, theo như tôi có thể nói, cung cấp một ví dụ hoạt động đầy đủ, bao gồm các #includecâu
puk

3
Các giá trị trả về không có ý nghĩa gì trên một nền tảng không có HĐH. Bạn sẽ không trở lại bất cứ điều gì. Nếu bạn nhấn một returntrong main(...)trên một thiết bị nhúng, hệ thống của bạn đi vào một trạng thái không thể đoán trước và máy giặt của bạn sẽ trở nên tự ý thức và cố gắng để giết bạn. Vì vậy, chúng tôi sử dụng void main()trong trường hợp đó. Đây là thực hành tiêu chuẩn công nghiệp trong nhúng kim loại trần.
3Dave

Câu trả lời:


569

Giá trị trả về cho mainbiết chương trình đã thoát như thế nào. Lối ra bình thường được biểu thị bằng giá trị trả về 0 từ main. Lối ra bất thường được báo hiệu bằng lợi nhuận khác không, nhưng không có tiêu chuẩn nào cho cách giải thích mã khác không. Theo ghi nhận của những người khác, void main()bị cấm theo tiêu chuẩn C ++ và không nên được sử dụng. mainChữ ký C ++ hợp lệ là:

int main()

int main(int argc, char* argv[])

tương đương với

int main(int argc, char** argv)

Cũng cần lưu ý rằng trong C ++, int main()có thể được để lại mà không có câu lệnh return, tại thời điểm đó, nó mặc định là trả về 0. Điều này cũng đúng với chương trình C99. Có return 0;nên bỏ qua hay không là mở để tranh luận. Phạm vi chữ ký chính của chương trình C hợp lệ là lớn hơn nhiều.

Hiệu quả không phải là một vấn đề với mainchức năng. Nó chỉ có thể được nhập và để lại một lần (đánh dấu bắt đầu và kết thúc chương trình) theo tiêu chuẩn C ++. Đối với C, nhập lại main()được cho phép, nhưng nên tránh.


69
chính CÓ THỂ được nhập / rời nhiều lần, nhưng chương trình đó có thể sẽ không giành được bất kỳ giải thưởng thiết kế nào;)
korona

13
C99 cũng có tính năng sai C ++ đạt đến cuối hàm main () tương đương với trả về 0 - nếu main () được xác định để trả về loại tương thích với int (phần 5.1.2.2.3).
Jonathan Leffler

62
nhập lại chính không hợp lệ C ++. Rõ ràng trong tiêu chuẩn, 3.6.1.3 không được sử dụng chính trong một chương trình '
workmad3

117
stdlib.h cung cấp EXIT_SUCCESS và EXIT_FAILURE cho mục đích này
Clay

20
0 và khác không là đúng nhưng hoàn toàn vô nghĩa với ai đó đang đọc mã của bạn. Câu hỏi này là bằng chứng cho thấy mọi người không biết mã hợp lệ / không hợp lệ là gì. EXIT_SUCCESS / EXIT_FAILURE rõ ràng hơn nhiều.
JaredPar

169

Câu trả lời được chấp nhận dường như được nhắm mục tiêu cho C ++, vì vậy tôi nghĩ rằng tôi đã thêm một câu trả lời liên quan đến C, và điều này khác nhau theo một số cách.

ISO / IEC 9899: 1989 (C90):

main() nên được khai báo là một trong hai:

int main(void)
int main(int argc, char **argv)

Hoặc tương đương. Ví dụ, int main(int argc, char *argv[])tương đương với cái thứ hai. Hơn nữa, intloại trả về có thể được bỏ qua vì nó là một mặc định.

Nếu một triển khai cho phép nó, main()có thể được khai báo theo những cách khác, nhưng điều này làm cho việc thực hiện chương trình được xác định và không còn tuân thủ nghiêm ngặt.

Tiêu chuẩn xác định 3 giá trị để trả về tuân thủ nghiêm ngặt (nghĩa là không dựa vào hành vi được xác định thực hiện): 0EXIT_SUCCESSđể chấm dứt thành công và EXIT_FAILUREchấm dứt không thành công. Bất kỳ giá trị khác là không chuẩn và thực hiện được xác định. main()phải có một returntuyên bố rõ ràng ở cuối để tránh hành vi không xác định.

Cuối cùng, không có gì sai từ quan điểm tiêu chuẩn với việc gọi main()từ một chương trình.

ISO / IEC 9899: 1999 (C99):

Đối với C99, mọi thứ đều giống như trên ngoại trừ:

  • Các intkiểu trả về có thể không được bỏ qua.
  • Bạn có thể bỏ qua câu lệnh return từ main(). Nếu bạn làm, và main()kết thúc, có một ẩn return 0.

1
@Lundin Tôi không nghĩ bạn cần một trích dẫn để nói rằng ai đó được phép tạo một trình biên dịch chấp nhận các chương trình không tuân thủ tiêu chuẩn hoặc để có một trình biên dịch không tuân thủ tiêu chuẩn. Đó là kiến ​​thức phổ biến và lẽ thường
KABoissonneault 7/07/2015

4
@KABoissonneault Hành vi được xác định thực hiện là một thuật ngữ từ tiêu chuẩn, trái ngược với hành vi hoàn toàn không có giấy tờ. Nếu bạn thực hiện một cái gì đó được liệt kê là hành vi xác định thực hiện, bạn vẫn tuân theo tiêu chuẩn. Trong trường hợp này, C89 đã được trích dẫn, liệt kê không có hành vi được xác định thực hiện như vậy, do đó cần phải trích dẫn, để chứng minh rằng anh ta không chỉ làm cho mọi thứ trở nên khó hiểu.
Lundin

1
@Lundin Bạn đang thấy điều này sai cách. Những gì chúng ta đang nói đến không phải là hành vi được xác định theo triển khai, chúng ta đang nói về việc triển khai đi lệch khỏi tiêu chuẩn nếu họ chọn như vậy. Nó giống như một đứa trẻ không vâng lời cha mẹ: bạn không cần một câu trích dẫn từ cha mẹ để nói với bạn theo cách mà một đứa trẻ có thể đi ngược lại những gì cha mẹ nói. Bạn chỉ cần biết rằng khoảnh khắc đứa trẻ chọn làm như vậy, chúng không còn tuân thủ theo các
bang hội

2
@KABoissonneault Phần tôi trích dẫn trong nhận xét của tôi chắc chắn là về hành vi được xác định thực hiện (trái ngược với các phần mở rộng trình biên dịch không chuẩn .) Vì vậy, tôi đang nói về hành vi được xác định thực hiện. Nếu bạn đang có một cuộc độc thoại về điều gì khác, tốt nhất là may mắn với điều đó.
Lundin

1
@Lundin Tôi đoán từ ngữ trong trích dẫn là khó hiểu (phần họ nói "nhưng điều này làm cho việc thực hiện chương trình được xác định") nhưng tôi khá chắc chắn rằng người đó đã nói về hành vi không chuẩn (như đã nói trong "Nếu việc thực hiện cho phép nó "và" và không còn tuân thủ nghiêm ngặt [theo tiêu chuẩn] ") trái với hành vi được xác định thực hiện thực tế. Người đó chắc chắn nên điều chỉnh lại câu trả lời của họ, nhưng tôi vẫn không nghĩ rằng một trích dẫn từ tiêu chuẩn là cần thiết về điều đó
KABoissonneault

117

Tiêu chuẩn C - Môi trường lưu trữ

Đối với môi trường được lưu trữ (đó là môi trường bình thường), tiêu chuẩn C11 (ISO / IEC 9899: 2011) cho biết:

5.1.2.2.1 Khởi động chương trình

Hàm được gọi khi khởi động chương trình được đặt tên main. Việc thực hiện tuyên bố không có nguyên mẫu cho chức năng này. Nó sẽ được định nghĩa với kiểu trả về intvà không có tham số:

int main(void) { /* ... */ }

hoặc với hai tham số (được gọi ở đây là argcargv, mặc dù có thể sử dụng bất kỳ tên nào, vì chúng là cục bộ của hàm mà chúng được khai báo):

int main(int argc, char *argv[]) { /* ... */ }

hoặc tương đương; 10) hoặc trong một số cách xác định thực hiện khác.

Nếu chúng được khai báo, các tham số cho hàm chính sẽ tuân theo các ràng buộc sau:

  • Giá trị của argcsẽ là không âm.
  • argv[argc] sẽ là một con trỏ rỗng.
  • Nếu giá trị argclớn hơn 0, các thành viên mảng argv[0]thông qua argv[argc-1]sẽ bao gồm các con trỏ tới các chuỗi, được đưa ra các giá trị được xác định theo thực thi bởi môi trường máy chủ trước khi khởi động chương trình. Mục đích là cung cấp cho thông tin chương trình được xác định trước khi khởi động chương trình từ nơi khác trong môi trường được lưu trữ. Nếu môi trường máy chủ không có khả năng cung cấp các chuỗi có chữ cái ở cả chữ hoa và chữ thường, thì việc thực hiện phải đảm bảo rằng các chuỗi được nhận bằng chữ thường.
  • Nếu giá trị của argclớn hơn 0, chuỗi được trỏ đến argv[0] đại diện cho tên chương trình; argv[0][0]sẽ là ký tự null nếu tên chương trình không có sẵn từ môi trường máy chủ. Nếu giá trị của argclớn hơn một, các chuỗi được trỏ đến argv[1]thông qua argv[argc-1] đại diện cho các tham số chương trình.
  • Các tham số argcargvchuỗi được chỉ ra bởi argvmảng sẽ được chương trình sửa đổi và giữ lại các giá trị được lưu trữ cuối cùng của chúng giữa khởi động chương trình và kết thúc chương trình.

10) Do đó, intcó thể được thay thế bằng một tên typedef được định nghĩa là int, hoặc loại argvcó thể được viết là char **argv, v.v.

Chấm dứt chương trình trong C99 hoặc C11

Giá trị được trả về từ main()được truyền đến "môi trường" theo cách được xác định theo cách thực hiện.

5.1.2.2.3 Chấm dứt chương trình

1 Nếu kiểu trả về của mainhàm là loại tương thích với int, thì việc trả về từ lệnh gọi ban đầu đến mainhàm tương đương với việc gọi exithàm với giá trị được mainhàm trả về làm đối số của nó; 11) đạt đến mức }kết thúc mainhàm trả về giá trị 0. Nếu kiểu trả về không tương thích int, trạng thái kết thúc được trả về môi trường máy chủ là không xác định.

11) Theo 6.2.4, thời gian sống của các đối tượng có thời gian lưu trữ tự động được khai báo main sẽ kết thúc trong trường hợp trước, ngay cả khi chúng không có trong trường hợp sau.

Lưu ý rằng 0bắt buộc là "thành công". Bạn có thể sử dụng EXIT_FAILUREEXIT_SUCCESStừ <stdlib.h>nếu bạn thích, nhưng 0 được thiết lập tốt và vì vậy 1. Xem thêm Thoát mã lớn hơn 255 - có thể không? .

Trong C89 (và do đó trong Microsoft C), không có tuyên bố nào về những gì sẽ xảy ra nếu main()hàm trả về nhưng không chỉ định giá trị trả về; do đó dẫn đến hành vi không xác định.

7.22.4.4 exitHàm

¶5 Cuối cùng, điều khiển được trả về môi trường máy chủ. Nếu giá trị statusbằng 0 hoặc EXIT_SUCCESS, một hình thức xác định thực hiện của trạng thái chấm dứt thành công được trả về. Nếu giá trị statusEXIT_FAILURE, một hình thức xác định thực hiện của trạng thái chấm dứt không thành công được trả về. Nếu không, trạng thái trả về là xác định thực hiện.

Tiêu chuẩn C ++ - Môi trường lưu trữ

Tiêu chuẩn C ++ 11 (ISO / IEC 14882: 2011) cho biết:

3.6.1 Hàm chính [basic.start.main]

¶1 Một chương trình sẽ chứa một hàm toàn cục gọi là main, là khởi đầu được chỉ định của chương trình. [...]

¶2 Việc thực hiện sẽ không xác định trước chức năng chính. Chức năng này sẽ không bị quá tải. Nó sẽ có kiểu trả về kiểu int, nhưng nếu không thì kiểu của nó được xác định. Tất cả các triển khai sẽ cho phép cả hai định nghĩa chính sau đây:

int main() { /* ... */ }

int main(int argc, char* argv[]) { /* ... */ }

Ở dạng sau argcsẽ là số lượng đối số được truyền cho chương trình từ môi trường mà chương trình được chạy. Nếu argckhông khác, các đối số này sẽ được cung cấp argv[0] thông qua argv[argc-1]dưới dạng con trỏ tới các ký tự ban đầu của chuỗi đa chuỗi kết thúc null (NTMBS) (17.5.2.1.4.2) và argv[0]sẽ là con trỏ tới ký tự ban đầu của NTMBS đại diện cho tên được sử dụng để gọi chương trình hay "". Giá trị của argcsẽ không âm. Giá trị của argv[argc] sẽ là 0. [Lưu ý: Nên thêm bất kỳ tham số (tùy chọn) nào nữa argv. Lưu ý

¶3 Chức năng mainsẽ không được sử dụng trong một chương trình. Liên kết (3.5) của mainđược xác định theo thực hiện. [...]

¶5 Một câu lệnh return trong main có tác dụng rời khỏi hàm main (hủy mọi đối tượng có thời lượng lưu trữ tự động) và gọi std::exitvới giá trị trả về làm đối số. Nếu điều khiển đạt đến cuối của chính mà không gặp phải một câu lệnh return, thì hiệu quả là việc thực thi

return 0;

Tiêu chuẩn C ++ nói rõ ràng "Nó [chức năng chính] sẽ có kiểu trả về int, nhưng nếu không thì kiểu của nó được xác định" và yêu cầu hai chữ ký giống như tiêu chuẩn C được hỗ trợ làm tùy chọn. Vì vậy, 'void main ()' không được phép theo tiêu chuẩn C ++, mặc dù không có gì có thể làm để ngăn chặn việc triển khai không chuẩn cho phép các lựa chọn thay thế. Lưu ý rằng C ++ cấm người dùng gọi main(nhưng tiêu chuẩn C thì không).

Có một đoạn §18.5 Bắt đầu và kết thúc trong chuẩn C ++ 11 giống hệt đoạn từ §7.22.4.4 Các exitchức năng trong các tiêu chuẩn C11 (trích dẫn ở trên), ngoài một chú thích (mà chỉ đơn giản các tài liệu EXIT_SUCCESSEXIT_FAILUREđược định nghĩa trong <cstdlib>).

Tiêu chuẩn C - Gia hạn chung

Về mặt kinh điển, các hệ thống Unix hỗ trợ một biến thể thứ ba:

int main(int argc, char **argv, char **envp) { ... }

Đối số thứ ba là danh sách các con trỏ kết thúc null, mỗi chuỗi là một biến môi trường có tên, dấu bằng và giá trị (có thể trống). Nếu bạn không sử dụng điều này, bạn vẫn có thể truy cập vào môi trường thông qua ' extern char **environ;'. Biến toàn cục này là duy nhất trong số các biến trong POSIX ở chỗ nó không có tiêu đề khai báo nó.

Điều này được công nhận bởi tiêu chuẩn C là một phần mở rộng phổ biến, được ghi lại trong Phụ lục J:

J.5.1 Đối số môi trường

¶1 Trong một môi trường được lưu trữ, hàm chính nhận được một đối số thứ ba, char *envp[]trỏ đến một mảng các con trỏ kết thúc null char, mỗi chuỗi trỏ đến một chuỗi cung cấp thông tin về môi trường để thực hiện chương trình này (5.1. 2.2.1).

Microsoft C

Các Microsoft VS 2010 trình biên dịch là thú vị. Trang web nói:

Cú pháp khai báo cho main là

 int main();

hoặc, tùy chọn,

int main(int argc, char *argv[], char *envp[]);

Ngoài ra, các hàm mainwmaincó thể được khai báo là trả về void(không có giá trị trả về). Nếu bạn khai báo mainhoặc wmaintrả về khoảng trống, bạn không thể trả lại mã thoát cho quy trình cha hoặc hệ điều hành bằng cách sử dụng câu lệnh return. Để trả về mã thoát khi mainhoặc wmainđược khai báo là void, bạn phải sử dụng exithàm.

Tôi không rõ điều gì xảy ra (mã thoát nào được trả về cho cha mẹ hoặc HĐH) khi một chương trình void main()không thoát - và trang web MS cũng im lặng.

Thật thú vị, MS không quy định phiên bản hai đối số main()mà các tiêu chuẩn C và C ++ yêu cầu. Nó chỉ quy định một hình thức ba đối số trong đó đối số thứ ba là char **envpcon trỏ tới danh sách các biến môi trường.

Trang Microsoft cũng liệt kê một số lựa chọn thay thế khác - wmain()có các chuỗi ký tự rộng và một số khác.

Phiên bản Microsoft Visual Studio 2005 của trang này không liệt kê void main()thay thế. Các phiên bản từ Microsoft Visual Studio 2008 trở đi làm.

Tiêu chuẩn C - Môi trường độc lập

Như đã lưu ý sớm, các yêu cầu trên áp dụng cho môi trường được lưu trữ. Nếu bạn đang làm việc với một môi trường tự do (là sự thay thế cho một môi trường được lưu trữ), thì tiêu chuẩn có rất ít điều để nói. Đối với một môi trường tự do, hàm được gọi khi khởi động chương trình không cần phải được gọi mainvà không có ràng buộc nào đối với kiểu trả về của nó. Tiêu chuẩn nói:

5.1.2 Môi trường thực thi

Hai môi trường thực thi được xác định: freestanding và lưu trữ. Trong cả hai trường hợp, khởi động chương trình xảy ra khi một hàm C được chỉ định được gọi bởi môi trường thực thi. Tất cả các đối tượng có thời lượng lưu trữ tĩnh sẽ được khởi tạo (được đặt thành giá trị ban đầu của chúng) trước khi khởi động chương trình. Cách thức và thời gian khởi tạo như vậy là không xác định. Chấm dứt chương trình trả lại quyền điều khiển cho môi trường thực thi.

5.1.2.1 Môi trường tự do

Trong một môi trường tự do (trong đó thực thi chương trình C có thể diễn ra mà không có bất kỳ lợi ích nào của hệ điều hành), tên và loại chức năng được gọi khi khởi động chương trình được xác định theo thực hiện. Bất kỳ cơ sở thư viện nào có sẵn cho một chương trình độc lập, ngoài bộ tối thiểu theo yêu cầu của khoản 4, đều được xác định theo triển khai.

Hiệu quả của việc chấm dứt chương trình trong một môi trường tự do được xác định theo thực hiện.

Tham chiếu chéo của khoản 4 Sự phù hợp đề cập đến điều này:

¶5 Một chương trình tuân thủ nghiêm ngặt sẽ chỉ sử dụng các tính năng của ngôn ngữ và thư viện được chỉ định trong Tiêu chuẩn quốc tế này. 3) Nó sẽ không tạo ra đầu ra phụ thuộc vào bất kỳ hành vi không xác định, không xác định hoặc xác định thực hiện nào và không vượt quá bất kỳ giới hạn thực hiện tối thiểu nào.

6 Hai hình thức thực hiện tuân thủ được lưu trữtự do . Một triển khai lưu trữ tuân thủ sẽ chấp nhận bất kỳ chương trình tuân thủ nghiêm ngặt. Một phù hợp freestanding thực hiện trách nhiệm chấp nhận bất kỳ chương trình phù hợp chặt chẽ, trong đó việc sử dụng các tính năng theo quy định tại các khoản thư viện (khoản 7) được giới hạn trong các nội dung của tiêu đề chuẩn <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>, và <stdnoreturn.h>. Việc triển khai tuân thủ có thể có các phần mở rộng (bao gồm các chức năng thư viện bổ sung), miễn là chúng không làm thay đổi hành vi của bất kỳ chương trình tuân thủ nghiêm ngặt nào. 4)

¶7 Một chương trình phù hợp là một chương trình được chấp nhận để thực hiện tuân thủ. 5)

3) Một chương trình tuân thủ nghiêm ngặt có thể sử dụng các tính năng có điều kiện (xem 6.10.8.3) với điều kiện việc sử dụng được bảo vệ bởi một chỉ thị tiền xử lý bao gồm có điều kiện thích hợp sử dụng macro liên quan. Ví dụ:

#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */
    /* ... */
    fesetround(FE_UPWARD);
    /* ... */
#endif

4) Điều này ngụ ý rằng việc thực hiện tuân thủ dự trữ không có định danh nào ngoài những định danh được bảo lưu rõ ràng trong Tiêu chuẩn quốc tế này.

5) Các chương trình tuân thủ nghiêm ngặt nhằm mục đích mang tính di động tối đa trong số các triển khai tuân thủ. Các chương trình phù hợp có thể phụ thuộc vào các tính năng không di động của việc triển khai tuân thủ.

Điều đáng chú ý là tiêu đề duy nhất cần có của một môi trường tự do thực sự xác định bất kỳ chức năng nào là <stdarg.h>(và thậm chí những chức năng đó có thể - và thường là - chỉ là các macro).

Tiêu chuẩn C ++ - Môi trường độc lập

Giống như tiêu chuẩn C nhận ra cả môi trường lưu trữ và tự do, nên tiêu chuẩn C ++ cũng vậy. (Trích dẫn từ ISO / IEC 14882: 2011.)

1.4 Tuân thủ thực hiện [intro.compliance]

¶7 Hai loại triển khai được xác định: triển khai được lưu trữtriển khai tự do . Đối với việc triển khai được lưu trữ, Tiêu chuẩn quốc tế này xác định tập hợp các thư viện có sẵn. Việc triển khai tự do là một trong đó việc thực thi có thể diễn ra mà không có lợi ích của hệ điều hành và có một bộ thư viện được xác định theo thực thi bao gồm các thư viện hỗ trợ ngôn ngữ nhất định (17.6.1.3).

¶8 Việc triển khai tuân thủ có thể có các phần mở rộng (bao gồm các chức năng thư viện bổ sung), miễn là chúng không làm thay đổi hành vi của bất kỳ chương trình được định dạng tốt nào. Việc triển khai được yêu cầu để chẩn đoán các chương trình sử dụng các phần mở rộng như vậy không được định hình theo Tiêu chuẩn quốc tế này. Tuy nhiên, đã làm như vậy, họ có thể biên dịch và thực thi các chương trình đó.

¶9 Mỗi triển khai sẽ bao gồm tài liệu xác định tất cả các cấu trúc được hỗ trợ có điều kiện mà nó không hỗ trợ và xác định tất cả các đặc điểm cụ thể của miền địa phương. 3

3) Tài liệu này cũng xác định hành vi xác định thực hiện; xem 1.9.

17.6.1.3 Triển khai tự do [tuân thủ]

Hai loại triển khai được xác định: lưu trữ và tự do (1.4). Đối với triển khai được lưu trữ, Tiêu chuẩn quốc tế này mô tả tập hợp các tiêu đề có sẵn.

Một triển khai tự do có một bộ tiêu đề được xác định theo thực hiện. Bộ này sẽ bao gồm ít nhất các tiêu đề được hiển thị trong Bảng 16.

Phiên bản đã cung cấp của tiêu đề <cstdlib>thực hiện kê khai ít nhất các chức năng abort, atexit, at_quick_exit, exit, và quick_exit(18,5). Các tiêu đề khác được liệt kê trong bảng này sẽ đáp ứng các yêu cầu tương tự như đối với việc triển khai được lưu trữ.

Bảng 16 - Các tiêu đề C ++ để triển khai tự do

Subclause                           Header(s)
                                    <ciso646>
18.2  Types                         <cstddef>
18.3  Implementation properties     <cfloat> <limits> <climits>
18.4  Integer types                 <cstdint>
18.5  Start and termination         <cstdlib>
18.6  Dynamic memory management     <new>
18.7  Type identification           <typeinfo>
18.8  Exception handling            <exception>
18.9  Initializer lists             <initializer_list>
18.10 Other runtime support         <cstdalign> <cstdarg> <cstdbool>
20.9  Type traits                   <type_traits>
29    Atomics                       <atomic>

Còn việc sử dụng int main()trong C thì sao?

Tiêu chuẩn §5.1.2.2.1 của tiêu chuẩn C11 cho thấy ký hiệu ưa thích -  int main(void)- nhưng cũng có hai ví dụ trong tiêu chuẩn thể hiện int main(): §6.5.3.4 8§6.7.6.3 20 . Bây giờ, điều quan trọng cần lưu ý là các ví dụ không phải là 'quy chuẩn'; chúng chỉ mang tính minh họa. Nếu có lỗi trong các ví dụ, chúng không ảnh hưởng trực tiếp đến văn bản chính của tiêu chuẩn. Điều đó nói rằng, chúng là biểu hiện mạnh mẽ của hành vi dự kiến, vì vậy nếu tiêu chuẩn bao gồm int main()trong một ví dụ, nó cho thấy điều đó int main()không bị cấm, ngay cả khi đó không phải là ký hiệu ưa thích.

6.5.3.4 Các nhà khai thác sizeof_Alignof

Giáo dục

¶8 VÍ DỤ 3 Trong ví dụ này, kích thước của một mảng có chiều dài thay đổi được tính toán và trả về từ một hàm:

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}
int main()
{
    size_t size;
    size = fsize3(10); // fsize3 returns 13
    return 0;
}

@DavidBowling: Một định nghĩa hàm như int main(){ … }xác định rằng hàm không có đối số, nhưng không cung cấp nguyên mẫu hàm, AFAICT. Vì main()đó hiếm khi là một vấn đề; điều đó có nghĩa là nếu bạn có các cuộc gọi đệ quy đến main(), các đối số sẽ không được kiểm tra. Đối với các hàm khác, đó là vấn đề nhiều hơn - bạn thực sự cần một nguyên mẫu trong phạm vi khi hàm được gọi để đảm bảo rằng các đối số là chính xác.
Jonathan Leffler

1
@DavidBowling: Bạn thường không gọi main()đệ quy, bên ngoài những nơi như IOCCC. Tôi có một chương trình thử nghiệm thực hiện nó - chủ yếu cho sự mới lạ. Nếu bạn có int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }và biên dịch với GCC và không bao gồm -Wstrict-prototypes, nó sẽ biên dịch sạch theo các cảnh báo nghiêm ngặt. Nếu nó main(void), nó không biên dịch.
Jonathan Leffler

61

Tôi tin rằng main()nên trở lại EXIT_SUCCESShoặc EXIT_FAILURE. Chúng được định nghĩa trongstdlib.h


20
0 cũng là tiêu chuẩn.
Chris Young

2
@ChrisYoung Có EXIT_SUCCESSEXIT_FAILUREvì một số hệ điều hành lịch sử (VMS?) Đã sử dụng một số khác 0 để biểu thị thành công. Nó là 0 ở mọi nơi hiện nay.
fuz

5
@FUZxxl bạn đúng, nhưng điều đó không mâu thuẫn với nhận xét của tôi. EXIT_SUCCESS thực sự có thể là khác không, nhưng các tiêu chuẩn (C89, C99, C11) đều xác định 0 (cũng như EXIT_SUCCESS) cũng là một hình thức xác định thực hiện của việc chấm dứt thành công trạng thái.
Chris Young

2
@FUZxxl: Đúng là VMS đã sử dụng các giá trị lẻ (như 1) để biểu thị thành công và các giá trị chẵn (như 0) để biểu thị sự thất bại. Thật không may, tiêu chuẩn ANSI C ban đầu đã được giải thích có nghĩa là EXIT_SUCCESS phải là 0, do đó, việc trả lại EXIT_SUCCESS từ chính có hành vi sai trên VMS. Điều di động để làm cho VMS là sử dụng exit(EXIT_SUCCESS), điều này luôn luôn làm đúng.
Adrian McCarthy

1
5.1.2.2.3 "Nếu kiểu trả về của hàm chính là loại tương thích với int, thì việc trả về từ cuộc gọi ban đầu đến hàm chính tương đương với việc gọi hàm thoát với giá trị được trả về bởi hàm chính làm đối số của nó; 11) đạt đến} kết thúc hàm chính trả về giá trị 0. "
Lundin

38

Lưu ý rằng các tiêu chuẩn C và C ++ xác định hai loại triển khai: tự do và lưu trữ.

  • Môi trường lưu trữ C90

    Các hình thức được phép 1 :

    int main (void)
    int main (int argc, char *argv[])
    
    main (void)
    main (int argc, char *argv[])
    /*... etc, similar forms with implicit int */

    Bình luận:

    Hai cái trước được tuyên bố rõ ràng là các hình thức được phép, những cái còn lại được cho phép hoàn toàn vì C90 cho phép "ẩn int" cho kiểu trả về và tham số hàm. Không có hình thức khác được cho phép.

  • Môi trường tự do C90

    Bất kỳ hình thức hoặc tên của chính được cho phép 2 .

  • Môi trường lưu trữ C99

    Hình thức cho phép 3 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */

    Bình luận:

    C99 đã xóa "ẩn int" vì vậy main()không còn hiệu lực.

    Một câu lạ, mơ hồ "hoặc trong một số cách xác định thực hiện khác" đã được giới thiệu. Điều này có thể được hiểu là "các tham số int main()có thể thay đổi" hoặc là "chính có thể có bất kỳ hình thức xác định thực hiện nào".

    Một số trình biên dịch đã chọn giải thích tiêu chuẩn theo cách sau. Có thể cho rằng, người ta không thể dễ dàng tuyên bố rằng họ không tuân thủ nghiêm ngặt bằng cách tự trích dẫn tiêu chuẩn, vì nó không rõ ràng.

    Tuy nhiên, để cho phép các hình thức hoàn toàn hoang dã main()có lẽ (?) Không phải là ý định của câu mới này. Cơ sở lý luận C99 (không quy phạm) ngụ ý rằng câu đề cập đến các tham số bổ sung cho int main 4 .

    Tuy nhiên, phần chấm dứt chương trình môi trường được lưu trữ sau đó tiếp tục tranh luận về trường hợp chính không trả về int 5 . Mặc dù phần đó không quy định về cách khai báo chính, nhưng nó chắc chắn ngụ ý rằng chính có thể được khai báo theo cách hoàn toàn xác định thực hiện ngay cả trên các hệ thống được lưu trữ.

  • Môi trường tự do C99

    Bất kỳ hình thức hoặc tên của chính được cho phép 6 .

  • Môi trường lưu trữ C11

    Hình thức cho phép 7 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */
  • Môi trường tự do C11

    Bất kỳ hình thức hoặc tên của chính được cho phép 8 .


Lưu ý rằng int main()không bao giờ được liệt kê là một hình thức hợp lệ cho bất kỳ triển khai C được lưu trữ trong bất kỳ phiên bản nào ở trên. Trong C, không giống như C ++, ()(void)có ý nghĩa khác nhau. Cái trước là một tính năng lỗi thời có thể được loại bỏ khỏi ngôn ngữ. Xem hướng dẫn ngôn ngữ trong tương lai của C11:

6.11.6 Bộ khai báo hàm

Việc sử dụng các bộ khai báo hàm với dấu ngoặc rỗng (không phải là bộ khai báo kiểu tham số định dạng nguyên mẫu) là một tính năng lỗi thời.


  • Môi trường lưu trữ C ++ 03

    Các hình thức được phép 9 :

    int main ()
    int main (int argc, char *argv[])

    Bình luận:

    Lưu ý dấu ngoặc đơn ở dạng đầu tiên. C ++ và C khác nhau trong trường hợp này, bởi vì trong C ++, điều này có nghĩa là hàm không có tham số. Nhưng trong C có nghĩa là nó có thể lấy bất kỳ tham số nào.

  • Môi trường tự do C ++ 03

    Tên của hàm được gọi khi khởi động được xác định theo thực hiện. Nếu được đặt tên, main()nó phải tuân theo các mẫu đã nêu 10 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])
  • Môi trường lưu trữ C ++ 11

    Biểu mẫu được phép 11 :

    int main ()
    int main (int argc, char *argv[])

    Bình luận:

    Văn bản của tiêu chuẩn đã được thay đổi nhưng nó có cùng ý nghĩa.

  • Môi trường tự do C ++ 11

    Tên của hàm được gọi khi khởi động được xác định theo thực hiện. Nếu được đặt tên, main()nó phải tuân theo các mẫu đã nêu 12 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])

Người giới thiệu

  1. ANSI X3.159-1989 2.1.2.2 Môi trường được lưu trữ. "Chương trình khởi động"

    Hàm được gọi khi khởi động chương trình được đặt tên chính. Việc thực hiện tuyên bố không có nguyên mẫu cho chức năng này. Nó sẽ được định nghĩa với kiểu trả về của int và không có tham số:

    int main(void) { /* ... */ } 

    hoặc với hai tham số (được gọi ở đây là argc và argv, mặc dù có thể sử dụng bất kỳ tên nào, vì chúng là cục bộ của hàm mà chúng được khai báo):

    int main(int argc, char *argv[]) { /* ... */ }
  2. ANSI X3.159-1989 2.1.2.1 Môi trường tự do:

    Trong một môi trường tự do (trong đó thực thi chương trình C có thể diễn ra mà không có bất kỳ lợi ích nào của hệ điều hành), tên và loại chức năng được gọi khi khởi động chương trình được xác định theo thực hiện.

  3. ISO 9899: 1999 5.1.2.2 Môi trường được lưu trữ -> 5.1.2.2.1 Khởi động chương trình

    Hàm được gọi khi khởi động chương trình được đặt tên chính. Việc thực hiện tuyên bố không có nguyên mẫu cho chức năng này. Nó sẽ được định nghĩa với kiểu trả về của int và không có tham số:

    int main(void) { /* ... */ } 

    hoặc với hai tham số (được gọi ở đây là argc và argv, mặc dù có thể sử dụng bất kỳ tên nào, vì chúng là cục bộ của hàm mà chúng được khai báo):

    int main(int argc, char *argv[]) { /* ... */ }

    hoặc tương đương; 9) hoặc theo một số cách xác định thực hiện khác.

  4. Cơ sở lý luận cho tiêu chuẩn quốc tế - Ngôn ngữ lập trình - C, Sửa đổi 5.10. 5.1.2.2 Môi trường được lưu trữ -> 5.1.2.2.1 Khởi động chương trình

    Hành vi của các đối số thành chính và tương tác của lối ra, chính và không chính xác (xem §7.20.4.2) đã được mã hóa để hạn chế một số loại không mong muốn trong biểu diễn các chuỗi argv và theo nghĩa của các giá trị được trả về bởi chính.

    Các đặc điểm kỹ thuật của argc và argv là đối số chính nhận ra thực tiễn mở rộng trước đó. argv [argc] được yêu cầu là một con trỏ null để cung cấp kiểm tra dự phòng cho phần cuối của danh sách, cũng trên cơ sở thực tiễn chung.

    hàm chính là hàm duy nhất có thể được khai báo bằng 0 hoặc hai đối số. (Số lượng đối số của các hàm khác phải khớp chính xác giữa cách gọi và định nghĩa.) Trường hợp đặc biệt này chỉ đơn giản nhận ra cách thực hành rộng rãi của việc loại bỏ các đối số thành chính khi chương trình không truy cập vào chuỗi đối số của chương trình. Trong khi nhiều triển khai hỗ trợ nhiều hơn hai đối số cho chính, thực tế như vậy không được ban phước hay cấm bởi Tiêu chuẩn; một chương trình xác định chính với ba đối số không tuân thủ nghiêm ngặt (xem §J.5.1.).

  5. ISO 9899: 1999 5.1.2.2 Môi trường được lưu trữ -> 5.1.2.2.3 Kết thúc chương trình

    Nếu kiểu trả về của hàm chính là loại tương thích với int, thì việc trả về từ lệnh gọi ban đầu đến hàm chính tương đương với việc gọi hàm thoát với giá trị được trả về bởi hàm chính làm đối số của nó; 11) đạt đến }kết thúc đó hàm chính trả về giá trị bằng 0. Nếu kiểu trả về không tương thích với int, trạng thái kết thúc được trả về môi trường máy chủ là không xác định.

  6. ISO 9899: 1999 5.1.2.1 Môi trường tự do

    Trong một môi trường tự do (trong đó thực thi chương trình C có thể diễn ra mà không có bất kỳ lợi ích nào của hệ điều hành), tên và loại chức năng được gọi khi khởi động chương trình được xác định theo thực hiện.

  7. ISO 9899: 2011 5.1.2.2 Môi trường được lưu trữ -> 5.1.2.2.1 Khởi động chương trình

    Phần này giống hệt với C99 được trích dẫn ở trên.

  8. ISO 9899: 1999 5.1.2.1 Môi trường tự do

    Phần này giống hệt với C99 được trích dẫn ở trên.

  9. ISO 14882: 2003 3.6.1 Chức năng chính

    Việc thực hiện sẽ không xác định trước chức năng chính. Chức năng này sẽ không bị quá tải. Nó sẽ có kiểu trả về kiểu int, nhưng nếu không thì kiểu của nó được xác định theo kiểu triển khai. Tất cả các triển khai sẽ cho phép cả hai định nghĩa chính sau đây:

    int main() { /* ... */ }

    int main(int argc, char* argv[]) { /* ... */ }
  10. ISO 14882: 2003 3.6.1 Chức năng chính

    Nó được định nghĩa triển khai cho dù một chương trình trong môi trường tự do được yêu cầu để xác định một chức năng chính.

  11. ISO 14882: 2011 3.6.1 Chức năng chính

    Việc thực hiện sẽ không xác định trước chức năng chính. Chức năng này sẽ không bị quá tải. Nó sẽ có kiểu trả về kiểu int, nhưng nếu không thì kiểu của nó được xác định theo kiểu triển khai. Tất cả các thực hiện sẽ cho phép cả hai

    - một hàm của () return int và

    - một hàm của (int, con trỏ tới con trỏ tới char) return int

    là loại chính (8.3.5).

  12. ISO 14882: 2011 3.6.1 Chức năng chính

    Phần này giống hệt với C ++ 03 được trích dẫn ở trên.


Một câu hỏi: Các tiêu chuẩn C ++ có nghĩa là chữ ký của chức năng khởi động trong các môi trường tự do cũng được xác định không? Ví dụ, một triển khai có thể đã định nghĩa hàm khởi động là: int my_startup_function ()hoặc int my_startup_function (int argc, char *argv[]), ví dụ: nó có thể có char my_startup_function (long argc, int *argv[])chức năng khởi động không? Tôi đoán là không, phải không? Ngoài ra, không phải là mơ hồ là tốt?
Utku

@Utku Nó có thể có bất kỳ chữ ký nào, miễn là nó không được đặt tên main()bởi vì sau đó nó phải sử dụng một chữ ký được liệt kê. Tôi sẽ tưởng tượng cái phổ biến nhất sẽ là void my_startup_function (), vì nó không có ý nghĩa để trở về từ chương trình trên các hệ thống tự do.
Lundin

1
Tôi hiểu rồi. Nhưng nếu nó được phép sử dụng bất kỳ tên và bất kỳ chữ ký nào cho chức năng khởi động, tại sao không cho phép sử dụng một chữ ký khác cho main? Xin lỗi nếu đó không phải là một câu hỏi thông minh nhưng tôi không thể hiểu lý do đằng sau.
Utku

@Utku C và C ++ khác nhau ở đó. Về lý do tại sao C ++ thực thi điều này, tôi không biết, không có lý do. Tôi nghi ngờ thủ phạm chính (ý định chơi chữ) là Stroustrup , người đã sớm tuyên bố rằng chính phải trả lại int, kỳ. Bởi vì khi anh ta tạo phiên bản C ++ đầu tiên, anh ta chỉ được sử dụng cho các hệ thống được lưu trữ. Trong bài liên kết, Stroustrup vẫn dường như không biết gì về sự tồn tại của freestanding triển khai: ví dụ, ông được vì không biết gì đề cập đến tổ chức thực hiện phụ chương của chuẩn C, bỏ qua sự tồn tại của chương 5.1.2.1.
Lundin

1
Điều đáng chú ý về dự thảo tiêu chuẩn C11 là mặc dù func()được coi là lỗi thời, bản thân dự thảo sử dụng int main()trong các ví dụ của chính nó.
Antti Haapala

29

Trả về 0 khi thành công và khác không cho lỗi. Đây là tiêu chuẩn được sử dụng bởi kịch bản UNIX và DOS để tìm hiểu điều gì đã xảy ra với chương trình của bạn.


8

main() trong C89 và K & R C các kiểu trả về không xác định mặc định là 'int`.

return 1? return 0?
  1. Nếu bạn không viết câu lệnh return int main(), đóng {sẽ trả về 0 theo mặc định.

  2. return 0hoặc return 1sẽ được nhận bởi quá trình cha mẹ. Trong một shell nó đi vào một biến shell và nếu bạn đang chạy chương trình của mình tạo thành một shell và không sử dụng biến đó thì bạn không cần phải lo lắng về giá trị trả về của main().

Xem Làm thế nào tôi có thể nhận được những gì chức năng chính của tôi đã trở lại? .

$ ./a.out
$ echo $?

Bằng cách này bạn có thể thấy rằng đó là biến $?nhận byte nhỏ nhất có giá trị trả về main().

Trong kịch bản Unix và DOS, lỗi return 0thành công và khác không thường được trả về. Đây là tiêu chuẩn được sử dụng bởi kịch bản Unix và DOS để tìm hiểu điều gì đã xảy ra với chương trình của bạn và kiểm soát toàn bộ luồng.


4
Nói đúng ra, $?không phải là một biến môi trường; nó là một biến được xác định trước (hoặc tích hợp sẵn). Sự khác biệt là khó phát hiện, nhưng nếu bạn chạy env(không có bất kỳ đối số nào), nó sẽ in ra môi trường và $?sẽ không được hiển thị trong môi trường.
Jonathan Leffler

1
Tự động trở về 0 khi "điểm rơi chính" chỉ có trong C ++ và C99 trở đi, không phải trong C90.
Kaz

Typo: "đóng cửa {" nên được }. SO sẽ không cho phép tôi thực hiện một chỉnh sửa nhỏ này.
Spencer

7

Hãy nhớ rằng, mặc dù bạn đang trả về một int, một số HĐH (Windows) cắt ngắn giá trị được trả về thành một byte đơn (0-255).


4
Unix cũng làm như vậy, giống như hầu hết các hệ điều hành khác. Tôi biết VMS thực hiện những điều kỳ lạ đáng kinh ngạc như vậy với nó khi trả lại bất cứ thứ gì ngoài EXIT_SUCCESS hoặc EXIT_FAILURE đang yêu cầu sự cố.
Leon Timmermans

2
MSDN cầu xin khác nhau: khi được báo cáo qua mscorlib, mã thoát là số nguyên 32 bit đã ký . Điều này dường như ngụ ý rằng các thư viện thời gian chạy C cắt ngắn mã thoát bị lỗi.

Vâng, điều này là không chính xác. Trên Windows, một số nguyên 32 bit được trả về (và được chuyển đổi thành unsigned). Điều này cũng tương tự trên các hệ thống UNIX với số nguyên 32 bit. Nhưng các shell kiểu UNIX trên một trong hai hệ thống thường sẽ chỉ giữ lại một số nguyên 8 bit không dấu.
John McFarlane

4

Giá trị trả về có thể được sử dụng bởi hệ điều hành để kiểm tra cách chương trình được đóng.

Trả về giá trị 0 thường có nghĩa là OK trong hầu hết các hệ điều hành (những cái tôi có thể nghĩ ra bằng mọi cách).

Nó cũng có thể được kiểm tra khi bạn tự gọi một quy trình và xem chương trình đã thoát và kết thúc đúng chưa.

KHÔNG chỉ là một quy ước lập trình.


Không có gì trong câu hỏi chỉ ra rằng một hệ thống phẫu thuật có mặt. Trả lại một giá trị không có ý nghĩa gì trong một hệ thống tự do.
Lundin

3

Giá trị trả về của main()chương trình đã thoát. Nếu giá trị trả về là zeronó có nghĩa là việc thực thi đã thành công trong khi bất kỳ giá trị khác không nào sẽ biểu thị rằng có điều gì đó không ổn trong thực thi.


1
Đây là một nhận xét không phải là một câu trả lời cho câu hỏi.
Lundin

2

Tôi có ấn tượng rằng tiêu chuẩn chỉ định rằng chính không cần giá trị trả về vì lợi nhuận thành công là dựa trên hệ điều hành (số không có thể là thành công hoặc thất bại ở người khác), do đó, việc không trả lại là một dấu hiệu cho trình biên dịch để chèn trả về thành công chính nó.

Tuy nhiên tôi thường trả về 0.


C99 (và C ++ 98) cho phép bạn bỏ qua câu lệnh return từ hàm chính; C89 không cho phép bạn bỏ qua câu lệnh return.
Jonathan Leffler

Đây là một nhận xét không phải là một câu trả lời.
Lundin

Điều này không cung cấp một câu trả lời cho câu hỏi. Để phê bình hoặc yêu cầu làm rõ từ một tác giả, hãy để lại nhận xét bên dưới bài đăng của họ.
Steve Lillis

6
@SteveLillis: Năm 2008 SO không có phần bình luận.
graham.reeds

2

Trở về 0 nên nói với lập trình viên rằng chương trình đã hoàn thành công việc.


Trả về 1 từ main()thông thường báo hiệu một lỗi xảy ra; Trả về 0 tín hiệu thành công. Nếu các chương trình của bạn luôn thất bại, thì 1 là OK, nhưng đó không phải là ý tưởng tốt nhất.
Jonathan Leffler

1
@JonathanLeffler: Ý nghĩa của trở về 1từ mainlà thực hiện xác định. Các giá trị được xác định bằng ngôn ngữ là 0, EXIT_SUCCESS(thường được xác định là 0) và EXIT_FAILURE. Trong OpenVMS, return 1;biểu thị chấm dứt thành công .
Keith Thompson

VMS không phải là "bình thường" - theo nghĩa của những gì tôi đã nói. Nó không phải là một cái gì đó giống như 'bất kỳ giá trị kỳ lạ là thành công; thậm chí giá trị là thất bại 'trên VMS?
Jonathan Leffler

2

Bỏ sót return 0

Khi một chương trình C hoặc C ++ đến cuối maintrình biên dịch sẽ tự động tạo mã để trả về 0, do đó không cần phải đặt return 0;rõ ràng vào cuối main.

Lưu ý: khi tôi đưa ra đề xuất này, nó hầu như luôn luôn được theo sau bởi một trong hai loại nhận xét: "Tôi không biết điều đó". hoặc "Đó là lời khuyên tồi!" Lý do của tôi là nó an toàn và hữu ích khi dựa vào hành vi của trình biên dịch được hỗ trợ rõ ràng bởi tiêu chuẩn. Đối với C, kể từ C99; xem ISO / IEC 9899: 1999 phần 5.1.2.2.3:

[...] Trả về từ lệnh gọi ban đầu đến mainhàm tương đương với gọi exithàm với giá trị được mainhàm trả về làm đối số của nó; đạt đến mức }kết thúc mainhàm trả về giá trị 0.

Đối với C ++, kể từ tiêu chuẩn đầu tiên vào năm 1998; xem ISO / IEC 14882: 1998 phần 3.6.1:

Nếu điều khiển đạt đến cuối của main mà không gặp phải câu lệnh return, thì hiệu quả là việc thực hiện return 0;

Tất cả các phiên bản của cả hai tiêu chuẩn kể từ đó (C99 và C ++ 98) đều duy trì cùng một ý tưởng. Chúng tôi dựa vào các hàm thành viên được tạo tự động trong C ++ và rất ít người viết các return;câu lệnh rõ ràng ở cuối voidhàm. Những lý do chống lại việc bỏ qua dường như sôi lên vì "nó trông thật kỳ lạ" . Nếu, giống như tôi, bạn tò mò về lý do thay đổi tiêu chuẩn C, hãy đọc câu hỏi này . Cũng lưu ý rằng vào đầu những năm 1990, điều này được coi là "thực hành cẩu thả" bởi vì đó là hành vi không xác định (mặc dù được hỗ trợ rộng rãi) tại thời điểm đó.

Ngoài ra, Nguyên tắc cốt lõi C ++ chứa nhiều trường hợp bỏ qua return 0;ở cuối mainvà không có trường hợp nào trả lại rõ ràng được viết. Mặc dù chưa có một hướng dẫn cụ thể về chủ đề cụ thể này trong tài liệu đó, nhưng dường như ít nhất đó là một sự chứng thực ngầm của thực tiễn.

Vì vậy, tôi ủng hộ bỏ qua nó; những người khác không đồng ý (thường kịch liệt!) Trong mọi trường hợp, nếu bạn gặp phải mã bỏ qua nó, bạn sẽ biết rằng nó được hỗ trợ rõ ràng bởi tiêu chuẩn và bạn sẽ biết ý nghĩa của nó.


2
Đây là lời khuyên tồi vì các trình biên dịch chỉ thực hiện C89, không phải bất kỳ tiêu chuẩn nào sau này, vẫn cực kỳ phổ biến (tôi viết điều này vào năm 2017) và sẽ vẫn cực kỳ phổ biến trong tương lai gần. Chẳng hạn, lần trước tôi đã kiểm tra không có phiên bản trình biên dịch nào của Microsoft triển khai C99 và tôi hiểu rằng đây vẫn là điển hình cho các trình biên dịch hệ thống nhúng không phải là GCC.
zwol

4
@zwol: Bất cứ ai không có lựa chọn nào khác ngoài việc sử dụng trình biên dịch đã hết hạn 28 năm có lẽ có nhiều vấn đề hơn là quyết định có bao gồm rõ ràng hay không return 0;, tuy nhiên tôi sẽ lưu ý rằng nhiều trình biên dịch thời đó cũng đã thực hiện một ẩn ý return 0;ngay cả trước khi nó chuẩn hóa.
Edward

2
Trên thực tế, tôi làm rất nhiều hệ thống nhúng hoạt động và không gặp phải trình biên dịch không hỗ trợ ngầm return 0trong hơn một thập kỷ. Ngoài ra các phiên bản hiện tại của Microsoft C cũng hỗ trợ nó . Có lẽ thông tin của bạn đã hết hạn?
Edward

2
Tôi có thể đánh giá cao điều này đang gây tranh cãi trong C, gần như (per @zwol). Trong C ++, mọi tranh cãi xung quanh điều này là vô nghĩa.
Các cuộc đua nhẹ nhàng trong quỹ đạo

2
@Edward Tôi không nói rằng cuộc tranh cãi đã không tồn tại, tôi đã nói điều đó là vô nghĩa: P
Các cuộc đua nhẹ nhàng trong quỹ đạo

1

Những gì để trở về phụ thuộc vào những gì bạn muốn làm với thực thi. Ví dụ: nếu bạn đang sử dụng chương trình của mình với vỏ dòng lệnh, thì bạn cần trả về 0 cho thành công và không bằng 0 cho thất bại. Sau đó, bạn sẽ có thể sử dụng chương trình trong shell với xử lý có điều kiện tùy thuộc vào kết quả của mã của bạn. Ngoài ra, bạn có thể chỉ định bất kỳ giá trị khác nào theo cách hiểu của mình, ví dụ đối với các lỗi nghiêm trọng, các điểm thoát chương trình khác nhau có thể chấm dứt chương trình với các giá trị thoát khác nhau và có sẵn cho vỏ gọi có thể quyết định việc cần làm bằng cách kiểm tra giá trị được trả về. Nếu mã không có ý định sử dụng với shell và giá trị trả về không làm phiền bất cứ ai thì nó có thể bị bỏ qua. Cá nhân tôi sử dụng chữ kýint main (void) { .. return 0; .. }


Định dạng của hàm main () được xác định bởi trình thực hiện, nghĩa là trình biên dịch. Lập trình viên không được chọn chọn biểu mẫu nào, ngoại trừ khi trình biên dịch hỗ trợ một số biểu mẫu.
Lundin

@Lundin Loại trả về sẽ được thực hiện bằng cách thực hiện. Nhưng giá trị được trả lại là do người lập trình quyết định. C99 Mục 5.1.2.2.3 đề cập rằng loại trả về maintương thích với int. Do đó, việc trở lại intsẽ không thành vấn đề. Mặc dù các kiểu trả về khác được cho phép, nhưng trong trường hợp đó, biến môi trường có giá trị trả về sẽ không được chỉ định. Nhưng nếu một lập trình viên làm return 0;thì trong bash, nó có thể được sử dụng để tạo các nhánh.
phoxis

1

Nếu bạn thực sự có vấn đề liên quan đến hiệu quả của việc trả về một số nguyên từ một quy trình, có lẽ bạn nên tránh gọi quy trình đó nhiều lần để giá trị trả về này trở thành một vấn đề.

Nếu bạn đang làm điều này (gọi một quá trình nhiều lần), bạn nên tìm cách đưa logic của mình trực tiếp vào bên trong người gọi hoặc trong tệp DLL mà không cần phân bổ một quy trình cụ thể cho mỗi cuộc gọi; phân bổ nhiều quá trình mang lại cho bạn vấn đề hiệu quả có liên quan trong trường hợp này.

Cụ thể, nếu bạn chỉ muốn biết nếu trả về 0 hiệu quả hơn hoặc ít hơn trả về 1, thì nó có thể phụ thuộc vào trình biên dịch trong một số trường hợp, nhưng nói chung, giả sử chúng được đọc từ cùng một nguồn (cục bộ, trường, hằng, nhúng trong mã, kết quả chức năng, v.v.) nó yêu cầu chính xác cùng một số chu kỳ đồng hồ.


1

Đây là một minh chứng nhỏ về việc sử dụng mã trả lại ...

Khi sử dụng các công cụ khác nhau mà thiết bị đầu cuối Linux cung cấp, người ta có thể sử dụng mã trả về để xử lý lỗi sau khi quá trình hoàn tất. Hãy tưởng tượng rằng tệp văn bản sau đây myfile có mặt:

Đây là một số ví dụ để kiểm tra cách grep hoạt động.

Khi bạn thực thi lệnh grep, một quy trình được tạo. Khi đã xong (và không bị hỏng), nó sẽ trả về một số mã trong khoảng từ 0 đến 255. Ví dụ:

$ grep order myfile

Nếu bạn làm

$ echo $?
$ 0

bạn sẽ nhận được 0. Tại sao? Bởi vì grep đã tìm thấy kết quả khớp và trả về mã thoát 0, đây là giá trị thông thường để thoát với thành công. Chúng ta hãy kiểm tra lại nhưng với thứ gì đó không có trong tệp văn bản của chúng tôi và do đó sẽ không tìm thấy kết quả khớp nào:

$ grep foo myfile
$ echo $?
$ 1

Vì grep không khớp mã thông báo "foo" với nội dung tệp của chúng tôi, mã trả về là 1 (đây là trường hợp thông thường khi xảy ra lỗi nhưng như đã nêu ở trên, bạn có nhiều giá trị để chọn).

Bây giờ tập lệnh bash sau (chỉ cần nhập nó vào một thiết bị đầu cuối Linux) mặc dù rất cơ bản sẽ đưa ra một số ý tưởng về xử lý lỗi:

$ grep foo myfile
$ CHECK=$?
$ [ $CHECK -eq 0] && echo 'Match found'
$ [ $CHECK -ne 0] && echo 'No match was found'
$ No match was found

Sau dòng thứ hai, không có gì được in đến thiết bị đầu cuối vì "foo" đã tạo grep return 1 và chúng tôi kiểm tra xem mã trả về của grep có bằng 0. Câu lệnh điều kiện thứ hai lặp lại thông điệp của nó ở dòng cuối cùng vì nó đúng do CHECK == 1.

Như bạn có thể thấy nếu bạn đang gọi điều này và quá trình đó đôi khi rất cần thiết để xem những gì nó đã trả về (bằng giá trị trả về của hàm main ()).


Trong tập lệnh shell, bạn sẽ sử dụng if grep foo myfile; then echo 'Match found'; else echo 'No match was found'; fi- kiểm tra trạng thái trả về trực tiếp. Nếu bạn muốn nắm bắt trạng thái (để báo cáo, v.v.), thì bạn sử dụng một bài tập. Bạn có thể sử dụng if grep foo myfile; CHECK=$?; [ "$CHECK" = 0 ]; then echo 'Match found'; else echo 'No match was found'; fihoặc bạn có thể sử dụng ba dòng. Bạn cũng có thể sử dụng tùy chọn -s-qđể grepngăn chặn các trận đấu hoặc thông báo lỗi thường xuyên xuất hiện. Tuy nhiên, đây là shell minutiae - điểm chính, rằng trạng thái thoát có thể hữu ích - là OK.
Jonathan Leffler

1

Cách chính xác (hiệu quả nhất) để xác định hàm main () trong C và C ++ - int main () hoặc void main () - và tại sao?

Những từ "(hiệu quả nhất)" không thay đổi câu hỏi. Trừ khi bạn ở trong một môi trường tự do, có một cách chính xác để khai báo main(), và đó là trả về int.

Điều gì sẽ main()trở lại trong C và C ++?

Đó không phải là những gì nên main() trở lại, đó là những gì làm main() trở lại. main()tất nhiên là một chức năng mà người khác gọi. Bạn không có bất kỳ quyền kiểm soát nào đối với mã gọi main(). Do đó, bạn phải khai báo main()bằng chữ ký đúng loại để khớp với người gọi của nó. Bạn chỉ đơn giản là không có bất kỳ sự lựa chọn trong vấn đề. Bạn không cần phải tự hỏi mình cái gì hiệu quả hơn hay kém hơn, phong cách tốt hơn hay xấu hơn, hay bất cứ điều gì tương tự, bởi vì câu trả lời đã được xác định rõ ràng, theo tiêu chuẩn C và C +. Chỉ cần làm theo họ.

Nếu int main () thì trả về 1 hoặc trả về 0?

0 cho thành công, khác không cho thất bại. Một lần nữa, không phải thứ bạn cần (hoặc nhận) chọn: nó được xác định bởi giao diện mà bạn phải tuân thủ.

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.