日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 21ic電子網
[導讀]main的返回值 main函數的返回值用于說明程序的退出狀態(tài)。如果返回0,則代表程序正常退出。返回其它數字的含義則由系統(tǒng)決定。通常,返回非零代表程序異常退出。

main的返回值

main函數的返回值用于說明程序的退出狀態(tài)。如果返回0,則代表程序正常退出。返回其它數字的含義則由系統(tǒng)決定。通常,返回非零代表程序異常退出。

void main()

有一些書上的,都使用了void main( ) ,其實這是錯誤的。C/C++ 中從來沒有定義過void main( ) 。

C++ 之父 Bjarne Stroustrup 在他的主頁上的 FAQ 中明確地寫著 “The definition void main( ) { /* … */ } is not and never has been C++, nor has it even been C.” 這可能是因為 在 C 和 C++ 中,不接收任何參數也不返回任何信息的函數原型為“void foo(void);”。

可能正是因為這個,所以很多人都誤認為如果不需要程序返回值時可以把main函數定義成void main(void) 。然而這是錯誤的!main 函數的返回值應該定義為 int 類型,C 和 C++ 標準中都是這樣規(guī)定的。

雖然在一些編譯器中,void main() 可以通過編譯,但并非所有編譯器都支持 void main() ,因為標準中從來沒有定義過 void main 。

g++3.2 中如果 main 函數的返回值不是 int 類型,就根本通不過編譯。而 gcc3.2 則會發(fā)出警告。所以,為了程序擁有很好的可移植性,一定要用 int main ()。測試如下:

#include?

void?main()
{
????printf("Hello?world\n");
????return;
}

運行結果:g++ test.c

干貨!深度剖析C語言的main函數

main()

那既然main函數只有一種返回值類型,那么是不是可以不寫?規(guī)定:不明確標明返回值的,默認返回值為int,也就是說 main()等同于int main(),而不是等同于void main()。

在C99中,標準要求編譯器至少給 main() 這種用法來個警告,而在c89中這種寫法是被允許的。但為了程序的規(guī)范性和可讀性,還是應該明確的指出返回值的類型。測試代碼:

#include?

main()
{
????printf("Hello?world\n");
????return?0;
}

運行結果:

干貨!深度剖析C語言的main函數

C和C++的標準

在 C99 標準中,只有以下兩種定義方式是正確的:

int?main(?void?)?
int?main(?int?argc,?char?*argv[]?)?

若不需要從命令行中獲取參數,就使用int main(void) ;否則的話,就用int main( int argc, char *argv[] )。當然參數的傳遞還可以有其他的方式,在下一節(jié)中,會單獨來講。

main 函數的返回值類型必須是 int ,這樣返回值才能傳遞給程序的調用者(如操作系統(tǒng)),等同于 exit(0),來判斷函數的執(zhí)行結果。

C++89中定義了如下兩種 main 函數的定義方式:

int?main(?)?
int?main(?int?argc,?char?*argv[]?)?

int main( ) 等同于 C99 中的 int main( void ) ;int main( int argc, char*argv[] ) 的用法也和C99 中定義的一樣。同樣,main函數的返回值類型也必須是int。

return 語句

如果 main 函數的最后沒有寫 return 語句的話,C99 和c++89都規(guī)定編譯器要自動在生成的目標文件中加入return 0,表示程序正常退出。

不過,建議你最好在main函數的最后加上return語句,雖然沒有這個必要,但這是一個好的習慣。在linux下我們可以使用shell命令:echo $? 查看函數的返回值。

#include?

int?main()
{
????printf("Hello?world\n");
}

運行結果:

干貨!深度剖析C語言的main函數

同時,需要說明的是return的返回值會進行 類型轉換,比如:若return 1.2 ;會將其強制轉換為1,即真正的返回值是1,同理,return ‘a’ ;的話,真正的返回值就是97,;但是若return “abc”;便會報警告,因為無法進行隱式類型轉換。

測試main函數返回值的意義

前文說到,main函數如果返回0,則代表程序正常退出。通常,返回非零代表程序異常退出。在本文的最后,測試一下:  test.c:

#include?

int?main()
{
????printf("c?語言\n");
????return?11.1;?
}

在終端執(zhí)行如下:

??testSigpipe?git:(master)??vim?test.c
??testSigpipe?git:(master)??gcc?test.c
??testSigpipe?git:(master)??./a.out?&&?echo?"hello?world"??#&&與運算,前面為真,才會執(zhí)行后邊的
c?語言

可以看出,操作系統(tǒng)認為main函數執(zhí)行失敗,因為main函數的返回值是11

??testSigpipe?git:(master)??./a.out?
??testSigpipe?git:(master)??echo?$?
11

若將main函數中返回值該為0的話:

??testSigpipe?git:(master)??vim?test.c
??testSigpipe?git:(master)??gcc?test.c?
??testSigpipe?git:(master)??./a.out?&&?echo?"hello?world"?#hello
c?語言
hello?world

可以看出,正如我們所期望的一樣,main函數返回0,代表函數正常退出,執(zhí)行成功;返回非0,代表函數出先異常,執(zhí)行失敗。

main函數傳參

首先說明的是,可能有些人認為main函數是不可傳入參數的,但是實際上這是錯誤的。main函數可以從命令行獲取參數,從而提高代碼的復用性。

函數原形

為main函數傳參時,可選的main函數原形為:

int?main(int?argc?,?char*?argv[],char*?envp[]);

參數說明:

①、第一個參數argc表示的是傳入參數的個數 。

②、第二個參數char* argv[],是字符串數組,用來存放指向的字符串參數的指針數組,每一個元素指向一個參數。各成員含義如下:

argv[0]:指向程序運行的全路徑名。

argv[1]:指向執(zhí)行程序名后的第一個字符串 ,表示真正傳入的第一個參數。

argv[2]:指向執(zhí)行程序名后的第二個字符串 ,表示傳入的第二個參數。

…… argv[n]:指向執(zhí)行程序名后的第n個字符串 ,表示傳入的第n個參數。

規(guī)定:argv[argc]為NULL ,表示參數的結尾。

③、第三個參數char* envp[],也是一個字符串數組,主要是保存這用戶環(huán)境中的變量字符串,以NULL結束。envp[]的每一個元素都包含ENVVAR=value形式的字符串,其中ENVVAR為環(huán)境變量,value為其對應的值。

envp一旦傳入,它就只是單純的字符串數組而已,不會隨著程序動態(tài)設置發(fā)生改變??梢允褂胮utenv函數實時修改環(huán)境變量,也能使用getenv實時查看環(huán)境變量,但是envp本身不會發(fā)生改變;平時使用到的比較少。

注意:main函數的參數char* argv[]和char* envp[]表示的是字符串數組,書寫形式不止char* argv[]這一種,相應的argv[][]和 char** argv均可。

char* envp[]

寫個小測試程序,測試main函數的第三個參數:

#include?

int?main(int?argc?,char*?argv[]?,char*?envp[])
{
????int?i?=?0;

????while(envp[i++])
????{
????????printf("%s\n",?envp[i]);
????}

????return?0;
}

運行結果:部分截圖

干貨!深度剖析C語言的main函數

envp[] 獲得的信息等同于Linux下env命令的結果。

常用版本

在使用main函數的帶參版本的時,最常用的就是:**int main(int argc , char* argv[]);**變量名稱argc和argv是常規(guī)的名稱,當然也可以換成其他名稱。

命令行執(zhí)行的形式為:可執(zhí)行文件名 參數1 參數2 … … 參數n??蓤?zhí)行文件名稱和參數、參數之間均使用空格隔開。

示例程序

#include?

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

????int?i;
????printf("Total?%d?arguments\n",argc);

????for(i?=?0;?i?????{
????????printf("\nArgument?argv[%d]??=?%s?\n",i,?argv[i]);
????}

????return?0;
}

運行結果:

??cpp_workspace?git:(master)??vim?testmain.c?
??cpp_workspace?git:(master)??gcc?testmain.c?
??cpp_workspace?git:(master)??./a.out?1?2?3????#./a.out為程序名?1為第一個參數?,?2?為第二個參數,?3?為第三個參數
Total?4?arguments
Argument?argv[0]??=?./a.out?
Argument?argv[1]??=?1?
Argument?argv[2]??=?2?
Argument?argv[3]??=?3?
Argument?argv[4]??=?(null)????#默認argv[argc]為null

main的執(zhí)行順序

可能有的人會說,這還用說,main函數肯定是程序執(zhí)行的第一個函數。那么,事實果然如此嗎?相信在看了本節(jié)之后,會有不一樣的認識。

為什么說main()是程序的入口

linux系統(tǒng)下程序的入口是”_start”,這個函數是linux系統(tǒng)庫(Glibc)的一部分,當我們的程序和Glibc鏈接在一起形成最終的可執(zhí)行文件的之后,這個函數就是程序執(zhí)行初始化的入口函數。通過一個測試程序來說明:

#include?

int?main()
{
????printf("Hello?world\n");
????return?0;
}

編譯:

gcc testmain.c -nostdlib     # -nostdlib (不鏈接標準庫)

程序執(zhí)行會引發(fā)錯誤:/usr/bin/ld: warning: cannot find entry symbol _start; 未找到這個符號

所以說:

  1. 編譯器缺省是找 __start 符號,而不是 main
  2. __start 這個符號是程序的起始
  3. main 是被標準庫調用的一個符號

那么,這個_start和main函數有什么關系呢?下面我們來進行進一步探究。

_start函數的實現(xiàn)該入口是由ld鏈接器默認的鏈接腳本指定的,當然用戶也可以通過參數進行設定。_start由匯編代碼實現(xiàn)。大致用如下偽代碼表示:

void?_start()
{
??%ebp?=?0;
??int?argc?=?pop?from?stack
??char?**?argv?=?top?of?stack;
??__libc_start_main(main,?argc,?argv,?__libc_csu_init,?__linc_csu_fini,
??edx,?top?of?stack);
}

對應的匯編代碼如下:

_start:
?xor?ebp,?ebp?//清空ebp
?pop?esi?//保存argc,esi?=?argc
?mov?esp,?ecx?//保存argv,?ecx?=?argv

?push?esp?//參數7保存當前棧頂
?push?edx?//參數6
?push?__libc_csu_fini//參數5
?push?__libc_csu_init//參數4
?push?ecx?//參數3
?push?esi?//參數2
?push?main//參數1
?call?_libc_start_main

hlt

可以看出,在調用_start之前,裝載器就會將用戶的參數和環(huán)境變量壓入棧中。

main函數運行之前的工作

從_start的實現(xiàn)可以看出,main函數執(zhí)行之前還要做一系列的工作。主要就是初始化系統(tǒng)相關資源:

Some?of?the?stuff?that?has?to?happen?before?main():

set?up?initial?stack?pointer?

initialize?static?and?global?data?

zero?out?uninitialized?data?

run?global?constructors

Some?of?this?comes?with?the?runtime?library's?crt0.o?file?or?its?__start()?function.?Some?of?it?you?need?to?do?yourself.

Crt0?is?a?synonym?for?the?C?runtime?library.

1.設置棧指針

2.初始化static靜態(tài)和global全局變量,即data段的內容

3.將未初始化部分的賦初值:數值型short,int,long等為0,bool為FALSE,指針為NULL,等等,即.bss段的內容

4.運行全局構造器,類似c++中全局構造函數

5.將main函數的參數,argc,argv等傳遞給main函數,然后才真正運行main函數

main之前運行的代碼

下面,我們就來說說在mian函數執(zhí)行之前到底會運行哪些代碼:(1)全局對象的構造函數會在main 函數之前執(zhí)行。

(2)一些全局變量、對象和靜態(tài)變量、對象的空間分配和賦初值就是在執(zhí)行main函數之前,而main函數執(zhí)行完后,還要去執(zhí)行一些諸如釋放空間、釋放資源使用權等操作

(3)進程啟動后,要執(zhí)行一些初始化代碼(如設置環(huán)境變量等),然后跳轉到main執(zhí)行。全局對象的構造也在main之前。

(4)通過關鍵字attribute,讓一個函數在主函數之前運行,進行一些數據初始化、模塊加載驗證等。

示例代碼

①、通過關鍵字attribute

#include?

__attribute__((constructor))?void?before_main_to_run()?
{?
????printf("Hi~,i?am?called?before?the?main?function!\n");
????printf("%s\n",__FUNCTION__);?
}?

__attribute__((destructor))?void?after_main_to_run()?
{?
????printf("%s\n",__FUNCTION__);?
????printf("Hi~,i?am?called?after?the?main?function!\n");
}?

int?main(?int?argc,?char?**?argv?)?
{?
????printf("i?am?main?function,?and?i?can?get?my?name(%s)?by?this?way.\n",__FUNCTION__);?
????return?0;?
}

②、全局變量的初始化

#include?

using?namespace?std;

inline?int?startup_1()
{
????cout<<"startup_1?run"<<endl;
????return?0;
}

int?static?no_use_variable_startup_1?=?startup_1();

int?main(int?argc,?const?char?*?argv[])?
{
????cout<<"this?is?main"<<endl;
????return?0;
}

至此,我們就聊完了main函數執(zhí)行之前的事情,那么,你是否還以為main函數也是程序運行的最后一個函數呢?

結果當然不是,在main函數運行之后還有其他函數可以執(zhí)行,main函數執(zhí)行完畢之后,返回到入口函數,入口函數進行清理工作,包括全局變量析構、堆銷毀、關閉I/O等,然后進行系統(tǒng)調用結束進程。

main函數之后執(zhí)行的函數

1、全局對象的析構函數會在main函數之后執(zhí)行; 2、用atexit注冊的函數也會在main之后執(zhí)行。

atexit函數

原形:

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

atexit 函數可以“注冊”一個函數,使這個函數將在main函數正常終止時被調用,當程序異常終止時,通過它注冊的函數并不會被調用。

編譯器必須至少允許程序員注冊32個函數。如果注冊成功,atexit 返回0,否則返回非零值,沒有辦法取消一個函數的注冊。

在 exit 所執(zhí)行的任何標準清理操作之前,被注冊的函數按照與注冊順序相反的順序被依次調用。每個被調用的函數不接受任何參數,并且返回類型是 void。被注冊的函數不應該試圖引用任何存儲類別為 auto 或 register 的對象(例如通過指針),除非是它自己所定義的。

多次注冊同一個函數將導致這個函數被多次調用。函數調用的最后的操作就是出棧過程。main()同樣也是一個函數,在結束時,按出棧的順序調用使用atexit函數注冊的,所以說,函數atexit是注冊的函數和函數入棧出棧一樣,是先進后出的,先注冊的后執(zhí)行。通過atexit可以注冊回調清理函數??梢栽谶@些函數中加入一些清理工作,比如內存釋放、關閉打開的文件、關閉socket描述符、釋放鎖等等。

#include
#include

void?fn0(?void?),?fn1(?void?),?fn2(?void?),?fn3(?void?),?fn4(?void?);

int?main(?void?)

{
??//注意使用atexit注冊的函數的執(zhí)行順序:先注冊的后執(zhí)行
????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(?"first?register?,last?call\n"?);
}

void?fn1(
{
????printf(?"next.\n"?);
}

void?fn2()
{
????printf(?"executed?"?);
}

void?fn3()
{
????printf(?"is?"?);
}

void?fn4()
{
????printf(?"This?"?);
}


最后

以上就是本次的分享,如果覺得文章不錯,轉發(fā)、在看,也是我們繼續(xù)更新的動力。



來源:https://blog.csdn.net/z_ryan/category_7316855.html

作者z_ryan


版權歸原作者所有,如有侵權,請聯(lián)系刪除。

干貨!深度剖析C語言的main函數

免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

21ic電子網

掃描二維碼,關注更多精彩內容

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統(tǒng) 散熱

根據LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統(tǒng)中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現(xiàn)代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉