一、main結(jié)束不代表整個進程結(jié)束
(1)全局對象的析構(gòu)函數(shù)會在main函數(shù)之后執(zhí)行;
?(2)用atexit注冊的函數(shù)也會在main之后執(zhí)行。
二、用atexit注冊的函數(shù)會在main結(jié)束之后執(zhí)行
#include<stdio.h>
#include<stdlib.h>
void fn1(void)
{
printf("next.\n");
}
void fn2(void)
{
printf("executed ");
}
void fn3(void)
{
printf("is ");
}
void fn4(void)
{
printf("This ");
}
int main(void)
{
//
// 注冊需要在 main 函數(shù)結(jié)束后執(zhí)行的函數(shù).?
// 請注意它們的注冊順序和執(zhí)行順序
// 在 main 函數(shù)結(jié)束后被調(diào)用,調(diào)用順序與注冊順序相反。 先注冊后執(zhí)行。
//
atexit(fn1);
atexit(fn2);
atexit(fn3);
atexit(fn4);
// 這條輸出語句具有參照性,它可不是最后一句輸出.
puts("This is executed first.");
// EXIT_SUCCESS 代表 0,它定義在 stdlib.h 中。第一節(jié)課里也介紹了
puts("老鐵,感謝有緣一起學(xué)習(xí)C語言,除了跟著這套課程走,為了更好地解決大家實操中的問題,可以加QQ群676593534,以便及時交流。群里還有很多精致資料哦,我這這里等你!");
return EXIT_SUCCESS;
}
關(guān)于atexit函數(shù)稍微補充一下,原型如下:
int?atexit(void?(*func)(void));??
? atexit 函數(shù)是標(biāo)準(zhǔn) C 新增的。它“注冊”一個函數(shù),使這個函數(shù)將在 exit 函數(shù)被調(diào)用時或者當(dāng) mian 函數(shù)返回時被調(diào)用。當(dāng)程序異常終止時(例如調(diào)用 abort 或 raise),通過它注冊的函數(shù)并不會被調(diào)用。編譯器必須至少允許程序員注冊32個函數(shù)。如果注冊成功,atexit 返回0,否則返回非零值。
沒有辦法取消一個函數(shù)的注冊。在 exit 所執(zhí)行的任何標(biāo)準(zhǔn)清理操作之前,被注冊的函數(shù)按照與注冊順序相反的順序被依次調(diào)用。每個被調(diào)用的函數(shù)不接受任何參數(shù),并且返回類型是 void。
被注冊的函數(shù)不應(yīng)該試圖引用任何存儲類別為 auto 或 register 的對象(例如通過指針),除非是它自己所定義的。多次注冊同一個函數(shù)將導(dǎo)致這個函數(shù)被多次調(diào)用。
?? atexit是注冊后進先出的函數(shù),和函數(shù)入棧出棧是一樣的。
?? 在這里注冊了四個函數(shù),理解為入棧的順序為fn1() -> fn2() -> fn3() -> fn4();出棧的順序正好相反,而什么時候出棧呢?就是在調(diào)用函數(shù)結(jié)束時,準(zhǔn)確的說應(yīng)該是函數(shù)調(diào)用的最后的操作就是出棧過程。main()同樣也是一個函數(shù),在結(jié)束時,按出棧的順序調(diào)用四個函數(shù),即為fn4() -> fn3() -> fn2() -> fn1();
Demo演示

P.S簡書這個不友好的編輯器,無法把我心中的如來佛祖完美演繹出來。想把佛祖請到你家,給我留言即可
#include<stdio.h>
#include<stdlib.h>
/**
?*? ? ? ? ? ? ? ? ? ? _ooOoo_
?*? ? ? ? ? ? ? ? ? o8888888o
?*? ? ? ? ? ? ? ? ? 88" . "88
?*? ? ? ? ? ? ? ? ? (| -_- |)
?*? ? ? ? ? ? ? ? ? ? O\ = /O
?*? ? ? ? ? ? ? ? ____/`---'\____
?*? ? ? ? ? ? ? .? ' \\| |// `.
?*? ? ? ? ? ? ? / \\||| : |||// \
?*? ? ? ? ? ? / _||||| -:- |||||- \
?*? ? ? ? ? ? ? | | \\\ - /// | |
?*? ? ? ? ? ? | \_| ''\---/'' | |
?*? ? ? ? ? ? ? \ .-\__ `-` ___/-. /
?*? ? ? ? ? ___`. .' /--.--\ `. . __
?*? ? ? ? ."" '< `.___\_<|>_/___.' >'"".
?*? ? ? | | : `- \`.;`\ _ /`;.`/ - ` : | |
?*? ? ? ? \ \ `-. \_ __\ /__ _/ .-` / /
?* ======`-.____`-.___\_____/___.-`____.-'======
?*? ? ? ? ? ? ? ? ? ? `=---='
?*
?* .............................................
?*? ? ? ? ? 佛祖保佑? ? ? ? ? ? 永無BUG
?*/
void fn0( void ), fn1( void ), fn2( void ), fn3( void ), fn4( void );
int main( void )
{
atexit( fn0 );
atexit( fn1 );
atexit( fn2 );
atexit( fn3 );
atexit( fn4 );
printf( "This is executed first.\n" );
? ? printf("main will quit now!\n");
return 0;
}
void fn0()
{
printf( "哎呦,我的小心臟啊,嚇?biāo)缹殞毩?。竟然還真可以這樣玩,扶我起來,我要繼續(xù)學(xué)習(xí)!\n" );
}
void fn1()
{
printf( "next.\n" );
}
void fn2()
{
printf( "executed " );
}
void fn3()
{
printf( "is " );
}
void fn4()
{
printf( "This " );
}
總結(jié):
1.main函數(shù)不是可運行的最后一個函數(shù)。
2.由于代碼在任何一個地方都有可能退出(主動和被動),通過atexit可以注冊回調(diào)清理函數(shù)。注冊這個函數(shù)的目的就是為了在函數(shù)退出時調(diào)用的,即使是main()函數(shù)也是這樣的??梢栽谶@些函數(shù)中加入一些清理工作,比如內(nèi)存釋放、關(guān)閉打開的文件、關(guān)閉socket描述符、釋放鎖等等。
3.atexit是注冊后進先出的函數(shù),和函數(shù)入棧出棧是一樣的。
注:這里的主動退出是指,程序調(diào)用諸如exit、_exit、pthread_exit等函數(shù)。被動退出是指,發(fā)生不可預(yù)知的異常導(dǎo)致,如訪問非法內(nèi)存,訪問越界,OOM(out of memery),除零,類型錯誤等等。