C語言深度總結(jié)[全面認識main函數(shù)之后運行代碼]

一、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演示

如來在手,bug遠去

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),除零,類型錯誤等等。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容