2016-07-23 91 views
1

我正在閱讀教程,我已經閱讀那裏,我們不能定義void main()作爲原型已經定義爲int main() or int main(int argc , char *argv)這些只是兩個有效的方式來定義C中的主要功能。 所以在哪個頭文件或庫文件這些原型設置爲 我不知道如何編譯器給出錯誤float main()後面的機制是什麼,我的意思是它是一個語法錯誤或爲main()定義的一些原型?c或C++中main()函數的原型?

請用簡單的語言給出答案。

+0

這裏沒有任何原型。這個限制是編譯器本身的一部分。 – HolyBlackCat

+1

「這些只是C中定義主函數的兩種有效方式」 - > _almost_。這些(和等價物)只有兩個_defined_有效的方法。其他可能存在的每個實施可能會失去可移植性, – chux

+0

因爲每個發佈的答案都包含事實錯誤,所以會投票刪除投票。所以這篇文章造成的傷害比好的更多。如果沒有錯誤地引用這些標準,請參閱標準實際所說的內容[閱讀本文](https://stackoverflow.com/a/31263079/584518)。 – Lundin

回答

-1

棒的標準形式,

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

int main(void) 

,如果你從一個編譯器的移動程序到另一個你不會遇到問題。
這就是說,ISO/IEC 9899:201X - > 5.1.2.2.1(程序啓動)啓動狀態:

稱爲在程序啓動的功能被命名爲主力。 實現聲明沒有原型這個函數。它應 與的INT返回類型和不帶參數進行定義:

int main(void) { /* ... */ } 

或兩個參數(這裏稱爲argc和argv,雖然任何 名稱可以使用,因爲它們是地方於它們是 聲明的功能):

int main(int argc, char *argv[]) { /* ... */ } 

或等同物; 10)或某種其他實施方式定義的 方式。 。


10)因此,int可以被定義爲int的 typedef名稱替代,或者argv的類型可以被寫爲 char ** argv,依此類推。

+0

問題中沒有提到託管環境。您通過引用託管環境的子章節來錯誤地引用標準,從而忽略了標準中獨立環境的整個章節。 – Lundin

0

我不認爲有主函數的原型。這是您的程序的入口點,它由標準定義。

例如C標準定義是這樣:

5.1.2.2.1計劃啓動

1名爲在程序啓動的功能被命名爲主力。 實現聲明此函數沒有原型。應當帶有int返回類型和不帶參數來 定義:

int main(void){} 

或具有兩個參數(此處稱爲argc和argv,雖然任何名字>可以使用,因爲它們是本地的在它們 聲明功能):

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

或等同物; 9)或在一些其它實現定義的方式。

+0

問題中沒有提到託管環境。您通過引用託管環境的子章節來錯誤地引用標準,從而忽略了標準中獨立環境的整個章節。 – Lundin

0

C11 Standard指示什麼是有效的和無效的C(舊標準具有相同的規則)。

在對於main簽名它說(5.1.2.2.1):

稱爲在程序啓動的功能被命名爲主力。該實現沒有聲明這個函數的原型。它應爲int的返回類型和不帶參數進行定義:

 int main(void) { /* ... */ } 

或兩個參數(這裏稱爲argc和argv,雖然可以使用任何名字,因爲他們是當地的在其中聲明它們的功能):

 int main(int argc, char *argv[]) { /* ... */ } 

或同等或在一些其它實現定義的方式。

所以,除了一些其他實現定義的方式,唯一有效的簽名都是這兩個。 如果一個實現定義了一個不同的簽名,您可以自由使用該簽名,前提是您不介意丟失一些代碼可移植性。

所以下面都是一個編譯器無效無額外:

int main() { /* ... */ } 
void main() { /* ... */ } 
void main(void) { /* ... */ } 
double main() { /* ... */ } 
int main(int argc, double *argv[]) { /* ... */ } 
int main(int argc, char **argv, char **envp) { /* ... */ } 
+0

如果'int main()','int main(int argc,char ** argv)'等有效,建議添加add。 – chux

+0

問題中沒有提到託管環境。您通過引用託管環境的子章節來錯誤地引用標準,從而忽略了標準中獨立環境的整個章節。 – Lundin

2

你的教程是不完全,但它描述了一個非常嚴格執行的行爲,比C標準嚴格要求的實現成爲。

C標準中關於程序啓動時調用的函數的說法就是某些可能性需要工作。它並不是說需要其他東西而不是才能工作。另外,C標準對編譯器錯誤的說法很少。現代C實現通常超出標準對診斷的要求;支持他們接受的一套程序的許多擴展也是很常見的。

在「託管」的環境,一個提供所有的標準C庫,讓啓動名稱和簽名int main(void)int main(int argc, char **argv)符合呼籲程序的功能程序的設施。他們需要工作。但是這個標準允許這個函數被「以某種其他實現定義的方式」進行聲明,並且存在許多替代名稱和簽名:我將列出一些最常見的名稱和簽名。

  • int main(int argc, char **argv, char **envp)
  • void main(void)
  • int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

如果它支持這些替代入口點的名稱或簽名之一的執行文件,這是完全可以使用它。 (你的程序將不會是「嚴格符合」,但幾乎沒有真正的計劃是「嚴格符合」,所以不要擔心。)

在一個「獨立」的環境,這提供所有的標準C庫中,在程序啓動時調用的函數的名稱和簽名留給實現 - 您可能不得不使用諸如EFI_STATUS efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)之類的古怪東西。 但是,int main(void)可以正常工作,而int main(int argc, char **argv)可以正常工作(在運行時收到的參數可能是垃圾)。現在


,如果你有一個託管環境中會發生什麼,你使用了一個沒有記錄工作的入口點函數的名稱和/或簽名? C標準說你的程序在這種情況下有未定義行爲- 什麼都可以發生。一些常見的事情將會發生:

  • 編譯器確實發出了一個錯誤或至少是一個警告。它沒有從任何頭文件獲取正確的原型;相反,正確的原型據說是內置於的編譯器,在編譯器自己的源代碼中定義。現在很多C庫函數以及main都是這種情況。演示:

    $ cat > test.c <<\! 
    extern int exit(int); // wrong, `exit` should return `void` 
    void main(void) {} // wrong, `main` should return `int` 
    ! 
    $ gcc -fsyntax-only -std=gnu11 -Wall test.c 
    test.c:1:12: warning: conflicting types for built-in function ‘exit’ 
    test.c:2:6: warning: return type of ‘main’ is not ‘int’ 
    

    (由於歷史原因,GCC是幾乎沒有挑剔別人的代碼,因爲它可能是,很多的事情,從現代的眼光看,應該是錯誤僅僅是警告,並如果你從頭開始編寫新的代碼並使用GCC,我建議基本上總是使用-std=gnu11 -Wall -Wextra -Wpedantic -Werror選項,但不是-std=c11,因爲這會使關閉您可能需要的擴展名,並且可以也暴露系統頭文件中的錯誤)。

  • prog內存無法鏈接。這是什麼情況,舉例來說,如果你試圖彌補自己的名字,而不是調用它main

    $ cat > test.c <<\! 
    extern int puts(const char *); 
    void my_program_starts_here(void) { puts("hello world"); } 
    ! 
    $ gcc -std=gnu11 -Wall test.c 
    /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: 
        In function `_start': 
        (.text+0x20): undefined reference to `main' 
    

    這是更神祕的錯誤,你可以走出連接器中的一個,所以我將它解開一點。你有沒有想過如何調用main?這是令人驚訝的簡單:有C庫提供的功能,通常被稱爲_start,其最後一行是一樣的東西

    exit(main(argc, argv, environ)); 
    

    由於歷史原因,該功能不與libc.so大部分C庫的捆綁。它位於一個單獨的目標文件crt1.o中,編譯器在被要求鏈接一個程序時會自動引入(就像它自動在-lc上一樣)。因此,當您未定義main時,從_startmain的引用不滿足,並且鏈接失敗。

    (OK,如何_start被調用這就是你進入更深的魔法問另外一個問題嗎?)

  • 最後,程序可以編譯和鏈接罰款,甚至出現正常工作 - 但看起來更難,你發現它是行爲不端。如果您在Unix系統上使用void main(void),會發生這種情況。 (一階,除Windows以外的所有託管的環境是Unix系統現在。)

    $ cat > test.c <<\! 
    extern int puts(const char *); 
    void main(void) { puts("hello world"); } 
    ! 
    $ gcc -std=gnu11 test.c 
    $ ./a.out 
    hello world 
    

    沒有-Wall,而不是從編譯器窺視,程序運行罰款......或者做的?

    $ ./a.out ; echo $? 
    hello world 
    12 
    

    該數字應從main返回的值成爲該程序的exit status,其出現在殼可變$?。如果main已正確聲明爲返回int,並且末尾有return 0;,則echo $?將打印0.從哪裏來的?可能是puts的返回值,編譯器在從main返回之前沒有打擾從返回值寄存器中清除。

    很容易沒有注意到這個錯誤,但它一個錯誤,並且第一個嘗試編寫涉及你的程序的shell腳本的人將會對你很惱火。


有關退出狀態的一些註解,主要用於學究:

  1. 在C++中,並用C開始與1999年的標準,你在技術上允許省略任何明確return 0;main只要你聲明正確,但我認爲依靠這是糟糕的風格。

  2. 在Unix上的很多,但並非所有的實現,在$?中顯示的值將只能從main返回的值低了七八位。這是用於檢索子進程退出狀態的系統調用的一個限制,waitpid

  3. 嚴格符合 ISO C程序只能從main返回三個值:0,EXIT_SUCCESS,和EXIT_FAILURE;後兩個常量在stdlib.h中聲明。從main返回零的效果保證是一樣返回EXIT_SUCCESS效果,但不能保證的值相等。

    在實踐中,返回至少0,1和2以及其中EXIT_SUCCESS != 0和/或EXIT_FAILURE != 1早已走到天空中的一大桶的實現是安全的,所以不用擔心它。

+0

謝謝zwol的回答,它非常有幫助,也非常有知識。 –

+0

此答案不正確,不完整。如果這是編譯器使用的形式,那麼'void main(void)'沒有未定義的行爲。實現定義的行爲。這是獨立環境和完全可接受的標準C的常見形式,見5.1.2.1。重要的是要注意的是__的形式是由編譯器選擇的,而不是由程序員選擇的。如果程序員偏離編譯器使用的main()的形式,我們只會得到UB。 – Lundin

+0

至於gcc,用於獨立環境的正確編譯器選項是「-freestanding」,它可以有效地將所有這種main()格式的信息變成不相關的。 – Lundin