câu lệnh return vs exit () trong hàm main ()


197

Tôi nên sử dụng exit()hoặc chỉ returnbáo cáo trong main()? Cá nhân tôi ủng hộ các returncâu lệnh vì tôi cảm thấy nó giống như đọc bất kỳ chức năng nào khác và điều khiển luồng khi tôi đang đọc mã trơn tru (theo ý kiến ​​của tôi). Và ngay cả khi tôi muốn cấu trúc lại main()hàm, có returnvẻ như là một lựa chọn tốt hơn exit().

exit()làm gì đặc biệt returnkhông?

Câu trả lời:


277

Trên thực tế, có một sự khác biệt, nhưng đó là tinh tế. Nó có nhiều ý nghĩa hơn đối với C ++, nhưng sự khác biệt rất quan trọng.

Khi tôi gọi returntrong main(), hàm hủy sẽ được gọi cho các đối tượng tại địa phương scoped tôi. Nếu tôi gọi exit(), sẽ không có hàm hủy nào được gọi cho các đối tượng trong phạm vi cục bộ của tôi! Đọc lại mà. exit() không trở về . Điều đó có nghĩa là một khi tôi gọi nó, "không có hậu trường". Bất kỳ đối tượng nào bạn đã tạo trong chức năng đó sẽ không bị hủy. Thường thì điều này không có ý nghĩa gì, nhưng đôi khi nó cũng giống như đóng các tệp (chắc chắn bạn muốn tất cả dữ liệu của mình được xóa vào đĩa?).

Lưu ý rằng staticcác đối tượng sẽ được dọn sạch ngay cả khi bạn gọi exit(). Cuối cùng lưu ý rằng, nếu bạn sử dụng abort(), sẽ không có đối tượng nào bị phá hủy. Đó là, không có đối tượng toàn cầu, không có đối tượng tĩnh và không có đối tượng cục bộ sẽ có các hàm hủy của chúng được gọi.

Tiến hành thận trọng khi ủng hộ thoát hơn lợi nhuận.

http://groups.google.com/group/gnu.gcc.help/msg/8348c50030cfd15a


1
abort () thoát với điều kiện lỗi (mã thoát không bằng 0) và thậm chí có thể là lõi. Nếu bạn cần thoát w / o gọi hàm hủy tĩnh, hãy sử dụng _exit.

7
@Mike: có sự khác biệt giữa bộ đệm tệp thư viện C và các đối tượng luồng tệp C ++. exit () - là một phần của thư viện C - được thiết kế để phối hợp và xóa cái trước, nhưng có thể bỏ qua cái sau: ngay cả nội dung dòng tiêu chuẩn C ++ cũng không bị xóa vào đĩa (hãy thử - tôi đã làm, nó thất bại với Linux / GCC) và các loại rõ ràng do người dùng xác định đã nhập vào bộ đệm I / O không thể được mong đợi để xóa.
Tony Delroy

9
Lưu ý: Câu lệnh: không có hàm hủy sẽ được gọi cho các đối tượng trong phạm vi cục bộ của tôi! không còn đúng với C ++ 11: - Các đối tượng được liên kết với luồng hiện tại có thời gian lưu trữ luồng bị hủy (chỉ C ++ 11). cplusplus.com/reference/cstdlib/exit
Ilendir

7
Nó có nghĩa là, thread_localkẻ hủy diệt của các đối tượng sẽ được gọi. Phá hủy cho các đối tượng địa phương khác vẫn chưa được gọi. ideone.com/Y6Dh3f
HolyBlackCat

3
BTW, và chỉ mang tính mô phạm và bởi vì câu trả lời này vẫn có thể gây nhầm lẫn cho người đọc khi sử dụng C: Đối với C, vấn đề về việc exit()đóng tệp một cách sạch sẽ thực sự sai. Dữ liệu thời gian duy nhất có thể không bị xóa là trong trường hợp ngược lại: tức là nếu một người sử dụng returntừ main()và một người đã gọi setbuf()hoặc setvbuf()với một bộ đệm được khai báo là lưu trữ tự động trong main()(như được thảo luận trong câu trả lời của R. bên dưới). Thật tệ khi câu hỏi này được gắn thẻ bằng cả C và C ++ (và kiểu mã hóa - đó không phải là vấn đề về phong cách!).
Greg A. Woods

25

Một điểm khác biệt: exitlà chức năng Thư viện tiêu chuẩn, do đó bạn cần bao gồm các tiêu đề và liên kết với thư viện chuẩn. Để minh họa (trong C ++), đây là một chương trình hợp lệ:

int main() { return 0; }

nhưng để sử dụng exitbạn sẽ cần bao gồm:

#include <stdlib.h>
int main() { exit(EXIT_SUCCESS); }

Thêm vào đó, thêm một giả định bổ sung: việc gọi exittừ maincó tác dụng phụ tương tự như trả về số không. Như những người khác đã chỉ ra, điều này phụ thuộc vào loại thực thi mà bạn đang xây dựng (nghĩa là ai đang gọi main). Bạn đang mã hóa một ứng dụng sử dụng thời gian chạy C? Một plugin Maya? Một dịch vụ Windows? Một tài xế? Mỗi trường hợp sẽ yêu cầu nghiên cứu để xem nếu exittương đương với return. IMHO sử dụng exitkhi bạn thực sự có nghĩa là return chỉ làm cho mã khó hiểu hơn. OTOH, nếu bạn thực sự có ý nghĩa exit , thì bằng mọi cách hãy sử dụng nó.


16

Có ít nhất một lý do để ưu tiên exit: Nếu bất kỳ atexittrình xử lý nào của bạn đề cập đến dữ liệu thời lượng lưu trữ tự động mainhoặc nếu bạn đã sử dụng setvbufhoặc setbufgán cho một trong các luồng tiêu chuẩn, bộ đệm có thời lượng lưu trữ tự động main, sau đó quay lại từ mainsản xuất hành vi không xác định, nhưng gọi exitlà hợp lệ.

Tuy nhiên, một cách sử dụng tiềm năng khác (thường dành riêng cho các chương trình đồ chơi) là thoát khỏi chương trình với các yêu cầu đệ quy main.


1
@Seb không có gì đặc biệt main()- đó chỉ là một chức năng như mọi thứ khác. Mặt khác, vì nó có đề cập đặc biệt trong tiêu chuẩn, tiêu chuẩn phải khá cẩn thận về cách định nghĩa main()và những thứ gần và thân với nó. Tuy nhiên, cuối cùng mặc dù tiêu chuẩn không (và không phải ) yêu cầu trình biên dịch làm bất cứ điều gì đặc biệt về lưu trữ tự động main(). Hãy cẩn thận để đọc chú thích số 11 bên dưới đoạn bạn đã tham chiếu trong bình luận của mình.
Greg A. Woods

1
@ GregA.Woods Thú vị. Dường như có một số văn bản quy phạm mâu thuẫn với một số văn bản thông tin. Theo các chỉ thị của ISO / IEC , tham chiếu quy phạm được coi là "không thể thiếu", trong đó - vì thông tin chỉ được coi là bổ sung ... Ngoài ra, việc sử dụng từ "ý chí" để truyền đạt yêu cầu là không hợp lệ; theo tài liệu nói trên (Phụ lục H). Tóm lại, văn bản thông tin chắc chắn là không hợp lệ.
tự kỷ

2
@Seb: Mục đích rõ ràng là không ghi đè các yêu cầu về hành vi lưu trữ tự động và chú thích rõ ràng được viết để làm rõ điều này. Có, không chính xác, từ ngữ kém trong tiêu chuẩn C. Bất cứ ai đã đọc nó đều biết điều này. Chúng tôi cũng biết rằng ủy ban thường không khắc phục các vấn đề như thế này vì mục đích đã rõ ràng.
R .. GitHub DỪNG GIÚP ICE

1
@Seb: Đây không phải là một cuộc tranh luận hoặc vụ kiện để chứng minh bạn đúng. Mục tiêu cần có được sự hiểu biết rõ ràng về ngôn ngữ C thực tế (như dự định và như đã thực hiện) là gì, và thể hiện rằng trong các câu trả lời hữu ích cho độc giả. Văn bản quy phạm là sai một cách tinh tế (trái với ý định của những gì nó được cho là thể hiện) theo cách mà về cơ bản là cố định bởi chú thích. Nếu bạn không hài lòng với điều này, hãy gửi báo cáo lỗi, nhưng đừng mong chờ hồi âm. Đó là cách WG14 cuộn ...
R .. GitHub DỪNG GIÚP ICE

3
@Seb: Bạn dường như tin rằng ngôn ngữ C có thể được hiểu bằng cách diễn giải văn bản ngôn ngữ tự nhiên của tiêu chuẩn như thể nó hoàn toàn nghiêm ngặt. Điều này chỉ đơn giản là không thể. Đặc tả kỹ thuật có lỗi và WG14 không lãng phí thời gian để viết lại nội dung khi một chú thích đơn giản làm rõ rằng họ đã biết rằng họ đã mắc lỗi nhưng người đọc có thể hiểu ý nghĩa của nó.
R .. GitHub DỪNG GIÚP ICE

5

Tôi luôn luôn sử dụng returnbởi vì nguyên mẫu chuẩn cho main()biết nó sẽ trả về một int.

Điều đó nói rằng, một số phiên bản của các tiêu chuẩn đưa ra mainđối xử đặc biệt và cho rằng nó trả về 0 nếu không có returntuyên bố rõ ràng . Cho mã sau:

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

G ++ chỉ tạo cảnh báo cho foo()và bỏ qua trả lại bị thiếu từ main:

% g++ -Wall -c foo.cc
foo.cc: In function int foo()’:
foo.cc:1: warning: control reaches end of non-void function

Tôi không biết về C, nhưng tiêu chuẩn C ++ chỉ định rằng nếu bạn không trả về giá trị chính, thì nó được coi là trả về 0.
Jason Baker

Dường như C99 giống nhau: faq.cprogramming.com/cgi-bin/ Kẻ
Jason Baker

2
C99 và C ++ trả về 0 nếu không có câu lệnh return, C90 thì không.
d0k

Chỉ vì một hàm được khai báo là có giá trị trả về không có nghĩa là bạn phải sử dụng returnđể kết thúc thực thi. Gọi exit()cũng là một cách hợp lệ và đôi khi cần thiết để kết thúc việc thực hiện bất kỳ chức năng nào. Thật vậy, như tôi và những người khác đã mô tả ở nơi khác, exit()thậm chí gọi từ việc main()truyền đạt ý định rõ ràng hơn nhiều về việc thoát khỏi toàn bộ quy trình, bảo quản lưu trữ tự động cho đến khi thoát quy trình và giúp bảo trì dễ dàng hơn trong quá trình tái cấu trúc mã trong tương lai. Đối với C sử dụng returntrong main()khi mục đích là để kết thúc quá trình này là như vậy, cho là một thói quen xấu.
Greg A. Woods

@ GregA.Woods đó là ý kiến ​​của bạn, nhưng hầu như không bỏ phiếu! Những gì tôi đã viết ở trên hoàn toàn phù hợp với tiêu chuẩn , trong khi đối số của bạn chỉ là ngữ nghĩa.
Alnitak

5

Tôi MẠNH thứ hai nhận xét của R. về việc sử dụng exit () để tránh việc lưu trữ tự động trong main()khai hoang trước khi chương trình thực sự kết thúc. Một return X;câu lệnh trong main()không chính xác tương đương với một cuộc gọi đến exit(X);, vì bộ lưu trữ động main()biến mất khi main()trả về, nhưng nó không biến mất nếu một cuộc gọi đến exit()được thực hiện thay thế.

Hơn nữa, trong ngôn ngữ C hoặc bất kỳ ngôn ngữ giống C nào, một returntuyên bố mạnh mẽ gợi ý cho người đọc rằng việc thực thi sẽ tiếp tục trong chức năng gọi và trong khi việc tiếp tục thực hiện này thường đúng về mặt kỹ thuật nếu bạn tính thói quen khởi động C gọi là main()chức năng của bạn , thì không chính xác những gì bạn có nghĩa là khi bạn có nghĩa là kết thúc quá trình.

Rốt cuộc, nếu bạn muốn kết thúc chương trình của mình từ bên trong bất kỳ chức năng nào khác ngoại trừ main()bạn phải gọi exit(). Làm như vậy một cách nhất quán main()cũng làm cho mã của bạn dễ đọc hơn nhiều, và nó cũng giúp mọi người dễ dàng tái lập mã của bạn hơn nhiều; tức là mã được sao chép từ main()một số chức năng khác sẽ không hoạt động sai vì các returncâu lệnh vô tình đáng lẽ phải là exit()các cuộc gọi.

Vì vậy, kết hợp tất cả các điểm này lại với nhau kết luận rằng đó là một thói quen xấu , ít nhất là đối với C, sử dụng một returntuyên bố để kết thúc chương trình main().


Bạn có thể thấy 5.1.2.2.3p1 của tiêu chuẩn C thú vị ...
tự kỷ

Câu trả lời này đáng để xem xét cẩn thận cho các chương trình C, như được chỉ ra theo ngữ cảnh trong câu trả lời. Để sử dụng với C ++, nó cần được cân nhắc cẩn thận với tất cả các cảnh báo đã đề cập trước đó. Đối với C ++, tôi sẽ đề nghị tránh exit()nói chung, nhưng hãy sử dụng nó nếu một throwhoặc abort()các lựa chọn thay thế không hoạt động trong một bối cảnh cụ thể. Nhưng đặc biệt tránh exit()trong chính, và sử dụng trở lại trong chính thay vì như thực hành điển hình.
Eljay

5

Liệu exit () có làm gì đặc biệt mà 'return' không?

Với một số trình biên dịch cho các nền tảng không phổ biến, exit()có thể dịch đối số của nó thành giá trị thoát của chương trình trong khi lợi nhuận từ main()có thể chuyển trực tiếp giá trị sang môi trường máy chủ mà không cần dịch.

Tiêu chuẩn yêu cầu hành vi giống hệt nhau trong những trường hợp này (cụ thể, nó nói rằng việc trả lại một cái gì đó inttương thích từ main()đó sẽ tương đương với việc gọi exit()với giá trị đó). Vấn đề là các hệ điều hành khác nhau có các quy ước khác nhau để diễn giải các giá trị thoát. Trên nhiều hệ thống (NHIỀU!), 0 có nghĩa là thành công và bất cứ điều gì khác là thất bại. Nhưng trên, nói, VMS, giá trị lẻ có nghĩa là thành công và thậm chí những giá trị có nghĩa là thất bại. Nếu bạn trả về 0 từ main(), người dùng VMS sẽ thấy một thông báo khó chịu về vi phạm quyền truy cập. Thực tế không có vi phạm truy cập - đó đơn giản là thông báo tiêu chuẩn liên quan đến mã lỗi 0.

Sau đó, ANSI xuất hiện và ban phước EXIT_SUCCESSEXIT_FAILUREnhư những lý lẽ bạn có thể vượt qua exit(). Tiêu chuẩn này cũng nói rằng exit(0)nên cư xử y hệt exit(EXIT_SUCCESS), vì vậy hầu hết các trường xác định EXIT_SUCCESSđể 0.

Do đó, tiêu chuẩn đặt bạn vào một liên kết trên VMS, vì nó không có cách tiêu chuẩn nào để trả về mã lỗi xảy ra có giá trị 0.

Do đó, trình biên dịch VAX / VMS C thời kỳ đầu những năm 1990 không giải thích giá trị trả về từ main()đó, nó chỉ đơn giản trả về bất kỳ giá trị nào cho môi trường máy chủ. Nhưng nếu bạn đã sử dụng exit()nó sẽ làm những gì tiêu chuẩn yêu cầu: dịch EXIT_SUCCESS(hoặc 0) thành mã thành công và EXIT_FAILUREthành mã lỗi chung. Để sử dụng EXIT_SUCCESS, bạn phải chuyển nó đến exit(), bạn không thể trả lại từ đó main(). Tôi không biết liệu các phiên bản hiện đại hơn của trình biên dịch đó có duy trì hành vi đó hay không.

Một chương trình C di động được sử dụng để trông như thế này:

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

int main() {
  printf("Hello, World!\n");
  exit(EXIT_SUCCESS);  /* to get good return value to OS */
  /*NOTREACHED*/ /* to silence lint warning */
  return 0;  /* to silence compiler warning */
}

Ngoài ra: Nếu tôi nhớ lại một cách chính xác, quy ước VMS cho các giá trị thoát có nhiều sắc thái hơn lẻ / chẵn. Nó thực sự sử dụng một cái gì đó như ba bit thấp để mã hóa mức độ nghiêm trọng. Tuy nhiên, nói chung, mức độ nghiêm trọng kỳ lạ chỉ ra thông tin thành công hoặc thông tin linh tinh và các mức chẵn chỉ ra lỗi.


Một số trình biên dịch trước ANSI cũ có thể đã đối xử với giá trị returnedbằng maincách khác nhau từ giá trị truyền cho exit- nhưng tiêu chuẩn nói cụ thể, "Nếu kiểu trả về của mainhàm là một loại phù hợp với int, một trở về từ các cuộc gọi ban đầu đối với mainchức năng là 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ó ". Đó là C11; C89 / C90 có cách diễn đạt gần giống nhau.
Keith Thompson

Thật. Tuy nhiên, một số trình biên dịch thời ANSI không có quyền này và yêu cầu sử dụng lối thoát rõ ràng để có được giá trị trả về chính xác được trả về môi trường máy chủ. Do tiêu chuẩn (thậm chí sau đó) yêu cầu 0 phải được xử lý giống như EXIT_SUCCESS, nên không có cách nào để trả về trạng thái lỗi cụ thể của nền tảng với giá trị 0, đó có thể là lý do tại sao một số trình biên dịch của thời đại được xử lý trả về từ chính và exit()khác nhau.
Adrian McCarthy

Bạn có một trích dẫn cho điều đó? Một vấn đề riêng là liệu có bất kỳ trình biên dịch hiện tại nào có lỗi cụ thể đó không. Câu trả lời của bạn là cho phrased ở thì hiện tại.
Keith Thompson

Đó là một lời chỉ trích công bằng. Tôi đã thay đổi từ ngữ để giới hạn phạm vi cho trường hợp cụ thể mà tôi biết.
Adrian McCarthy

0

Trong C trở về mainhoàn toàn giống như gọi exitvới cùng một giá trị.

Mục 5.1.2.2.3 của trạng thái tiêu chuẩn C :

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ị 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.

Các quy tắc cho C ++ là một chút khác nhau như được đề cập trong các câu trả lời khác.


-1

Thực sự có một sự khác biệt giữa exit(0)return(0)trong main- khi mainchức năng của bạn được gọi nhiều lần.

Chương trình sau

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

int main(int argc, char** argv) {
  if (argc == 0)
    return(0);
  printf("%d", main(argc - 1, argv));
}

Chạy như

./program 0 0 0 0

Sẽ dẫn đến kết quả đầu ra sau:

00000

Tuy nhiên cái này:

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

int main(int argc, char** argv) {
  if (argc == 0)
    exit(0);
  printf("%d", main(argc - 1, argv));
}

Không in bất cứ điều gì bất kể đối số.

Nếu bạn chắc chắn rằng không ai sẽ gọi cho bạn mainmột cách rõ ràng thì về mặt kỹ thuật, đó không phải là một sự khác biệt lớn, nhưng để duy trì mã rõ ràng hơn exitsẽ tốt hơn nhiều. Nếu bạn vì lý do nào đó muốn gọi main- bạn nên điều chỉnh nó theo nhu cầu của bạn.

Nói về C.

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.