Có sự khác biệt nào giữa return n và exit (n) trong C không?


9

Có sự khác biệt nào giữa return n(trong mainhàm) và exit(n)trong C không? Được xác định bởi các tiêu chuẩn C hoặc POSIX hay nó phụ thuộc vào HĐH hoặc trình biên dịch?

Câu trả lời:


5

Trong hầu hết các trường hợp, không có sự khác biệt, nhưng đây là chương trình C có khả năng hoạt động khác nhau tùy thuộc vào việc nó sử dụng return 0;hay exit(0);:

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

static char *message;

void cleanup(void) {
    printf("message = \"%s\"\n", message);
}

int main(void) {
    char local_message[] = "hello, world";
    message = local_message;
    atexit(cleanup);
#ifdef USE_EXIT
    puts("exit(0);");
    exit(0);
#else
    puts("return 0;");
    return 0;
#endif
}

Bởi vì các atexit()cuộc gọi, một trong hai exit(0);hoặc return 0;nguyên nhân các cleanupchức năng được gọi. Sự khác biệt là nếu chương trình gọi exit(0);, việc dọn dẹp xảy ra trong khi "cuộc gọi" main()vẫn còn hoạt động, vì vậy local_messageđối tượng vẫn tồn tại. Thực hiện return 0;, tuy nhiên, ngay lập tức chấm dứt gọi trình main()sau đó gọi các cleanup()chức năng. Vì tham chiếu cleanup()(thông qua messagecon trỏ toàn cục ) đến một đối tượng được phân bổ cục bộ mainvà đối tượng đó không còn tồn tại, nên hành vi không được xác định.

Đây là hành vi tôi thấy trên hệ thống của mình:

$ gcc -DUSE_EXIT c.c -o c && ./c
exit(0);
message = "hello, world"
$ gcc c.c -o c && ./c
return 0;
message = ""
$ 

Chạy chương trình mà không -DUSE_EXITthể làm bất cứ điều gì, bao gồm cả sự cố hoặc in "hello, world"(nếu bộ nhớ được sử dụng local_messagexảy ra không bị ghi đè).

Tuy nhiên, trong thực tế, sự khác biệt này chỉ hiển thị nếu các đối tượng được xác định cục bộ bên trong main()được hiển thị bên ngoài main()bằng cách lưu con trỏ vào chúng. Điều này có thể xảy ra hợp lý cho argv. (Thử nghiệm trên hệ thống của tôi cho thấy các đối tượng được chỉ đến argv*argvtiếp tục tồn tại sau khi trở về main(), nhưng bạn không nên phụ thuộc vào điều đó.)


16
  • Đối với C
    Tiêu chuẩn nói rằng việc trả lại từ cuộc gọi ban đầu đến chính tương đương với thoát lệnh gọi. Tuy nhiên, một sự trở lại từ chính không thể được dự kiến ​​sẽ hoạt động nếu dữ liệu cục bộ đến chính có thể cần thiết trong quá trình dọn dẹp.

  • Dành cho C ++

Khi exit (0) được sử dụng để thoát khỏi chương trình, các hàm hủy cho các đối tượng không tĩnh trong phạm vi cục bộ không được gọi. Nhưng các hàm hủy được gọi nếu return 0 được sử dụng.

Chương trình 1 - - sử dụng exit (0) để thoát

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

using namespace std;

class Test {
public:
  Test() {
    printf("Inside Test's Constructor\n");
  }

  ~Test(){
    printf("Inside Test's Destructor");
    getchar();
  }
};

int main() {
  Test t1;

  // using exit(0) to exit from main
  exit(0);
}

Đầu ra: Inside Test's Con constructor

Chương trình 2 - sử dụng return 0 để thoát

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

using namespace std;

class Test {
public:
  Test() {
    printf("Inside Test's Constructor\n");
  }

  ~Test(){
    printf("Inside Test's Destructor");
  }
};

int main() {
  Test t1;

   // using return 0 to exit from main
  return 0;
}

Đầu ra: Inside Con's
Contortor Inside Test's Destructor

Gọi hàm hủy đôi khi rất quan trọng, ví dụ, nếu hàm hủy có mã để giải phóng tài nguyên như đóng tệp.

Lưu ý rằng các đối tượng tĩnh sẽ được dọn sạch ngay cả khi chúng ta gọi exit (). Ví dụ, xem chương trình sau đây.

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

using namespace std;

class Test {
public:
  Test() {
    printf("Inside Test's Constructor\n");
  }

  ~Test(){
    printf("Inside Test's Destructor");
    getchar();
  }
};

int main() {
  static Test t1;  // Note that t1 is static

  exit(0);
}

Đầu ra: Inside Con's
Contortor Inside Test's Destructor


Đóng các tệp không thực sự là một ví dụ tốt về một hàm hủy quan trọng để kích hoạt khi bạn thoát, bởi vì các tệp sẽ bị đóng khi thoát khỏi chương trình.
Winston Ewert

1
@WinstonEwert: Đúng, nhưng có thể tồn tại bộ đệm cấp ứng dụng vẫn cần phải xóa.
Philipp

1
Câu hỏi không đề cập đến C ++ ở bất cứ đâu ...
tdammers

Tôi không biết ngôn ngữ nào tha thứ cho tôi, nhưng câu trả lời này khiến tôi nghĩ lối thoát giống như thất bại của C #, vậy trong C ++ có thoát ra trong một lần thử cuối cùng không?
Jimmy Hoffa

@JimmyHoffa, c ++ không cófinally
Winston Ewert

6

Đáng chú ý là tiêu chuẩn C (C99) xác định hai loại môi trường thực thi là Môi trường tự doMôi trường lưu trữ . Môi trường tự do là môi trường C không hỗ trợ các thư viện C và dành cho các ứng dụng nhúng và tương tự. Môi trường AC hỗ trợ các thư viện C được gọi là môi trường được lưu trữ.

C99 nói, trong một chấm dứt chương trình môi trường tự do được thực hiện được xác định. Vì vậy, nếu việc thực hiện định nghĩa main, return nexit, hành vi của họ được xác định như trong việc thực hiện đó.

C99 định nghĩa hành vi môi trường được lưu trữ là,

Nếu kiểu trả về của hàm chính là một loại tương thích với nó, 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ó; đạ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.


1
Và nó thực sự không có ý nghĩa gì khi gọi exit () từ một môi trường tự do. Tương đương được nhúng của exit () sẽ là treo chương trình trong một vòng lặp vĩnh cửu, sau đó chờ cơ quan giám sát hết thời gian.

0

Từ quan điểm của tiêu chuẩn C, không thực sự, ngoài returnviệc là một tuyên bố và exit()là một chức năng. Hoặc sẽ gây ra bất kỳ chức năng nào được đăng ký atexit()để được gọi theo sau là chấm dứt chương trình.

Có một vài tình huống bạn muốn đề phòng:

  • Đệ quy trong main(). Mặc dù hiếm khi được nhìn thấy trong thực tế, nó là hợp pháp trong C. (C ++ rõ ràng cấm nó.)
  • Tái sử dụng main(). Đôi khi một cái hiện có main()sẽ được đổi tên thành cái khác và được gọi bởi một cái mới main().

Việc sử dụng exit()sẽ gây ra lỗi nếu một trong hai lỗi xảy ra sau khi bạn đã viết mã, đặc biệt là nếu không chấm dứt bất thường. Để tránh điều đó, bạn nên tập thói quen coi main()chức năng của nó và sử dụng returnkhi bạn muốn nó kết thú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.