pernyataan return vs exit () di main ()

197

Haruskah saya menggunakan exit()atau hanya returnpernyataan dalam main()? Secara pribadi saya menyukai returnpernyataan karena saya merasa itu seperti membaca fungsi lain dan kontrol aliran ketika saya membaca kode halus (menurut saya). Dan bahkan jika saya ingin memperbaiki main()fungsi, memiliki pilihan yang returnsepertinya lebih baik daripada exit().

Apakah exit()melakukan sesuatu yang istimewa yang returntidak?

Srikanth
sumber

Jawaban:

277

Sebenarnya, ada adalah perbedaan, tapi itu halus. Ini memiliki lebih banyak implikasi untuk C ++, tetapi perbedaannya penting.

Ketika saya sebut returndi main(), destructors akan dipanggil untuk objek saya scoped lokal. Jika saya panggil exit(), tidak ada destruktor yang akan dipanggil untuk objek yang dicakup secara lokal! Baca kembali itu. exit() tidak kembali . Itu berarti bahwa begitu saya menyebutnya, tidak ada "backsies." Objek apa pun yang Anda buat dalam fungsi itu tidak akan dihancurkan. Seringkali ini tidak memiliki implikasi, tetapi kadang-kadang tidak, seperti menutup file (pasti Anda ingin semua data Anda di-flush ke disk?).

Perhatikan bahwa staticobjek akan dibersihkan bahkan jika Anda menelepon exit(). Akhirnya perhatikan, bahwa jika Anda menggunakan abort(), tidak ada objek yang akan dihancurkan. Artinya, tidak ada objek global, tidak ada objek statis dan tidak ada objek lokal yang akan memanggil destruktornya.

Lanjutkan dengan hati-hati saat memilih jalan keluar daripada pulang.

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

GratisMemory
sumber
1
batalkan () keluar dengan kondisi kesalahan (kode keluar bukan nol) dan mungkin bahkan sebuah inti. Jika Anda perlu keluar tanpa memanggil destruktor statis, gunakan _exit.
7
@ Mike: ada perbedaan antara buffer file library C dan objek stream file C ++. exit () - menjadi bagian dari pustaka C - dirancang untuk berkoordinasi dan mem-flush yang pertama, tetapi dapat mem-bypass yang terakhir: bahkan konten standar C ++ fstream tidak memerah ke disk (coba - saya lakukan, gagal w / Linux / GCC), dan jelas jenis yang ditentukan pengguna yang memiliki buffer I / O tidak dapat diharapkan untuk memerah juga.
Tony Delroy
9
Catatan: Pernyataan: tidak ada destruktor yang akan dipanggil untuk objek yang dicakup secara lokal! tidak lagi benar untuk C ++ 11: - Objek yang terkait dengan utas saat ini dengan durasi penyimpanan utas dihancurkan (hanya C ++ 11). cplusplus.com/reference/cstdlib/exit
Ilendir
7
Artinya, thread_localpenghancur benda akan dipanggil. Destructors untuk objek lokal lainnya masih belum dipanggil. ideone.com/Y6Dh3f
HolyBlackCat
3
BTW, dan hanya untuk menjadi bertele-tele dan karena jawaban ini masih bisa membingungkan bagi pembaca menggunakan C: Untuk C masalah tentang exit()menutup file dengan bersih sebenarnya salah. Satu-satunya waktu data mungkin tidak memerah adalah dalam kasus sebaliknya: yaitu jika seseorang menggunakan returndari main()dan seseorang telah memanggil setbuf()atau setvbuf()dengan buffer dinyatakan sebagai penyimpanan otomatis dalam main()(seperti dibahas dalam jawaban R. di bawah). Sayang sekali pertanyaan ini ditandai dengan C dan C ++ (dan gaya pengkodean - ini bukan masalah gaya!).
Greg A. Woods
25

Perbedaan lainnya: exitadalah fungsi Perpustakaan Standar sehingga Anda harus menyertakan header dan tautan dengan perpustakaan standar. Untuk menggambarkan (dalam C ++), ini adalah program yang valid:

int main() { return 0; }

tetapi untuk menggunakan exitAnda harus menyertakan:

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

Plus ini menambahkan asumsi tambahan: bahwa panggilan exitdari mainmemiliki efek samping yang sama dengan mengembalikan nol. Seperti yang telah ditunjukkan orang lain, ini tergantung pada jenis executable yang Anda bangun (yaitu, siapa yang menelepon main). Apakah Anda mengkode aplikasi yang menggunakan runtime C? Plugin Maya? Layanan Windows? Supir? Setiap kasus akan membutuhkan penelitian untuk melihat apakah exitsetara dengan return. Menggunakan IMHO exitketika Anda benar-benar berarti return hanya membuat kode lebih membingungkan. OTOH, jika Anda benar-benar jahat exit , maka gunakanlah itu.

jwfearn
sumber
16

Setidaknya ada satu alasan untuk memilih exit: Jika salah satu atexitpenangan Anda merujuk ke data durasi penyimpanan otomatis main, atau jika Anda menggunakan setvbufatau setbufuntuk menetapkan salah satu aliran standar, buffer durasi penyimpanan otomatis masuk main, lalu kembali dari mainmenghasilkan perilaku tidak terdefinisi, tetapi panggilan exitvalid.

Namun potensi penggunaan lain (biasanya dicadangkan untuk program mainan) adalah untuk keluar dari program dengan doa rekursif main.

R .. GitHub BERHENTI MEMBANTU ICE
sumber
1
@ Sebelah tidak ada yang istimewa main()- itu hanya fungsi seperti yang lain. Di sisi lain karena standar tersebut telah disebutkan secara khusus dalam standar, standar tersebut harus cukup hati-hati tentang bagaimana mendefinisikan main()dan hal-hal yang dekat dan disukai. Namun pada akhirnya meskipun standar tidak (dan tidak boleh ) memerlukan kompiler untuk melakukan sesuatu yang khusus tentang penyimpanan otomatis di main(). Harap berhati-hati untuk membaca Catatan Kaki # 11 di bawah paragraf yang Anda rujuk dalam komentar Anda.
Greg A. Woods
1
@ GregA. Kayu Menarik. Tampaknya ada beberapa teks normatif yang bertentangan dengan beberapa teks informatif. Menurut arahan ISO / IEC , referensi normatif dianggap "sangat diperlukan", di mana-karena informatif dianggap sebagai pelengkap saja ... Selain itu, penggunaan kata "akan" untuk menyampaikan persyaratan tidak valid; menurut dokumen tersebut (Lampiran H). Singkatnya, teks informatif ini tentu saja tidak valid.
autis
2
@Seb: Maksudnya jelas bukan untuk mengesampingkan persyaratan pada perilaku penyimpanan otomatis dan catatan kaki jelas ditulis untuk memperjelas ini. Ya ada kata-kata yang tidak tepat dan buruk dalam standar C. Siapa pun yang telah membacanya mengetahui hal ini. Kita juga tahu bahwa panitia umumnya tidak memperbaiki masalah seperti ini karena niatnya sudah jelas.
R .. GitHub BERHENTI MEMBANTU ICE
1
@Seb: Ini bukan kasus perdebatan atau pengadilan untuk membuktikan Anda benar. Tujuannya harus mendapatkan pemahaman yang jelas tentang apa bahasa C yang sebenarnya (sebagaimana dimaksud dan diimplementasikan), dan menyatakan bahwa dalam jawaban yang bermanfaat bagi pembaca. Teks normatif agak salah (bertentangan dengan maksud dari apa yang seharusnya diungkapkan) dengan cara yang pada dasarnya diperbaiki oleh catatan kaki. Jika Anda tidak puas dengan ini, kirimkan laporan cacat, tetapi jangan mengharapkan balasan. Begitulah cara WG14 berputar ...
R .. GitHub BERHENTI MEMBANTU ICE
3
@Seb: Anda tampaknya percaya bahwa bahasa C dapat dipahami dengan menafsirkan teks bahasa alami dari standar seolah-olah benar-benar ketat. Ini sama sekali tidak mungkin. Spesifikasi tersebut mengandung kesalahan, dan WG14 tidak membuang-buang waktu untuk menulis ulang ketika catatan kaki sederhana mengklarifikasi bahwa mereka sudah tahu bahwa mereka membuat kesalahan tetapi pembaca dapat memahaminya.
R .. GitHub BERHENTI MEMBANTU ICE
5

Saya selalu menggunakan returnkarena prototipe standar untuk main()mengatakan bahwa ia mengembalikan sebuah int.

Yang mengatakan, beberapa versi standar memberikan mainperlakuan khusus dan menganggap bahwa itu mengembalikan 0 jika tidak ada returnpernyataan eksplisit . Diberikan kode berikut:

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

G ++ hanya menghasilkan peringatan untuk foo()dan mengabaikan pengembalian yang hilang dari main:

% g++ -Wall -c foo.cc
foo.cc: In function int foo()’:
foo.cc:1: warning: control reaches end of non-void function
Alnitak
sumber
Aku tidak tahu tentang C, tapi C ++ Menentukan standar bahwa jika Anda tidak mengembalikan nilai dalam utama, itu diasumsikan untuk kembali 0.
Jason Baker
Tampaknya seolah-olah C99 adalah sama: faq.cprogramming.com/cgi-bin/…
Jason Baker
2
C99 dan C ++ mengembalikan 0 jika tidak ada pernyataan kembali, C90 tidak.
d0k
Hanya karena suatu fungsi dinyatakan memiliki nilai balik, bukan berarti Anda harus menggunakannya returnuntuk mengakhiri eksekusi. Memanggil exit()juga merupakan cara yang valid, dan terkadang diperlukan, untuk mengakhiri eksekusi fungsi apa pun. Memang seperti yang saya dan orang lain telah jelaskan di tempat lain, menelepon exit()bahkan dari main()menyampaikan niat yang jauh lebih jelas untuk keluar dari seluruh proses, menjaga penyimpanan otomatis hingga proses keluar, dan membuat pemeliharaan lebih mudah selama refactoring kode di masa depan. Untuk C menggunakan returndi main()saat tujuannya adalah untuk mengakhiri proses ini sehingga bisa dibilang praktek yang buruk.
Greg A. Woods
@ GregA. Kayu itu pendapat Anda, tapi hampir tidak layak untuk memilih! Apa yang saya tulis di atas sepenuhnya konsisten dengan standar , sedangkan argumen Anda hanya semantik.
Alnitak
5

SAYA SANGAT memberikan komentar kedua oleh R. tentang penggunaan exit () untuk menghindari penyimpanan otomatis di main()reklamasi sebelum program benar-benar berakhir. Sebuah return X;pernyataan di main()tidak tepat setara dengan panggilan untuk exit(X);, karena penyimpanan dinamis main()hilang ketika main()kembali, tapi itu tidak lenyap jika panggilan untuk exit()dibuat sebagai gantinya.

Selain itu, dalam bahasa C atau bahasa C-seperti returnpernyataan sangat mengisyaratkan kepada pembaca bahwa eksekusi akan melanjutkan dalam fungsi panggilan, dan sementara kelanjutan eksekusi ini biasanya benar secara teknis jika Anda menghitung rutin startup C yang disebut main()fungsi Anda , itu tidak persis apa yang Anda maksud ketika Anda bermaksud mengakhiri proses.

Lagi pula, jika Anda ingin mengakhiri program Anda dari dalam fungsi lain kecuali main()Anda harus memanggil exit(). Melakukannya secara konsisten main()juga membuat kode Anda jauh lebih mudah dibaca, dan juga membuatnya lebih mudah bagi siapa pun untuk memasukkan faktor ulang kode Anda; kode yaitu disalin dari main()ke beberapa fungsi lain tidak akan berperilaku tidak benar karena returnpernyataan tidak disengaja yang seharusnya menjadi exit()panggilan.

Jadi, menggabungkan semua poin ini bersama kesimpulannya adalah bahwa itu kebiasaan buruk , setidaknya bagi C, untuk menggunakan returnpernyataan untuk mengakhiri program main().

Greg A. Woods
sumber
Anda mungkin menemukan 5.1.2.2.3p1 dari standar C menarik ...
autis
Jawaban ini layak dipertimbangkan dengan cermat untuk program C, seperti yang ditunjukkan secara kontekstual dalam jawaban. Untuk digunakan dengan C ++, perlu ditimbang dengan hati-hati terhadap semua peringatan yang disebutkan sebelumnya. Untuk C ++, saya akan menyarankan untuk menghindari exit()secara umum, tetapi gunakan jika a throwatau abort()alternatif tidak bekerja dalam konteks tertentu. Tetapi terutama hindari exit()di main, dan gunakan return in main sebagai praktik biasa.
Eljay
5

Apakah exit () melakukan sesuatu yang istimewa yang 'kembali' tidak?

Dengan beberapa kompiler untuk platform yang tidak umum, exit()mungkin menerjemahkan argumennya ke nilai keluar program Anda sementara pengembalian dari main()mungkin hanya meneruskan nilai langsung ke lingkungan host tanpa terjemahan apa pun.

Standar ini memerlukan perilaku yang identik dalam kasus ini (khususnya, ia mengatakan mengembalikan sesuatu yang int-compatible dari main()harus setara dengan memanggil exit()dengan nilai itu). Masalahnya adalah bahwa OS yang berbeda memiliki konvensi yang berbeda untuk menafsirkan nilai keluar. Pada banyak sistem (BANYAK!), 0 berarti sukses dan yang lainnya gagal. Tetapi pada, katakanlah, VMS, nilai ganjil berarti kesuksesan dan bahkan yang berarti kegagalan. Jika Anda kembali 0 dari main(), pengguna VMS akan melihat pesan buruk tentang pelanggaran akses. Sebenarnya tidak ada pelanggaran akses - itu hanya pesan standar yang terkait dengan kode kegagalan 0.

Kemudian ANSI datang dan memberkati EXIT_SUCCESSdan EXIT_FAILUREsebagai argumen Anda bisa lolos exit(). Standar ini juga mengatakan bahwa exit(0)harus bersikap identik dengan exit(EXIT_SUCCESS), sehingga sebagian besar implementasi mendefinisikan EXIT_SUCCESSuntuk 0.

Standar, oleh karena itu, membuat Anda terikat pada VMS, karena tidak meninggalkan cara standar untuk mengembalikan kode kegagalan yang kebetulan memiliki nilai 0.

Oleh karena itu, kompiler VAX / VMS C awal 1990-an tidak menafsirkan nilai balik dari main() , ia hanya mengembalikan nilai apa pun ke lingkungan host. Tetapi jika Anda menggunakannya exit()akan melakukan apa yang diperlukan standar: menerjemahkan EXIT_SUCCESS(atau 0) menjadi kode sukses dan EXIT_FAILUREmenjadi kode kegagalan umum. Untuk menggunakannya EXIT_SUCCESS, Anda harus meneruskannya exit(), Anda tidak dapat mengembalikannya main(). Saya tidak tahu apakah versi yang lebih modern dari kompiler itu mempertahankan perilaku itu.

Program C portabel digunakan untuk terlihat seperti ini:

#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 */
}

Selain itu: Jika saya mengingat dengan benar, konvensi VMS untuk nilai keluar lebih bernuansa daripada ganjil / genap. Ini sebenarnya menggunakan sesuatu seperti tiga bit rendah untuk mengkodekan tingkat keparahan. Secara umum, tingkat keparahan ganjil mengindikasikan keberhasilan atau informasi lain-lain dan bahkan genap mengindikasikan kesalahan.

Adrian McCarthy
sumber
Beberapa kompiler lama pra-ANSI mungkin telah memperlakukan nilainya returneddengan mainberbeda dari nilai yang diteruskan ke exit- tetapi standar secara khusus mengatakan, "Jika tipe pengembalian mainfungsi adalah tipe yang kompatibel dengan int, pengembalian dari panggilan awal ke mainfungsi adalah setara dengan memanggil exitfungsi dengan nilai yang dikembalikan oleh mainfungsi sebagai argumennya ". Itu C11; C89 / C90 memiliki kata-kata yang hampir sama.
Keith Thompson
Memang. Namun demikian, beberapa kompiler era ANSI tidak mendapatkan hak ini dan mengharuskan penggunaan keluar secara eksplisit untuk mendapatkan nilai pengembalian yang benar dikembalikan ke lingkungan host. Karena standar (bahkan kemudian) mengharuskan 0 diperlakukan sama EXIT_SUCCESS, tidak ada cara untuk mengembalikan status kegagalan spesifik platform dengan nilai 0, yang mungkin mengapa beberapa kompiler pada era diperlakukan kembali-dari-main dan exit()berbeda.
Adrian McCarthy
Apakah Anda memiliki kutipan untuk itu? Masalah terpisah adalah apakah kompiler saat ini memiliki bug tertentu. Jawaban Anda untuk frasa dalam present tense.
Keith Thompson
Itu kritik yang adil. Saya telah mengubah kata-kata untuk membatasi ruang lingkup untuk kasus tertentu yang saya ketahui.
Adrian McCarthy
0

Di C kembali dari mainpersis sama dengan meneleponexit dengan nilai yang sama.

Bagian 5.1.2.2.3 dari status standar C :

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

Aturan untuk C ++ sedikit berbeda seperti yang disebutkan dalam jawaban lain.

dbush
sumber
-1

Sebenarnya ADA perbedaan antara exit(0)dan return(0)di main- ketika mainfungsi Anda dipanggil beberapa kali.

Program berikut

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

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

Jalankan sebagai

./program 0 0 0 0

Akan menghasilkan output sebagai berikut:

00000

Namun yang ini:

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

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

Tidak akan mencetak apa pun terlepas dari argumen.

Jika Anda yakin tidak akan ada yang memanggil Anda mainsecara eksplisit, secara teknis tidak ada perbedaan besar, tetapi untuk mempertahankan kode yang lebih jelas exitakan terlihat jauh lebih baik. Jika Anda karena alasan tertentu ingin menelepon main- Anda harus menyesuaikannya dengan kebutuhan Anda.

Berbicara tentang C.

Radrow
sumber