exit與return的深入辨析



1. exit用於結束正在運行的整個程序,它將參數返回給OS,把控制權交給操作系統;而return 是退出當前函數,返回函數值,把控制權交給調用函數
2. exit是系統調用級別,它表示一個進程的結束;而return 是語言級別的,它表示調用堆棧的返回。
3. 在main函數結束時,會隱式地調用exit函數,所以一般程序執行到main()結尾時,則結束主進程。exit將刪除進程使用的內存空間,同時把錯誤信息返回給父進程。
4. void exit(int status); 一般status爲0,表示正常退出,非0表示非正常退出。

1、exit函數和return函數的主要區別是:

1)exit用於在程序運行的過程中隨時結束程序,其參數是返回給OS的。也可以這麼講:exit函數是退出應用程序,並將應用程序的一個狀態返回給OS,這個狀態標識了應用程序的一些運行信息。

main函數結束時也會隱式地調用exit函數,exit函數運行時首先會執行由atexit()函數登記的函數,然後會做一些自身的清理工作,同時刷新所有輸出流、關閉所有打開的流並且關閉通過標準I/O函數tmpfile()創建的臨時文件。

exit是系統調用級別的,它表示了一個進程的結束,它將刪除進程使用的內存空間,同時把錯誤信息返回父進程。通常情況:exit(0)表示程序正常, exit(1)和exit(-1)表示程序異常退出,exit(2)表示系統找不到指定的文件。在整個程序中,只要調用exit就結束。

 

2)return是語言級別的,它表示了調用堆棧的返回;return是返回函數值並退出函數,通常0爲正常退出,非0爲非正常退出,請注意,如果是在主函數main, 自然也就結束當前進程了(也就是說,在main()裏面,你可以用return n,也能夠直接用exit(n)來做),如果不是在main函數中,那就是退回上一層調用。在多個進程時,如果有時要檢測上個進程是否正常退出,就要用到上個進程的返回值。

 

2、進程環境與進程控制

exit(int n)其實就是直接退出程序,因爲默認的標準程序入口爲 int main(int argc, char** argv),返回值是int型的。一般在shell下面,運行一個程序,然後使用命令echo $?就能得到該程序的返回值,也就是退出值。

理論上exit可以返回小於256的任何整數,返回的不同數值主要是給調用者作不同處理的。

對於單獨的進程exit的返回值是返回給操作系統的,但如果是多進程,則是返回給父進程的。父進程裏面調用waitpid()等函數得到子進程退出的狀態,以便作不同處理。根據相應的返回值來讓調用者作出相應的處理。

總的說來,exit()就是當前進程把其控制權返回給調用該子程序的主程序, 括號裏的是返回值,告訴調用程序該程序的運行狀態。

 

1)進程的開始:

C程序是從main函數開始執行, 原型如下: int main(int argc, char *argv[]); 通常main的返回值是int型, 正確返回0。 

2)進程終止: 

C程序的終止分爲兩種: 正常終止和異常終止。正常終止分爲: return, exit, _exit, _Exit, pthreade_exit。異常中指分爲: abort, SIGNAL, 線程響應取消。

主要說一下正常終止的前4種, 即exit系列函數.

#include <stdlib.h>  

void exit(int status);

void _Exit(int status);

 

#include <unistd.h>  

void _exit(int status);

以上3個函數的區別是: exit()(或return 0)會調用終止處理程序和用戶空間的標準I/O清理程序(如fclose), _exit和_Exit不調用而直接由內核接管進行清理。因此, 在main函數中exit(0)等價於return 0.

 

3)atexit終止處理程序:

ISO C規定, 一個進程最多可登記32個終止處理函數, 這些函數由exit按登記相反的順序自動調用。如果同一函數登記多次, 也會被調用多次。

 

原型如下: 

#include <stdlib.h>

int atexit(void (*func)(void));

 

其中參數是一個函數指針, 指向終止處理函數, 該函數無參無返回值。atexit函數本身成功調用後返回0。

 

以下面的程序爲例:

 

#include <stdlib.h> 

static void myexit1()

{

     printf("first exit handler\n");

static void myexit2()

{

     printf("second exit handler\n");

int main()

{

     atexit(my_exit2);

     atexit(my_exit1);

atexit(my_exit1);

     printf("main is done\n");

     return 0;

}

 

運行結果:

$ ./a.out

main is done

first exit handler

first exit handler

second exit handler

    注意上面的結果,可以發現這些函數由exit按登記相反的順序自動調用(先myexit1後myexit2)。如果同一函數登記多次, 也會被調用多次(如這裏的myexit1)。而這些處理函數都是在程序退出的時候利用atexit函數調用了這些處理函數。但是如果用_exit()退出程序,則它不關閉任何文件,不清除任何緩衝器、也不調用任何終止函數!


exit函數在頭文件stdlib.h中

exit(0):正常運行程序並退出程序;

exit(1):非正常運行導致退出程序;

return():返回函數,若在main主函數中,則會退出函數並返回一值,可以寫爲return(0),或return 0。

詳細說:

  1. return返回函數值,是關鍵字;exit是一個函數

  2. return是語言級別的,它表示了調用堆棧的返回;而exit是系統調用級別的,它表示了一個進程的結束。
  3. return是函數的退出(返回);exit是進程的退出。

  4. return是C語言提供的,exit是操作系統提供的(或者函數庫中給出的)。

  5. return用於結束一個函數的執行,將函數的執行信息傳出個其他調用函數使用;exit函數是退出應用程序,刪除進程使用的內存空間,並將應用程序的一個狀態返回給OS,這個狀態標識了應用程序的一些運行信息,這個信息和機器和操作系統有關,一般是 0 爲正常退出,非0 爲非正常退出。

  6. 非主函數中調用return和exit效果很明顯,但是在main函數中調用return和exit的現象就很模糊,多數情況下現象都是一致的。

 下面是幾個例子:

1.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
  
int main(void)
{
  pid_t pid;
  int count=0;
  
  pid=vfork();
  if(pid==0)
  {
    printf("child: count=%d\n",count);
    printf("child: getpid=%d\n",getpid());
    count=1;
    printf("child: count=%d\n",count);
    // return 0;//會出現段錯誤
    exit(0); //ok
  }
  else
  {
    printf("\nfather: pid=%d\n",pid);
    printf("father: count=%d\n",count);
  }
  return(0);
}

 

運行結果

[root@localhost part1_linux]# gcc fork2.c 
[root@localhost part1_linux]# ./a.out 
child: count=0
child: getpid=9911
child: count=1
  
father: pid=9911
father: count=1

運行結果說明:vfrok時父、子進程共享數據段,fork時是進行拷貝。如果,vfork子進程中,使用return返回時,出現段錯誤,結果如下:

[root@localhost part1_linux]# gcc fork2.c 
[root@localhost part1_linux]# ./a.out 
child: count=0
child: getpid=10864
child: count=1
  
father: pid=10864
father: count=0
段錯誤
2. 爲什麼執行結果子進程打印出來 我的父親是id:1,與父進程id不同
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
  
int main()
{
    int i=0;
    pid_t pid;
    printf("還沒創建子進程\n");
    i++;
    pid = fork();
    if(pid==-1)
    {
      printf("fork error!\n");
    }
    else if(pid==0)
    {
      i++;
      printf("我是子進程,id%d\n",getpid());
      printf("我的父親是id:%d\n",getppid());
      printf("-----i=%d-----\n",i);
    }
    else
    {
      i++;
      printf("我是父進程,id:%d\n",getpid());
      printf("-----i=%d-----\n",i);
    }
    exit(0);
}
<span style="font-size:14px;">子進程在打印第一句時,父進程也在打印第一句,但是子進程在執行第二句時,父進程已經直接over了(這只是個簡單的說法,實際過程可能並不如此,我要說的是,父進程先於子進程的打印語句之前就結束)。因此此時的子進程成了孤兒進程,會被init也就是1號進程領養,成爲init的子進程。 爲了避免這樣的情況,父進程最後可以執行wait來等待子進程的返回。</span>
3. 用vfork()創建子進程,執行後程序一直不斷地重複運行,不斷創建子進程,結尾用exit(0)代替return(0)後問題就能解決
<span style="font-size:14px;">return 0在一個函數中是正常的返回過程,它會使得程序返回到函數被調用處,回覆之前的執行流程,return 語句不會被執行。而exit 一般是在任意位置和使用的,執行到exit 0時,整個進程就over了(這也是爲什麼在多線程程序中不要隨意使用exit的原因),用來使程序退出運行,一般用來處理(不可挽回的)錯誤狀態。</span>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
  
int main()
{
  int i=0;
  pid_t pid;
  printf("還沒創建子進程\n");
  i++;
  pid = vfork();
  if(pid==-1)
  {
    printf("fork error!\n");
  }
  else if(pid==0)
  {
    i++;
    printf("我是子進程,id%d\n",getpid());
    printf("我的父親是id:%d\n",getppid());
    printf("-----i=%d-----\n",i);
  }
  else
  {
    i++;
    printf("我是父進程,id:%d\n",getpid());
    printf("-----i=%d-----\n",i);
  }
  return(0); 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章