Khai báo chính xác là gì?


147

Chữ ký thích hợp của mainhàm trong C ++ là gì? Loại trả về chính xác là gì và trả về một giá trị có nghĩa là maingì? Các loại tham số được phép là gì, và ý nghĩa của chúng là gì?

Đây có phải là hệ thống cụ thể? Những quy tắc đó đã thay đổi theo thời gian? Điều gì xảy ra nếu tôi vi phạm chúng?


1
Điều này liên quan rất chặt chẽ với, hoặc một bản sao của, Điều gì sẽ maintrả về trong C và C ++ .
Jonathan Leffler

@JonathanLeffler Không đùa đâu ... nó đã được thêm vào danh sách các bản sao trong phiên bản 6 khoảng 8 tháng trước.
dòng chảy

Câu trả lời:


192

Các mainchức năng phải được khai báo là một hàm không thành viên trong không gian tên toàn cầu. Điều này có nghĩa là nó không thể là hàm thành viên tĩnh hoặc không tĩnh của một lớp, cũng không thể đặt nó trong một không gian tên (ngay cả không gian tên không tên).

Tên mainkhông được dành riêng trong C ++ ngoại trừ như một chức năng trong không gian tên toàn cầu. Bạn có thể tự do khai báo các thực thể khác có tên main, bao gồm trong số những thứ khác, các lớp, biến, liệt kê, hàm thành viên và các hàm không phải thành viên không có trong không gian tên toàn cục.

Bạn có thể khai báo một hàm có tên mainlà một hàm thành viên hoặc trong một không gian tên, nhưng một hàm như vậy sẽ không phải là mainhàm chỉ định nơi chương trình bắt đầu.

Các mainchức năng không thể được khai báo là statichay inline. Nó cũng không thể bị quá tải; chỉ có thể có một hàm được đặt tên maintrong không gian tên toàn cục.

Các mainchức năng không thể được sử dụng trong chương trình của bạn: bạn không được phép để gọi mainhàm từ bất cứ nơi nào trong mã của bạn, cũng không phải bạn được phép lấy địa chỉ của nó.

Các loại trả lại mainphải đượcint . Không có loại trả về nào khác được cho phép (quy tắc này được in đậm vì rất thường thấy các chương trình không chính xác khai báo mainvới loại trả về void; đây có thể là quy tắc bị vi phạm thường xuyên nhất liên quan đến mainchức năng).

Có hai tuyên bố mainđó phải được cho phép:

int main()               // (1)
int main(int, char*[])   // (2)

Trong (1) , không có tham số.

Trong (2) , có hai tham số và chúng được đặt tên theo quy ước argcargv, tương ứng. argvlà một con trỏ tới một chuỗi các chuỗi C đại diện cho các đối số cho chương trình. argclà số lượng đối số trong argvmảng.

Thông thường, argv[0]chứa tên của chương trình, nhưng điều này không phải lúc nào cũng đúng. argv[argc]được đảm bảo là một con trỏ null.

Lưu ý rằng vì một đối số kiểu mảng (như char*[]) thực sự chỉ là một đối số kiểu con trỏ được ngụy trang, hai cách sau đây là cả hai cách hợp lệ để viết (2) và cả hai đều có nghĩa chính xác giống nhau:

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

Một số triển khai có thể cho phép các loại và số lượng tham số khác; bạn phải kiểm tra tài liệu triển khai của mình để xem những gì nó hỗ trợ.

main()dự kiến ​​sẽ trả về 0 để biểu thị thành công và khác không để biểu thị sự thất bại. Bạn không bắt buộc phải viết một returntuyên bố rõ ràng main(): nếu bạn main()trả lại mà không có một returntuyên bố rõ ràng , nó giống như khi bạn đã viết return 0;. Hai main()chức năng sau có cùng hành vi:

int main() { }
int main() { return 0; }

Có hai macro, EXIT_SUCCESSEXIT_FAILURE, được xác định trong <cstdlib>đó cũng có thể được trả về main()để biểu thị thành công và thất bại, tương ứng.

Giá trị được trả về main()được truyền cho exit()hàm, kết thúc chương trình.

Lưu ý rằng tất cả những điều này chỉ áp dụng khi biên dịch cho một môi trường được lưu trữ (không chính thức, một môi trường nơi bạn có một thư viện tiêu chuẩn đầy đủ và có một hệ điều hành chạy chương trình của bạn). Cũng có thể biên dịch chương trình C ++ cho môi trường tự do (ví dụ: một số loại hệ thống nhúng), trong đó trường hợp khởi động và chấm dứt được xác định hoàn toàn do triển khai và một main()chức năng thậm chí không được yêu cầu. Tuy nhiên, nếu bạn đang viết C ++ cho một hệ điều hành máy tính để bàn hiện đại, thì bạn đang biên dịch cho một môi trường được lưu trữ.


1
IIRC các giá trị trả về được bảo đảm duy nhất là 0, EXIT_SUCCESS (cùng hiệu ứng với 0) và EXIT_FAILURE. EDIT: Ah, OK, các giá trị trạng thái khác không khác có thể được trả về, nhưng với ý nghĩa do định nghĩa thực hiện. Chỉ EXIT_FAILURE được đảm bảo được hiểu theo một cách nào đó là giá trị thất bại.
Derrick Turk

4
@Synetech: Câu hỏi đặt ra trong câu đầu tiên, "Chữ ký thích hợp của hàm chính trong C ++ là gì?" và câu hỏi được gắn thẻ cả [c ++] và [c ++ - faq]. Tôi không thể giúp nó nếu người dùng Java hoặc C # (hoặc bất kỳ ai khác) vẫn còn bối rối. C # yêu cầu Mainphải là một hàm thành viên tĩnh bởi vì nó thậm chí không có các hàm không phải là tháng. Ngay cả C89 yêu cầu mainphải trả lại int. Tôi không đủ quen thuộc với K & R C để biết các quy tắc chính xác của nó, nhưng tôi đoán nó cũng yêu cầu mainphải trả về intmainkhông có loại trả lại nào là phổ biến và không có loại = ẩn inttrong K & R.
James McNellis

3
@Suhail: Bởi vì tiêu chuẩn ngôn ngữ nói loại trả về sẽ là int.
James McNellis

1
@Suhail: Vâng. Mã của bạn sẽ không đúng C ++ và nhiều trình biên dịch sẽ từ chối mã của bạn.
James McNellis

2
@Suhail: Visual C ++ cho phép voidloại trả về dưới dạng phần mở rộng ngôn ngữ . Trình biên dịch không cho phép nó bao gồm GCC và Comeau.
James McNellis

15

Từ tài liệu tiêu chuẩn., 3.6.1.2 Chức năng chính ,

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[]) { / ... / }

Ở dạng sau argcsẽ là số lượng các đố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 trong argv [0] thông qua argv [argc-1] làm con trỏ cho ban đầu các ký tự của chuỗi đa chuỗi kết thúc null .....

Mong rằng sẽ giúp ..


2
Có bất kỳ lý do cụ thể như tại sao loại trả lại mainnên được int?
Suhail Gupta

1
@SuhailGupta: Vì vậy mà quá trình gọi biết liệu này trình nên được coi là thành công hay không. Cho phép voidphá vỡ mô hình đó. Nó thậm chí không thực sự có ý nghĩa nếu bạn có nghĩa là "luôn luôn coi là thành công". Bởi vì bạn không có cách nào để nói nếu quá trình thực sự thất bại, vậy bạn đã thực sự thành công chưa? Không, trở về int.
Các cuộc đua nhẹ nhàng trong quỹ đạo

4

Từ ngữ chính xác của tiêu chuẩn được công bố mới nhất (C ++ 14) là:

Việc thực hiện sẽ cho phép cả hai

  • một chức năng ()trở lại int

  • một chức năng của (int, con trỏ đến con trỏ để char)trở vềint

như các loại main.

Điều này cho thấy rõ rằng các cách viết thay thế được cho phép miễn là loại đó mainlà loại int()hoặc int(int, char**). Vì vậy, những điều sau đây cũng được cho phép:

  • int main(void)
  • auto main() -> int
  • int main ( )
  • signed int main()
  • typedef char **a; typedef int b, e; e main(b d, a c)

1
Lưu ý Tôi đã đăng câu trả lời này như trong các bình luận cho một chủ đề khác, một người nào đó đã cố trích dẫn chủ đề này là bằng chứng int main(void)không chính xác trong C ++.
MM

3
@Starg Nghiệp auto main() -> intkhông có loại trả lại suy diễn. Hãy chú ý đến {in "(auto main () {... không được phép)" và vui lòng tìm hiểu khi bạn chưa biết đủ để thêm bất cứ điều gì có ý nghĩa.

3

Hai nguồn chính hợp lệ là int main()int main(int, char*[]). Bất cứ điều gì khác có thể hoặc không thể biên dịch. Nếu mainkhông trả về một giá trị rõ ràng, 0 được trả lại hoàn toàn.


1
Tôi chưa bao giờ thấy mã không được biên dịch khi tôi đề cập đến kiểu trả về của mainvoid. Có bất kỳ lý do cụ thể mà kiểu trả về của main nên là int không?
Suhail Gupta

4
Đặc tả ngôn ngữ nói chính phải có kiểu trả về int. Bất kỳ loại trả về nào khác được trình biên dịch của bạn cho phép là một cải tiến cụ thể của trình biên dịch. Về cơ bản sử dụng void có nghĩa là bạn đang lập trình bằng một ngôn ngữ tương tự nhưng không phải C ++.
ném đá

2
Lý do tiêu chuẩn yêu cầu intkiểu trả về mainlà giá trị này được trao cho shell dưới dạng mã thoát của chương trình và shmong đợi một int.
uckelman

Có lẽ lý do là kỷ luật? Có thể có nhiều hơn một con đường ra. Nếu loại trả về là voidtất cả họ im lặng. Với intchúng tôi phải xác định giá trị thoát cụ thể cho mỗi lần trả về main.
Andreas Spindler

2

Chi tiết về giá trị trả về và ý nghĩa của chúng

Mỗi 3.6.1 ( [basic.start.main]):

Một câu lệnh return in maincó tác dụng rời khỏi mainhàm (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 kiểm soát đến cuối mainmà không gặp phải một returntuyên bố, thì hiệu quả là việc thực thi

return 0;

Hành vi của std::exitđược chi tiết trong phần 18.5 ( [support.start.term]) và mô tả mã trạng thái:

Cuối cùng, kiểm soát được trả về môi trường máy chủ. Nếu trạng thái 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 trạng thái 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ả lại. Nếu không, trạng thái trả về là xác định thực hiện.

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.