Rumah > pembangunan bahagian belakang > C++ > Mengapa Kedua-dua return dan exit() Berfungsi dalam main()

Mengapa Kedua-dua return dan exit() Berfungsi dalam main()

DDD
Lepaskan: 2024-11-08 09:37:02
asal
1066 orang telah melayarinya

Why Both return and exit() Work in main()

pengenalan

Dalam pengaturcaraan C, terdapat dua cara untuk menamatkan program daripada fungsi utama: menggunakan return dan menggunakan exit().

int main() {
    printf("Hello, World!");
    return 0;    // Method 1: Normal termination
}

int main() {
    printf("Hello, World!");
    exit(0);     // Method 2:Normal termination
}
Salin selepas log masuk

Mengapakah kedua-dua kaedah boleh menamatkan program dengan betul, walaupun ia kelihatan berbeza?
Dalam artikel ini, kami akan membongkar misteri ini dengan memahami cara program C sebenarnya bermula dan ditamatkan.
Harap maklum bahawa artikel ini memfokuskan pada pelaksanaan dalam persekitaran GNU/Linux, khususnya menggunakan glibc.

Cara exit() Berfungsi

Pertama, mari kita periksa cara fungsi keluar berfungsi untuk memahami mekanisme penamatan program.
Fungsi keluar ialah fungsi perpustakaan standard yang menamatkan program dengan betul.
Secara dalaman, fungsi _exit, yang dipanggil melalui exit, dilaksanakan dalam glibc seperti berikut:

void
_exit (int status)
{
  while (1)
    {
      INLINE_SYSCALL (exit_group, 1, status);

#ifdef ABORT_INSTRUCTION
      ABORT_INSTRUCTION;
#endif
    }
}
Salin selepas log masuk

Melihat pelaksanaan ini, kita dapat melihat bahawa fungsi _exit menerima status keluar sebagai hujahnya dan memanggil exit_group (nombor panggilan sistem 231).

Panggilan sistem ini melaksanakan operasi berikut:

  1. Menghantar pemberitahuan penamatan program ke kernel
  2. Inti menjalankan operasi pembersihan:
    • Mengeluarkan sumber yang digunakan oleh proses
    • Mengemas kini jadual proses
    • Melakukan prosedur pembersihan tambahan

Melalui operasi ini, program ditamatkan dengan betul.

Jadi, mengapakah kembali dari main() juga menamatkan program dengan betul?

Titik Kemasukan Tersembunyi Program C

Untuk memahami perkara ini, kita perlu mengetahui fakta penting: program C sebenarnya tidak bermula dari utama.

Mari kita semak tetapan lalai pemaut (ld) untuk melihat titik masuk sebenar:

$ ld --verbose | grep "ENTRY"
ENTRY(_start)
Salin selepas log masuk

Seperti yang ditunjukkan oleh output ini, titik kemasukan sebenar program C ialah fungsi _start. utama dipanggil selepas _start.
Fungsi _start dilaksanakan dalam perpustakaan standard, dan dalam glibc, ia kelihatan seperti ini:

_start:
    # Initialize stack pointer
    xorl %ebp, %ebp
    popq %rsi        # Get argc
    movq %rsp, %rdx  # Get argv

    # Setup arguments for main
    pushq %rsi       # Push argc
    pushq %rdx       # Push argv

    # Call __libc_start_main
    call __libc_start_main
Salin selepas log masuk

Fungsi _start mempunyai dua peranan utama:

  1. Memulakan bingkai tindanan yang diperlukan untuk pelaksanaan program
  2. Sediakan hujah baris arahan (argc, argv) untuk fungsi utama

Selepas permulaan ini selesai, __libc_start_main dipanggil.
Fungsi ini bertanggungjawab untuk memanggil fungsi utama.

Sekarang, mari kita periksa cara __libc_start_main berfungsi secara terperinci.

Bagaimana __libc_start_main Membuat pulangan Berfungsi

__libc_start_call_main, yang dipanggil oleh __libc_start_main, dilaksanakan seperti berikut:

_Noreturn static void
__libc_start_call_main (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
                        int argc, char **argv
#ifdef LIBC_START_MAIN_AUXVEC_ARG
                            , ElfW(auxv_t) *auxvec
#endif
                        )
{
  int result;

  /* Memory for the cancellation buffer.  */
  struct pthread_unwind_buf unwind_buf;

  int not_first_call;
  DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
  /* This call results in a -Wstringop-overflow warning because struct
     pthread_unwind_buf is smaller than jmp_buf.  setjmp and longjmp
     do not use anything beyond the common prefix (they never access
     the saved signal mask), so that is a false positive.  */
  DIAG_IGNORE_NEEDS_COMMENT (11, "-Wstringop-overflow=");
#endif
  not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf);
  DIAG_POP_NEEDS_COMMENT;
  if (__glibc_likely (! not_first_call))
    {
      struct pthread *self = THREAD_SELF;

      /* Store old info.  */
      unwind_buf.priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf);
      unwind_buf.priv.data.cleanup = THREAD_GETMEM (self, cleanup);

      /* Store the new cleanup handler info.  */
      THREAD_SETMEM (self, cleanup_jmp_buf, &unwind_buf);

      /* Run the program.  */
      result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
    }
  else
    {
      /* Remove the thread-local data.  */
      __nptl_deallocate_tsd ();

      /* One less thread.  Decrement the counter.  If it is zero we
         terminate the entire process.  */
      result = 0;
      if (atomic_fetch_add_relaxed (&__nptl_nthreads, -1) != 1)
        /* Not much left to do but to exit the thread, not the process.  */
    while (1)
      INTERNAL_SYSCALL_CALL (exit, 0);
    }

  exit (result);
}
Salin selepas log masuk

Dalam pelaksanaan ini, bahagian utama yang perlu diberi tumpuan adalah seperti berikut:

result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
exit(result);
Salin selepas log masuk

Di sini, perkara penting ialah bagaimana fungsi utama dilaksanakan dan nilai pulangannya dikendalikan:

  1. Melaksanakan fungsi utama dan menyimpan nilai pulangannya dalam hasil
  2. Menggunakan nilai pulangan daripada utama sebagai hujah untuk keluar

Melalui mekanisme ini:

  • Apabila menggunakan return in main → Nilai pulangan dihantar ke __libc_start_main, yang kemudiannya meneruskannya untuk keluar
  • Apabila exit() dipanggil terus di utama → Program ditamatkan serta-merta

Dalam mana-mana kes, keluar akhirnya dipanggil, memastikan penamatan program yang betul.

Kesimpulan

Program C mempunyai mekanisme berikut:

  1. Program bermula dari _start
  2. _mula bersiap untuk pelaksanaan utama
  3. utama dilaksanakan melalui __libc_start_main
  4. Menerima nilai pulangan utama dan menggunakannya sebagai hujah untuk keluar

Melalui mekanisme ini:

  • Walaupun apabila menggunakan pulangan dalam utama, nilai pulangan dihantar secara automatik untuk keluar
  • Akibatnya, kedua-dua return dan exit() menamatkan program dengan betul

Perhatikan bahawa mekanisme ini tidak terhad kepada GNU/Linux; pelaksanaan serupa wujud dalam sistem pengendalian lain (seperti Windows dan macOS) dan pustaka standard C yang berbeza.

Atas ialah kandungan terperinci Mengapa Kedua-dua return dan exit() Berfungsi dalam main(). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan