對函數的“定義”和“聲明”不是一回事。函數的定義是指對函數功能的確立,包括指定函數名,函數值類型、形參及其類型以及函數體等,它是一個完整的、獨立的函數單位。而函數的聲明的作用則是把函數的名字,函數類型以及形參的類型、個數和順序通知編譯系統,以便在調用該函數時進行對照檢查(例如,函數名是否正確,實參與形參的類型和個數是否一致),它不包括函數體。——譚浩強 ,《C程序設計》(第四版),清華大學出版社,2010年6月,p182
定制開發可以根據自己的需求進行定制,成都網站設計、網站建設、外貿網站建設構思過程中功能建設理應排到主要部位公司成都網站設計、網站建設、外貿網站建設的運用實際效果公司網站制作網站建立與制做的實際意義
這段論述包含了許多概念性錯誤,這些概念錯誤在許多C語言書中都同樣普遍存在。為了說明這些錯誤,首先來回顧一下C語言演變和發展的一些情況。
最早,C語言的代碼可以這樣寫:
main(){?printf("hello,world!\n");}
注意,這段代碼對標識符printf沒有進行任何說明。這是因為printf()函數的返回值為int類型。當時的C語言規定,對于沒有任何說明的函數名,編譯器會默認為返回值為int類型,因此對這樣的函數名可以不做任何說明。那個時期的C語言,很多情況下int可以不寫。例如main()函數返回值的類型為int就可以不寫。
但是需要特別說明的是,這種“省勁”的寫法已經過時,從C90標準起,這種寫法就步入了被逐步拋棄的過程(盡管當時還沒有完全立即廢止)。C99廢除了隱式函數聲明法則(remove implicit function declaration),另外,省略main()前面的int也已經不再容許了。
在C語言早期,盡管有時不需要對函數名進行說明,但有些情況下對函數名進行說明還是必須的,比如:
?
12345
double sqrt();int main(){?printf("%f\n" , sqrt(9.) );}
這是因為函數sqrt()返回值的類型不是int類型而是double類型,編譯器編譯時需要知道sqrt(9.)這個表達式的類型。
不難注意到這種對函數名的說明非常簡單,這是最早期的一種函數類型說明的形式。這種說明只著重說明函數名是一個函數及其返回值類型,如果程序員在調用函數時存在參數類型或個數方面的錯誤編譯器是無法察覺的,因為函數類型說明中“()”內沒有任何信息。
這種辦法只說明了函數名與()進行運算的結果也就是函數返回值的數據類型,無法進一步檢查參數方面的錯誤是這種寫法的不足之處。
如果不寫函數類型說明,也可以把函數定義寫在函數調用之前:
?
123456789
double square ( double x){?return x * x ;}int main(void){?printf("%f\n" , square(3.) );?return 0;}
這表明函數定義也具有對函數名的類型加以說明的效果,因此從這個意義上來說,函數定義也是一種對函數類型的說明。這種辦法可以檢查出函數調用時在參數個數和類型方面的錯誤。
但是,用這種辦法說明函數名并不好,因為這樣做在編程時還需要考慮應該把哪個函數定義寫在前面,哪個寫在后面的問題。假如函數A調用函數B,函數B調用函數C,函數C又調用函數A,究竟如何安排函數定義的順序就會讓人感到無所適從。此外這種辦法也不利于代碼的組織,在由多個源文件組成的源程序時,這種寫法就更會捉襟見肘、漏洞百出。因此,在1990年,C標準借鑒C++語言規定了一種新的說明函數名的方法,這就是函數原型(Function Propotype)式說明函數類型的方法:
?
12345678910
double square ( double ); //或 double square ( double x)int main(void){??printf("%f\n" , square(3.) );??return 0;}double square ( double x){??return x * x ;}
使用這種辦法,不但可以檢查函數調用時參數類型和個數方面的錯誤,同時解決了源代碼的組織問題,因為程序員不必再考慮該把哪個函數寫在前面、哪個寫在后面這種無聊的問題了。這種辦法全面地說明了函數名的數據類型。此外要說明的是,把形參及其數據類型寫在“()”內形式的函數定義也屬于函數原型(Function Propotype)的范疇。
由此可見,古老的、不對參數進行任何說明的函數類型說明方式、函數定義以及函數原型式的函數類型說明方式都具有說明函數名意義的效用。從這個意義上講它們都是函數聲明。在C語言中,聲明(Declaration)這個詞的本義就是指定標識符的意義和性質(A declaration specifies the interpretation and attributes of a set of identifiers.),某個標識符的定義(Definition)同時也是這個標志符的“聲明”(Declaration)。函數定義(Function definition)則意指包括函數體。(A definition of an identifier is a declaration for that identifier that: ??for a function, includes the function body;)。函數原型則特指包括說明參數類型的函數聲明,它同樣包含用這種方式寫出的函數定義。
現在回過頭來看樣本中的第一句話:“對函數的“定義”和“聲明”不是一回事”。由于函數定義本身就是一種函數聲明,怎么可以說它們不是一回事呢?這句話的邏輯就如同說“男人”和“人”不是一回事。你可以說男人和女人不是一回事,因為他們沒有交集。但沒法說男人和人不是一回事,因為男人是人的子集,男人就是人的一種,怎么可以說男人和人不是一回事呢?
那么,不帶函數體的函數聲明應該如何稱呼呢?在C語言中,它們叫被做“函數類型聲明”(Function type declaration)。函數類型聲明最主要的特點是聲明了函數名是一個函數及其返回值的類型,如果也聲明了參數的類型,則是函數原型式的函數類型聲明。
樣本中的“而函數的聲明的作用則是把函數的名字,函數類型以及形參的類型、個數和順序通知編譯系統,以便在調用該函數時進行對照檢查(例如,函數名是否正確,實參與形參的類型和個數是否一致),它不包括函數體”這句話同樣不通。其主要錯誤是它混淆了“函數原型式類型聲明”與“函數聲明”這兩個概念,前一個概念只是后一個概念的子集。函數聲明中不但包含“函數類型聲明”,也包含“函數定義”和老式的“函數類型聲明”。由于函數定義本身就是一種函數聲明,所以無法斷定函數的聲明是否包括函數體;而且老式的函數類型聲明(例如double sqrt();)也屬于函數聲明,這種函數聲明并不檢查參數類型及個數方面的錯誤。此外函數聲明也并沒有檢查“函數名”正確與否的功能。
這段文字中的“函數類型”這個概念也有錯誤,函數類型所描述的不但包括函數返回值類型,也可能一并描述參數的個數和類型(如果是函數原型),因此不能與“形參的類型、個數”相提并論。
現代的C語言的函數定義和函數類型聲明都采用函數原型式的風格,C99把舊的非原型形式視為過時,這意味著非原型形式以后可能被禁止。
main()函數
在各種C語言書上,能看到各式各樣main()函數的寫法,簡直令人無所適從,這是這么回事?原因主要有兩個:一個是隨著C語言的發展和演化,main()函數的寫法也在不斷變化;另外,某些書籍寫法不規范或誤導的現象也同時存在。
最初main()函數的寫法非常簡潔,那個時候的C程序員哪怕一個字符似乎都不肯多寫。不知道是因為當時鍵盤質量不好還是因為編輯器太糟糕的緣故,那個時代的C程序員似乎驚人地一致崇尚“簡約”——甚至可以說是“至簡”。
?
1234
main(){?printf("hello,world\n");}
這就是main()函數最古老的寫法,KR在他們的經典名著《The C Programming Language》中的第一個C語言源程序(1978)。這種寫法是那個時代的主流。
簡直和裸體差不多,連#includestdio.h也沒有么?在《The C Programming Language》的第一版中確實沒有。那個時代的C語言,返回值類型為int的函數不用聲明。不過在該書的第二版(1988)中這個程序被改成了:
?
12345
#include stdio.hmain(){?printf("hello,world\n");}
返回值類型為int的函數不用聲明的規則改變了嗎?規則沒有改變。改變了的是觀念,人們已經不再傾向于代碼的“至簡”,而開始傾向于在代碼中交代清楚每一個標識符的來龍去脈。從C89開始倡導在函數調用之前一定要有函數聲明,但并沒有強求,而在C99這已經是強制性的要求了。由于《The C Programming Language》第二版正值ANSI C標準頒布(1989)前夕出版,所以這種變化也應該視為ANSI C標準的傾向性以及KR對新標準的認同。盡管這個例子沒有完全反映出來這種認同。
為什么說沒有完全反映出來這種認同呢?因為這個main()的定義并沒有按照函數原型(Function prototype)的方式來寫,C90中規定不帶參數的main()函數應該這樣寫:
?
1
int main(void) { /*. . .*/}
但同時規定那個int可以省略。C90把()內不寫任何內容視為過時的寫法,盡管C90無奈地容忍了它(The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.)。
為什么要容忍?因為有許多老式的代碼還在用。
如果以C99的標準看這個main()寫得如何呢?C99不容許省略int。但同樣只把()內不寫任何內容視為過時,而沒有完全禁止,可見習慣力量的頑固。
那又為什么說KR對新標準的認同呢?《The C Programming Language》第二版中的其他函數定義和函數類型聲明基本上都改成了函數原型風格。比如,在講解main()函數的參數時,KR把原來的main()函數
?
1234567
#include stdio.hmain(argc,argv)int argc;char *argv[];{?/*?? */?return 0;}
改成了:
?
123456
#include stdio.hmain(int argc, char *argv[]){?/*?? */?return 0;}
前一個寫法今天已經差不多絕跡,后一個main()以今天的眼光來看有些奇怪,main()的參數是用函數原型風格寫的,但卻沒有寫main()返回值的類型,給人有點半新半舊的感覺。盡管不能說它違背C90(因為C90容許不寫main()前面的int),但如果寫上了返回值的類型int,就同時滿足現代C99標準的要求了。
這里出現的“return 0;”是怎么回事?這在現代C語言中已經是司空見慣了,它返回給操作系統一個值以表明程序是在何種狀態下結束的。但在另一段代碼中,KR似乎又走得太遠:
?
1234567
#include stdio.hmain(int argc,char *argv[]){?int found = 0 ;?/*??計算found的值 */?return found;}
這個實在有些“標新立異”,居然把計算結果返回給了操作系統,頗有突破常規之嫌。
那前面幾個沒有“return 0;”的main()函數會怎么樣?按照C90標準,會返回一個不確定的int類型的值,如果確實不關心這個返回值是多少,不寫確實可以。但C99卻要求編譯器在編譯的時候幫忙給補上這個“return 0;”,C99在必須寫int這個問題上沒有遷就懶人,但在這里卻對偷懶的做法給予了遷就。 問:如果確實不關心main()函數的返回值,把main()的返回值定義為void類型如何?我看到許多書上都這樣寫的。
?
12345
#include stdio.hvoid main(){?printf("This is a C program.\n");}
這在C99之前是一種野路子寫法,究竟從哪里冒出來的,無據可考。但前幾年的主流教科書中這種寫法很常見。KR(C語言的發明者)沒有這樣寫過,C90國際標準也不承認這種寫法。Bjarne Stroustrup(C++語言的創始人)在他的關于C++的FAQ中,在回答是否可以寫“void main()”時憤怒地回答說這種寫法在C++和C中都不曾有過。事實上,很多C語言專家都認為“void main()”非常邪惡。
因此,在C99之前,這是不符合標準的寫法。盡管這段代碼的功能似乎是輸出“This is a C program.”,但其實卻不是一個“C program”。
但是有時這樣寫并沒有產生錯誤啊?首先,C語言的錯誤不一定反應在編譯、鏈接或運行過程中。你輸出一個垃圾值也可能一路通過編譯、鏈接或運行,但這不說明你的代碼沒有錯誤,更不能說明這樣的代碼正確、有意義。其次,這樣的寫法在有些編譯器下程序會產生崩潰或得到警告。這說明這種寫法至少不普遍性適用的。可以說,如果不是C99標準,這種寫法根本沒有立錐之地。
C99給了這種寫法以立足之地么?從某種意義上也許可以這樣理解。因為KR沒承認過這種寫法,C90根本不承認這種寫法,C99雖然沒有正式承認這種寫法,但為這種寫法留了一個后門:“It shall be defined ??or in some other implementation-defined manner”。這意思就是說,如果編譯器明確聲稱允許void main()這種寫法的話,那么C99不再象C90那樣簡單認為這種寫法違背C標準。
但是不管怎么說,這種寫法最多是某些編譯器的一種“方言土語”,如果沒有特殊理由,比如僅僅是工作在某個特殊環境,且僅僅使用特定的編譯器而根本不考慮程序的可移植性,為什么不寫普遍適用的形式呢?
既然很多C語言專家都認為“void main()”非常邪惡,C99為什么包容這種寫法呢?很難確定C99是否就是打算專門想把這種寫法也“收容”在標準之列。因為除了void main(),還有另外一些main()函數的寫法被C90排除在標準之外了。而現在,這些寫法在理論上也具備了符合C99標準的可能性。
還有什么樣的main()函數?很多編譯器都支持下面的main()的寫法:
?
12345
int main(int argc, char *argv[], char *env[]){?/* */?return 0;}
居然有3個形參,那個env是做什么用的?那個參數可以使程序獲得環境變量。
什么叫環境變量?簡單地講可以理解為操作系統記錄的一些數據,比如計算機的名字,操作系統放在哪里等等。應用程序在運行時可能要用到這些信息,這時可以通過env這個參數來獲得。
如果編譯器不支持main()的第三個參數怎么辦?標準庫函數也可以達到同樣的目的。
?
12
#include stdlib.hchar *getenv(const char *name);
是否可以說void main()和int main(int argc, char *argv[], char *env[])也符合C99標準呢?恐怕還不能這么說,現在只是不能說這兩種寫法一定不符合C99標準。但這兩種寫法不符合C90標準是確定的,這兩種寫法的可移植性很差也是確定無疑的。C99標準在這里寫的很模糊,沒有進一步界定“implementation-defined manner”的含義。除非編譯器聲明遵守C99標準,且容許這兩種寫法,否則斷言這兩種寫法符合C99標準屬于空穴來風。
有人說“C99建議把main函數指定為int型”,這種說法對嗎?顯然不對。因為C99并非絕對不包容返回值非int類型的main()。正確的說法是,C90要求main()函數的返回值一定得是int型。但C90容許不寫那個int,而C99則要求必須寫上這個“int”。
下面這種風格如何?
?
123456
#include stdio.hint main(){?printf("This is a C program.\n");?return 0;}
這個寫法有點不倫不類。返回值的類型int寫了,這個和C89的倡導或C99的要求一致,但是()里面什么都不寫,又與標準的所倡導的風格不符,所以說不倫不類。這種寫法目前的標準依然容許,但屬于標準目前尚能容忍的但即將過時的(obsolescent)寫法,被拋棄只是早晚的問題。這種寫法就如同古代的函數形參的寫法一樣:
?
123456
main(argc,argv)int argc;char *argv[];{?/*?? */?return 0;}
都屬于歷史的垃圾。
見過在main()的函數體的“}”之前前寫一句getch();,這個是怎么回事?這個是時代的產物。在PC從DOS時代轉變為Windows時代的過程中,DOS時代開發的IDE(主要是TC)無法在運行程序后顯示輸出結果,為了在運行后從容仔細地觀察一下運行結果再返回IED界面,加上了這么一句,人為地延長程序運行時間(因為getch()會等待用戶輸入一個字符)。但這與main()本身的結構無關。這條語句不具備普遍意義,只是將就過時的IDE的一種權宜之計而已。所謂不具備普遍意義是指,第一,真正的程序往往不需要這條語句,就是說這條語句與程序功能無關;第二,getch()這個函數并不是標準函數,只有個別的編譯器才支持它,在其他編譯器上寫這條語句,很可能行不通。
為什么不用getchar()這個標準庫函數呢?getchar()的功能和getch()有點區別,前者會在標準輸出設備上顯示用戶鍵入的字符,這顯得很不利索,而后者則不會顯示用戶所鍵入的字符,更接近“Press Any Key to continue??”的效果。
有的代碼在main()函數結束前寫system("PAUSE");,是否也是這個意思?是的。這也是一種人工制造的“請按任意鍵繼續. . .”,與程序功能結構無關,只是為了方便地觀察輸出結果。但是這種寫法比調用getch()要好,因為system()函數是標準庫函數,各個編譯器都提供支持。
有一種說法,“在最新的C99標準中,只有以下兩種定義方式是正確的:”
?
12345
int main( void ){?/* */?return 0;}
和
?
12345
int main( int argc, char *argv[] ){?/* */??return 0;}
這種說法對嗎?
這種說法顯然不對。但可以確認的是這兩種定義方式一定正確。不但在C99來說是正確的,以C89來說也是正確的。
還有一種寫法:
?
1234
int main( void ){?return EXIT_SUCCESS;}
那個EXIT_SUCCESS是怎么回事?
return EXIT_SUCCESS;是與return 0;等價的一種文雅的寫法。EXIT_SUCCESS是在stdlib.h中定義了的符號常量,返回這個值表示程序任務完成后程序退出。在stdlib.h定義的另一個符號常量EXIT_FAILURE,通常用于程序無法完成任務而退出。
實在太眼花繚亂了,需要記住這么多嗎?顯然沒必要。很多東西都是歷史原因遺留下的垃圾。
如果學習C語言,應該記住或使用哪種呢?顯然是:
?
12345
int main( void ){?/* */??return 0;}
和
?
12345
int main( int argc, char *argv[] ){?/* */??return 0;}
第一,他們普遍適用,不存在可移植性的問題;
第二,就目前看,他們不存在任何過時或即將過時的成分。當然,如果喜歡文雅,不寫return 0;而寫EXIT_SUCCESS也可以。 順便說一句,有的學習者記不住帶參數main()函數兩個形參的名字。其實這兩個形參的名字也可以自己取,不一定用那兩個名字,只要記住類型就可以了。第二個參數的類型也可以是char **,這和原來的是等價的。
回答于?2022-12-11
C語言的函數定義格式是:
函數類型 函數名稱 ( 參數列表 )
{
//函數體
}
函數聲明,不是說明的格式是“函數定義格式”的第一行,然后加 “;”,即:
函數類型 函數名稱 ( 參數列表 );
特別地,在函數聲明中的參數列表中,不需要給出具體的變量名稱,但必須給出每個變量的類型。
C語言的函數定義格式是:
函數返回值類型 函數名 ( 參數列表 )
{
//函數體
}
在使用函數時,要先做聲明,格式如下:
函數類型 函數名稱 ( 參數列表 );
特別地,在函數聲明中的參數列表中,不需給出具體的變量名稱,但要給出每個變量的類型。
一般包括字符庫函數,數學函數,目錄函數,進程函數,診斷函數,操作函數等。
字符庫函數所在函數庫為ctype.h;數學函數所在函數庫為math.h、stdio.h、string.h、float.h;目錄函數所在函數庫為dir.h、dos.h;進程函數所在函數庫為stdlib.h、process.h。
診斷函數所在函數庫為assert.h、math.h;接口子程序所在函數庫為:dos.h、bios.h;操作函數所在函數庫為string.h、mem.h;時間日期函數函數庫為time.h、dos.h。
擴展資料
C語言為一種程序設計的入門語言。由于C語言的語句中沒有提供直接計算sin或cos函數的語句,會造成編寫程序困難;但是函數庫提供了sin和cos函數,可以拿來直接調用。顯示一段文字,我們在C語言中找不到顯示語句,只能使用庫函數printf。
C語言的庫函數并不是C語言本身的一部分,它是由編譯程序根據一般用戶的需要,編制并提供用戶使用的一組程序。C的庫函數極大地方便了用戶,同時也補充了C語言本身的不足。在編寫C語言程序時,使用庫函數,既可以提高程序的運行效率,又可以提高編程的質量。
參考資料來源:百度百科-C語言庫函數
參考資料來源:百度百科-C語言函數
一般包括字符庫函數,數學函數,目錄函數,進程函數,診斷函數,操作函數等。
本文標題:c語言的函數聲明格式多種,c語言中的函數聲明
網站網址:http://vcdvsql.cn/article42/hshjec.html
成都網站建設公司_創新互聯,為您提供自適應網站、網站維護、定制網站、動態網站、網站改版、靜態網站
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯