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ề 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; 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
argc
sẽ là không âm.
argv[argc]
sẽ là một con trỏ rỗng.
- Nếu giá trị
argc
lớ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
argc
lớ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 argc
lớ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ố
argc
và argv
chuỗi được chỉ ra bởi argv
mả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 đó, int
có thể được thay thế bằng một tên typedef được định nghĩa là int
, hoặc loại argv
có 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 main
hàm là loại tương thích với int
, thì việc trả về từ lệnh gọi ban đầu đến main
hàm tương đương với việc gọi exit
hàm với giá trị được main
hàm trả về làm đối số của nó; 11) đạt đến mức }
kết thúc
main
hà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 0
bắt buộc là "thành công". Bạn có thể sử dụng EXIT_FAILURE
và EXIT_SUCCESS
từ <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 exit
Hàm
¶5 Cuối cùng, điều khiển được trả về môi trường máy chủ. Nếu giá trị status
bằ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ị status
là EXIT_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() { /* ... */ }
và
int main(int argc, char* argv[]) { /* ... */ }
Ở dạng sau argc
sẽ 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 argc
khô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 argc
sẽ 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 main
sẽ 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::exit
vớ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 exit
chứ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_SUCCESS
và EXIT_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 main
và wmain
có thể được khai báo là trả về void
(không có giá trị trả về). Nếu bạn khai báo main
hoặc wmain
trả 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 main
hoặc wmain
được khai báo là void
, bạn phải sử dụng exit
hà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 **envp
con 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 main
và 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ữ và 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ữ và 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 và §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
và_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;
}