C語言的函數問題是困擾很多學者的問題的,c程序中什麼是函數呢?那麼下面一起來看看小編爲大家精心推薦的c程序中什麼是函數,希望能夠對您有所幫助。
C語言讀書筆記--函數
先來看看函數的一般形式,嘗試寫一個加法的函數:
思路是這樣的:首先得有頭文件,頭文件之後就得寫主函數,主函數的內部應該就是加法的過程,我們將所有加法的語句都拿出來組成一個函數。代碼如下:
#include
int add(int a, int b);
int main()
{
int result = add(3,5);
printf("sum is %d", result);
return 0;
}
int add(int a, int b)
{
int sum;
sum = a+b;
return sum;
}
這是一個最簡單的函數,描述了一個加法函數的定義和調用的過程。
int add(int a, int b) 成爲函數的首部。
有了首部之後,就得考慮一件事情,將首部複製之後,加上一個分號,粘貼在主函數之前,作爲函數的原型聲明。試想,我們在主函數裏邊是不是要先定義變量result才能使用result?那麼函數的道理也是一樣的,當程序運行到主函數中語句“int result = add(3,5);”的時候,如果向上沒有尋找到add()的定義,那麼編譯器一定就會報錯。所以要不然添加函數的原型聲明,要不然就將函數的定義直接寫在主函數之前。
函數首部int add(int a, int b)中的第一個int,即add之前的這個int稱爲函數的類型。表明這個函數將要返回一個整數類型的值。這個類型可以是C語言中任何被允許的數據類型,包括void,意爲無返回值類型,即這個函數不需要返回任何的值。
函數首部int add(int a, int b)中的add稱爲函數的名字,簡稱函數名。
函數首部int add(int a, int b)中int a和int b稱爲函數的形式參數。這裏形式參數理論上可以有無窮多個,當然,現實情況下3-5個就已經算是很多了;形式參數中,即使a和b都是int類型的,也要分別定義才行;形式參數可以在函數中直接使用,無須再次定義;形式參數是用來告訴調用者,你應該給我傳遞來什麼樣子的數據,我好利用你給我的數據在函數中進行計算。
int add(int a, int b){}中的{}就是函數體的內容了。函數需要進行的所有的操作都要放在這對大括號中。想必大家也看到了函數體中最後有一條語句是return,這條語句起到的作用就是返回函數計算的結果,在這個程序中就是將加法的結果返回給主函數。需要注意的是,函數的類型和返回值的類型必須嚴格一致!
函數的定義到此爲止,接下來講講函數的調用方式。只要定義好函數,通過函數名(實際參數1,實際參數2,實際參數n)這種方式就可以調用函數了。例如主函數中的“int result = add(3,5);”,就是調用了add函數。這裏,3和5稱爲實際參數,即你究竟想讓函數幫你計算哪兩個數的加法結果,你就在這個括號裏邊寫哪幾個數字。必須要嚴格遵守的規定:實際參數和形式參數必須一一對應,數量應該相同,類型也保持一致。
理解了這幾點之後,一個基本的函數就已經可以寫出來了。接下來來個題目嘗試一下:
輸入精度e,使用公式求π的近似值,精確到最後一項的絕對值小於e。公式:π=1-1/3+1/5-1/7+...
代碼:
//首先得有頭文件
#include
#include
//然後就是主函數了
int main(void)
{
double pi, e; //定義所需變量
double f_pi(double e); //原型聲明。函數名只要符合命名規則即可 //因爲要求小於e,所以也將這個e傳遞過去
printf("enter e: "); //輸入的提示
scanf("%lf", &e); // double類型的e對應%lf,記住不要缺少&
printf("pi=%lf", f_pi(e) ); // 函數返回的是個double類型的值,直接輸出
return 0;
}
double f_pi(double e) //函數首部,形參和實參一定要對應,可以重名
{
int denominator, flag;
double item, sum;
//請注意“先定義,然後賦初值再使用”的好習慣!!!
flag = 1; //負責變換正負符號的變量
denominator = 1; //分母初值爲1,第一項的'1爲1/1
item=1.0; //存放每一項的值
sum=0;
while(fabs(item)>=e) //滿足條件就循環
{
item=flag*1.0/denominator; //計算每一項的值。flag控制符號
//1.0必須寫出小數位,否則整項就變成一個整型值
sum+=item; //累加
flag = -flag; //符號正負切換
denominator = denominator + 2;//分母遞增
}
return sum; //sum的類型和函數的類型必須一致
}
函數的定義和調用其實並不難理解,相信很多人困擾在參數的傳遞上,接下來總結一下函數參數傳遞的幾種方式:
正常的參數調用,例如int、float、double等一一對應的傳遞。
無參數,也無返回值。例如下列代碼就只是爲了輸出一些語句。這種做法在語法上是被允許的,但是並不推薦這麼寫。
void printf()
{
printf("hello world!");
}
3. 參數是數組的名字。我們知道數組的名字是個地址,那麼如果實參是數組名的話,我們可以將形參設置成指針,指向實參傳遞過來的數組的首地址。
4. 參數是指針。如果實參是指針,那麼形參肯定也得是指針。保持類型一致即可,然後在函數內部再對指針進行操作。
5. 參數是結構體。如果實參是結構體,一般來說我們使用結構體指針來做形參比較合適。
還是在此分割一下吧,說了這麼多,可能很多人在問問什麼函數定義這麼麻煩,還要定義函數,直接都寫在main函數中多方便?
非也!
C語言是一個過程化的語言,C語言中的主函數其實是用來主導程序的進程和數據的流動方向的。如果將主函數寫的過於複雜,我們閱讀程序的結構就會非常的費力。
C語言中函數回調
什麼是回調函數?
簡而言之,回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作爲參數傳遞給另一個函數,當這個指針被用爲調用它所指向的函數時,我們就說這是回調函數。
爲什麼要使用回調函數?
因爲可以把調用者與被調用者分開。調用者不關心誰是被調用者,所有它需知道的,只是存在一個具有某種特定原型、某些限制條件(如返回值爲int)的被調用函數。
如果想知道回調函數在實際中有什麼作用,先假設有這樣一種情況,我們要編寫一個庫,它提供了某些排序算法的實現,如冒泡排序、快速排序、shell排序、shake排序等等,但爲使庫更加通用,不想在函數中嵌入排序邏輯,而讓使用者來實現相應的邏輯;或者,想讓庫可用於多種數據類型(int、float、string),此時,該怎麼辦呢?可以使用函數指針,並進行回調。
回調可用於通知機制,例如,有時要在程序中設置一個計時器,每到一定時間,程序會得到相應的通知,但通知機制的實現者對我們的程序一無所知。而此時,就需有一個特定原型的函數指針,用這個指針來進行回調,來通知我們的程序事件已經發生。
下面是自己寫的一個簡單的回調函數,相比其他的那些複雜的代碼,這個更容易理解:
#include
#include
void perfect(int n)
{
int i=1;
int count=0;
for(i=1;i<n;i++)
{
if(0==n%i)
{
count+=i;
}
}
if(count==n)
printf("%d是完數",n);
else printf("%d不是完數",n);
}
void myCallback(void (*perfect)(int ),int n)
{
perfect(n);
}
int main()
{
int n;
printf("請輸入一個正整數");
scanf("%d",&n);
myCallback(perfect,n);
return 0;
}
C語言中的刷新和定位函數
一sh
sh的原型如下:
intfflush(FILE *stream);
2.當需要立即把輸出緩衝區的數據進行物理寫入時,應該使用這個函數。例如調用fflush函數保證調試信息實際打印出來,而不是保存在緩衝區中直到以後纔打印。
二.定位函數
1.在正常情況下,數據以線性的方式寫入,這意味着後面寫入的數據在文件中的位置是在以前所有寫入數據的後面。C同時支持隨機訪問I/O,也就是以任意順序訪問文件的不同位置。隨機訪問是通過在讀取或寫入前,先定位到文件中需要的位置來實現的。
2.定位函數原型:
1>long ftell(FILE*stream);
2>intfseek(FILE *steam,long offset,intfrom);
l函數返回流的當前位置。即:下一個讀取或寫入將要開始的位置距離文件起始位置的偏移量。該函數允許保存一個文件的當前位置。
1>在二進制流中,這個值就是當前位置距離文件起始位置之間的字節數。
2>在文本流中,這個值表示一個位置,但它並不一定準確地表示當前位置和文件起始位置之間的字符數,因爲有些系統將對行末字符進行翻譯轉換。但是,ftell函數返回的值總是可以用於fseek函數中,作爲一個距離文件起始位置的偏移量。
k函數允許你一個流中定位。這個函數將改變下一個讀取或寫入操作的位置。它的第 1個參數是需要改變的流。它的第2和第3個參數標識文件中需要定位的位置。
1>試圖定位到一個文件的起始位置之前是一個錯誤。定位到文件尾並進行寫入將擴展這個文件。定位到文件尾之後並進行讀取將導致返回一條“到達文件尾”的信息。
2>在二進制流中,從SEEK_END進行定位可能不被支持,所以應該避免。
3>在文本流中,如果from是SEEK_CUR或SEEK_END,offset必須是零。如果from是SEEK_SET,offset必須是一個從同一個流中以前調用ftell所返回的值。
5.用fseek改變一個流的位置會帶來三個副作用。
1>首先,行末指示字符被清除。
2>其次,如果在fseek之前使用ungetc把一個字符返回到流中,那麼這個被退回的字符會被丟棄,因爲在定位操作以後,它不再是“下一個字符”。
3>最後,定位允許你從寫入模式切換到讀取模式,或者回到打開的流以便更新。