Apakah ada perbedaan antara return n dan exit (n) dalam C?

9

Apakah ada perbedaan antara return n(dalam mainfungsi) dan exit(n)dalam C? Apakah ini didefinisikan oleh standar C atau POSIX atau tergantung pada OS atau kompiler?

Thomas Owens
sumber

Jawaban:

5

Dalam kebanyakan kasus, tidak ada perbedaan, tetapi inilah program C yang cenderung berperilaku berbeda tergantung pada apakah ia menggunakan return 0;atau 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
}

Karena atexit()panggilan, salah satu exit(0);atau return 0;menyebabkan cleanupfungsi dipanggil. Perbedaannya adalah bahwa jika program memanggil exit(0);, pembersihan terjadi ketika "panggilan" main()masih aktif, sehingga local_messageobjek masih ada. Melaksanakan return 0;, bagaimanapun, segera mengakhiri doa main()dan kemudian memanggil cleanup()fungsi. Karena cleanup()merujuk (melalui messagepenunjuk global ) ke objek yang dialokasikan secara lokal main, dan objek itu tidak ada lagi, perilaku tidak terdefinisi.

Inilah perilaku yang saya lihat di sistem saya:

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

Menjalankan program tanpa -DUSE_EXITbisa melakukan apa-apa, termasuk crash atau mencetak "hello, world"(jika memori yang digunakan local_messagekebetulan tidak akan musnah).

Namun dalam praktiknya, perbedaan ini hanya muncul jika objek yang didefinisikan secara lokal di dalam main()dibuat terlihat di luar main()dengan menyimpan pointer ke sana. Ini masuk akal untuk terjadi argv. (Eksperimen pada sistem saya menunjukkan bahwa objek yang ditunjukkan oleh argvdan oleh *argvterus ada setelah kembali dari main(), tetapi Anda tidak harus bergantung pada itu.)

Keith Thompson
sumber
16
  • Untuk C
    , Standar mengatakan bahwa pengembalian dari panggilan awal ke utama sama dengan panggilan keluar. Namun, pengembalian dari main tidak dapat diharapkan berfungsi jika data lokal ke main mungkin diperlukan selama pembersihan.

  • Untuk C ++

Ketika keluar (0) digunakan untuk keluar dari program, destruktor untuk objek non-statis yang dicakup secara lokal tidak dipanggil. Tetapi destruktor dipanggil jika return 0 digunakan.

Program 1 - - menggunakan exit (0) untuk keluar

#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);
}

Output: Konstruktor Inside Test

Program 2 - menggunakan return 0 untuk keluar

#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;
}

Output: Konstruktor
Inside Test Di dalam Destructor Test

Memanggil destruktor terkadang penting, misalnya, jika destruktor memiliki kode untuk melepaskan sumber daya seperti menutup file.

Perhatikan bahwa objek statis akan dibersihkan walaupun kita memanggil exit (). Sebagai contoh, lihat program berikut.

#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);
}

Output: Konstruktor
Inside Test Di dalam Destructor Test

Vaibhav Agarwal
sumber
Menutup file sebenarnya bukan contoh bagus destruktor penting yang akan diaktifkan ketika Anda keluar, karena file akan ditutup saat keluar dari program.
Winston Ewert
1
@ WinstonEwert: Benar, tetapi mungkin ada buffer tingkat aplikasi yang masih perlu dibilas.
Philipp
1
Pertanyaannya tidak menyebutkan C ++ di mana saja ...
tdammers
Saya tidak tahu bahasa yang memaafkan saya, tetapi jawaban ini membuat saya berpikir bahwa exit adalah seperti failfast C #, jadi di C ++ apakah exit dalam mencoba menjalankan akhirnya?
Jimmy Hoffa
@JimmyHoffa, c ++ tidak punyafinally
Winston Ewert
6

Perlu dicatat bahwa standar C (C99) mendefinisikan dua jenis lingkungan eksekusi, Freestanding Environment dan Hosted Environment . Lingkungan berdiri bebas adalah lingkungan C yang tidak mendukung perpustakaan C dan dimaksudkan untuk aplikasi yang disematkan dan sejenisnya. Lingkungan AC yang mendukung perpustakaan C disebut lingkungan Hosted.

C99 mengatakan, dalam penghentian program lingkungan Freestanding didefinisikan implementasi. Jadi, jika implementasi mendefinisikan main,, return ndan exit, perilaku mereka seperti yang didefinisikan dalam implementasi itu.

C99 mendefinisikan perilaku lingkungan yang dihosting sebagai,

Jika tipe pengembalian fungsi utama adalah tipe yang kompatibel dengannya, pengembalian dari panggilan awal ke fungsi utama sama dengan memanggil fungsi keluar dengan nilai yang dikembalikan oleh fungsi utama sebagai argumennya; mencapai} yang menghentikan fungsi utama mengembalikan nilai 0. Jika tipe kembali tidak kompatibel dengan int, status terminasi yang dikembalikan ke lingkungan host tidak ditentukan.

theD
sumber
1
Dan benar-benar tidak masuk akal untuk memanggil exit () dari lingkungan yang berdiri sendiri. Setara keluar dari tertanam () adalah untuk menggantung program dalam loop abadi, kemudian menunggu pengawas waktu habis.
0

Dari sudut pandang standar C, tidak benar-benar, selain returnsebagai pernyataan dan exit()fungsi. Entah akan menyebabkan fungsi apa pun yang terdaftar dengan atexit()dipanggil diikuti dengan penghentian program.

Ada beberapa situasi yang ingin Anda perhatikan:

  • Rekursi dalam main(). Meskipun jarang terlihat dalam praktek, itu legal dalam C. (C ++ secara eksplisit melarangnya.)
  • Penggunaan kembali main(). Terkadang yang sudah ada main()akan diganti namanya menjadi sesuatu yang lain dan dipanggil oleh yang baru main().

Penggunaan exit()akan memperkenalkan bug jika salah satu dari itu terjadi setelah Anda menulis kode, terutama jika tidak berhenti secara tidak normal. Untuk menghindarinya, adalah ide yang baik untuk memiliki kebiasaan memperlakukan main()sebagai fungsi itu dan menggunakannya returnketika Anda ingin itu berakhir.

Blrfl
sumber