程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C說話中函數的聲明、界說及應用的入門教程

C說話中函數的聲明、界說及應用的入門教程

編輯:關於C++

C說話中函數的聲明、界說及應用的入門教程。本站提示廣大學習愛好者:(C說話中函數的聲明、界說及應用的入門教程)文章只能為提供參考,不一定能成為您想要的結果。以下是C說話中函數的聲明、界說及應用的入門教程正文


對函數的“界說”和“聲明”不是一回事。函數的界說是指對函數功效切實其實立,包含指定函數名,函數值類型、形參及其類型和函數體等,它是一個完全的、自力的函數單元。而函數的聲明的感化則是把函數的名字,函數類型和形參的類型、個數溫柔序告訴編譯體系,以便在挪用該函數時停止對比檢討(例如,函數名能否准確,實介入形參的類型和個數能否分歧),它不包含函數體。——譚浩強 ,《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說話晚期,雖然有時不須要對函數名停止解釋,但有些情形下對函數名停止解釋照樣必需的,好比:

double sqrt();
int main()
{
 printf("%f\n" , sqrt(9.) );
}

這是由於函數sqrt()前往值的類型不是int類型而是double類型,編譯器編譯時須要曉得sqrt(9.)這個表達式的類型。

不難留意到這類對函數名的解釋異常簡略,這是最晚期的一種函數類型解釋的情勢。這類解釋只側重解釋函數名是一個函數及其前往值類型,假如法式員在挪用函數時存在參數類型或個數方面的毛病編譯器是沒法發覺的,由於函數類型解釋中“()”內沒有任何信息。

這類方法只解釋了函數名與()停止運算的成果也就是函數前往值的數據類型,沒法進一步檢討參數方面的毛病是這類寫法的缺乏的地方。

假如不寫函數類型解釋,也能夠把函數界說寫在函數挪用之前:

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)式解釋函數類型的辦法:

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法式員仿佛驚人地分歧崇尚“繁復”——乃至可以說是“至簡”。

main()
{
 printf("hello,world\n");
}

這就是main()函數最陳舊的寫法,K&R在他們的經典名著《The C Programming Language》中的第一個C說話源法式(1978)。這類寫法是誰人時期的主流。

的確和赤身差不多,連#include<stdio.h>也沒有麼?在《The C Programming Language》的初版中確切沒有。誰人時期的C說話,前往值類型為int的函數不消聲明。不外在該書的第二版(1988)中這個法式被改成了:

#include <stdio.h>
main()
{
 printf("hello,world\n");
}

前往值類型為int的函數不消聲明的規矩轉變了嗎?規矩沒有轉變。轉變了的是不雅念,人們曾經不再偏向於代碼的“至簡”,而開端偏向於在代碼中交卸清晰每個標識符的前因後果。從C89開端提倡在函數挪用之前必定要有函數聲明,但並沒有強求,而在C99這曾經是強迫性的請求了。因為《The C Programming Language》第二版正值ANSI C尺度公布(1989)前夜出書,所以這類變更也應當視為ANSI C尺度的偏向性和K&R對新尺度的認同。雖然這個例子沒有完整反應出來這類認同。

為何說沒有完整反應出來這類認同呢?由於這個main()的界說並沒有依照函數原型(Function prototype)的方法來寫,C90中劃定不帶參數的main()函數應當如許寫:

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。但異樣只把()內不寫任何內容視為過時,而沒有完整制止,可見習氣力氣的固執。

那又為何說K&R對新尺度的認同呢?《The C Programming Language》第二版中的其他函數界說和函數類型聲明根本上都改成了函數原型作風。好比,在講授main()函數的參數時,K&R把本來的main()函數

#include <stdio.h>
main(argc,argv)
int argc;char *argv[];
{
 /*…… */
 return 0;
}

改成了:

#include <stdio.h>
main(int argc, char *argv[])
{
 /*…… */
 return 0;
}

前一個寫法明天曾經差不多絕跡,後一個main()以明天的眼力來看有些奇異,main()的參數是用函數原型作風寫的,但卻沒有寫main()前往值的類型,給人有點半新半舊的感到。雖然不克不及說它違反C90(由於C90允許不寫main()後面的int),但假如寫上了前往值的類型int,就同時知足古代C99尺度的請求了。

這裡湧現的“return 0;”是怎樣回事?這在古代C說話中曾經是習以為常了,它前往給操作體系一個值以注解法式是在何種狀況下停止的。但在另外一段代碼中,K&R仿佛又走得太遠:

#include <stdio.h>
main(int argc,char *argv[])
{
 int found = 0 ;
 /*……盤算found的值 */
 return found;
}

這個其實有些“別具一格”,竟然把盤算成果前往給了操作體系,很有沖破慣例之嫌。

那後面幾個沒有“return 0;”的main()函數會怎樣樣?依照C90尺度,會前往一個不肯定的int類型的值,假如確切不關懷這個前往值是若干,不寫確切可以。但C99卻請求編譯器在編譯的時刻協助給補上這個“return 0;”,C99在必需寫int這個成績上沒有姑息懶人,但在這裡卻對偷懶的做法賜與了姑息。 問:假如確切不關懷main()函數的前往值,把main()的前往值界說為void類型若何?我看到很多書上都如許寫的。

#include <stdio.h>
void main()
{
 printf("This is a C program.\n");
}

這在C99之前是一種野門路寫法,畢竟從哪裡冒出來的,無據可考。但前幾年的主流教科書中這類寫法很罕見。K&R(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給了這類寫法以容身之地麼?從某種意義上或許可以如許懂得。由於K&R沒認可過這類寫法,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()的寫法:

int main(int argc, char *argv[], char *env[])
{
 /* */
 return 0;
}

竟然有3個形參,誰人env是做甚麼用的?誰人參數可使法式取得情況變量。

甚麼叫情況變量?簡略地講可以懂得為操作體系記載的一些數據,好比盤算機的名字,操作體系放在哪裡等等。運用法式在運轉時能夠要用到這些信息,這時候可以經由過程env這個參數來取得。

假如編譯器不支撐main()的第三個參數怎樣辦?尺度庫函數也能夠到達異樣的目標。

#include <stdlib.h>
char *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”。

上面這類作風若何?

#include <stdio.h>
int main()
{
 printf("This is a C program.\n");
 return 0;
}

這個寫法有點不正經。前往值的類型int寫了,這個和C89的提倡或C99的請求分歧,然則()外面甚麼都不寫,又與尺度的所提倡的作風不符,所以說不正經。這類寫法今朝的尺度仍然允許,但屬於尺度今朝尚能容忍的但行將過時的(obsolescent)寫法,被擯棄只是日夕的成績。這類寫法就好像現代的函數形參的寫法一樣:

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尺度中,只要以下兩種界說方法是准確的:”

int main( void )
{ 
 /* */
 return 0;
}


int main( int argc, char *argv[] )
{
 /* */
  return 0;
}

這類說法對嗎?

這類說法明顯纰謬。但可以確認的是這兩種界說方法必定准確。不只在C99來講是准確的,以C89來講也是准確的。

還有一種寫法:

int main( void )
{
 return EXIT_SUCCESS;
}

誰人EXIT_SUCCESS是怎樣回事?

return EXIT_SUCCESS;是與return 0;等價的一種高雅的寫法。EXIT_SUCCESS是在stdlib.h中界說了的符號常量,前往這個值表現法式義務完成後法式加入。在stdlib.h界說的另外一個符號常量EXIT_FAILURE,平日用於法式沒法完成義務而加入。

其實太目眩紛亂了,須要記住這麼多嗎?明顯沒需要。許多器械都是汗青緣由遺留下的渣滓。

假如進修C說話,應當記住或應用哪一種呢?明顯是:

int main( void )
{ 
 /* */
  return 0;
}


int main( int argc, char *argv[] )
{
 /* */
  return 0;
}

第一,他們廣泛實用,不存在可移植性的成績;

第二,就今朝看,他們不存在任何過時或行將過時的成份。固然,假如愛好高雅,不寫return 0;而寫EXIT_SUCCESS也能夠。 趁便說一句,有的進修者記不住帶參數main()函數兩個形參的名字。其實這兩個形參的名字也能夠本身取,紛歧定用那兩個名字,只需記住類型便可以了。第二個參數的類型也能夠是char **,這和本來的是等價的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved