C語言函數(shù)(一)
謹(jǐn)記
短暫的人生中,注定有很多人陪伴著你度過,陪你一起歡呼,陪你一起傷心流淚,陪你一起走過一條又一條的街道,或許他們并不是真真切切的,但是至少他們還在你身邊。這個(gè)世界很大,但是卻又很小,大的時(shí)候你要找到的你想要的卻始終也找不到的,小的時(shí)候你要逃避或者不想要的卻始終在你身邊徘徊著,曾經(jīng)有一段時(shí)間,自己很墮落,不知道干嘛,每天就渾渾噩噩的過完一天又一天,還把自己的筆名換成“徘徊的小青年”,那個(gè)時(shí)候,整個(gè)人生已經(jīng)失去意義了,知道后來有一天看到了一個(gè)畫面,我才明白,其實(shí)還有很多東西是需要自己努力去奮斗的,請(qǐng)你一定要記住,不管你做什么,身處何境,請(qǐng)不要抱怨,相信自己,你會(huì)成功的。
前言
非常的抱歉,由于其他事耽誤了,文章沒有及時(shí)的更新,望各位讀者體諒。今天給大家?guī)淼氖钦僀語言中很重要的一個(gè)知識(shí)點(diǎn)——函數(shù),前面我們對(duì)指針已經(jīng)有一個(gè)深刻的理解了,那么,下面我們來看看函數(shù)吧,通過本篇文章你可以學(xué)到:
- 函數(shù)的定義和聲明
- 函數(shù)的調(diào)用、傳參和返回值
- 函數(shù)的分類展示
- 函數(shù)和數(shù)組
- main函數(shù)機(jī)制
函數(shù)的定義和聲明
1、函數(shù)的定義
函數(shù)是一個(gè)完成特定功能的代碼模塊,通常有參數(shù),也可以沒有參數(shù);通常要求有返回值,也可以是空值。一般形式如下:
<數(shù)據(jù)類型> <函數(shù)名稱>( <形式參數(shù)說明> )
{
語句序列;
return[(<表達(dá)式>)];
}
函數(shù)名稱是一個(gè)標(biāo)識(shí)符,要求符合標(biāo)識(shí)符的命名規(guī)則。
數(shù)據(jù)類型是整個(gè)函數(shù)返回值的類型。這里可以包括存儲(chǔ)類型說明符、數(shù)據(jù)類型說明符以及時(shí)間域說明符。如果函數(shù)不需要有返回值時(shí),函數(shù)類型說明符可以寫為void。
形式參數(shù)說明就是形式參數(shù)的列表,簡稱形參。形參可以是任意類型的變量,各參數(shù)之間用逗號(hào)分隔。在進(jìn)行函數(shù)調(diào)用時(shí),調(diào)用函數(shù)將賦予這些形式參數(shù)實(shí)際的值。
函數(shù)名后有一對(duì)花括弧?!皗}”中的內(nèi)容稱為函數(shù)體。函數(shù)體中有若干條語句(大于或等于零個(gè)),實(shí)現(xiàn)特定的功能。注意:在函數(shù)體中,表達(dá)式語句里使用的變量必須事先已有說明,否則不能使用。
return[(<表達(dá)式>)]語句中表達(dá)式的值,要求和函數(shù)名前的數(shù)據(jù)類型保持一致。如果數(shù)據(jù)類型為void,可以省略,也可以寫成“return;”。
例如定義一個(gè)簡單的函數(shù)
無形參的函數(shù):
void test(){
printf("這是void一個(gè)函數(shù)\n");
}
有形參的函數(shù)
void test(int a){
int b = 20;
b = b + a;
printf("%d\n",b);
}
2、函數(shù)的聲明
函數(shù)的聲明就是把函數(shù)名、返回值類型以及形參類型、個(gè)數(shù)和順序通知編譯系統(tǒng),以便在調(diào)用該函數(shù)時(shí),編譯系統(tǒng)進(jìn)行對(duì)照檢查,包括函數(shù)名是否正確、傳遞參數(shù)的類型、個(gè)數(shù)是否與形參一致。如若出現(xiàn)不對(duì)應(yīng)的情況,編譯會(huì)有語法錯(cuò)誤。
在C語言中,用戶可以通過兩種方法來進(jìn)行函數(shù)聲明。
① 如果函數(shù)調(diào)用前,沒有對(duì)函數(shù)作聲明,且同一源文件的前面出現(xiàn)了該函數(shù)的定義,那么編譯器就會(huì)記住它的參數(shù)數(shù)量和類型以及函數(shù)的返回值類型,即把它作為函數(shù)的聲明,并將函數(shù)返回值的類型默認(rèn)為int型。
② 如果在同一源文件的前面沒有該函數(shù)的定義,則需要提供該函數(shù)的函數(shù)原型。用戶自定義的函數(shù)原型通??梢砸黄饘懺陬^文件中,通過頭文件引用的方式來聲明。
提示;實(shí)際上,如果在調(diào)用函數(shù)之前沒有對(duì)函數(shù)進(jìn)行聲明,則編譯系統(tǒng)會(huì)把第一次遇到的該函數(shù)形式(函數(shù)定義或函數(shù)調(diào)用)作為函數(shù)的聲明,并將函數(shù)返回值的類型默認(rèn)為int型。
在前面的內(nèi)容中介紹過變量的聲明。對(duì)于全局變量的聲明可以加上extern標(biāo)識(shí),同樣對(duì)于函數(shù)的聲明,也可以使用extern。如果函數(shù)的聲明中帶有關(guān)鍵字extern,是告訴編譯器這個(gè)函數(shù)是在別的源文件里定義的。還有一個(gè)關(guān)鍵字static,若全局變量前有static修飾,則變量只能在當(dāng)前文件中被使用,同樣若函數(shù)的聲明前面有static限制,是告訴編譯器,該函數(shù)只能在當(dāng)前文件中被調(diào)用,外部文件不能調(diào)用。
1、函數(shù)的分類
對(duì)于函數(shù)的分類,我們可以從返回值、形參進(jìn)行一個(gè)分類,一般分為4類:
- 沒有返回值和形參
- 沒有返回值,有形參
- 有返回值沒有形參
- 有返回值,有形參
接下來分別寫一個(gè)簡單的示例代碼看看這四種函數(shù)的寫法
#include <stdio.h>
//=======函數(shù)的聲明部分========
void test();
void test1(int a);
int test2();
int test3(int a,int b);
//========主函數(shù)===========
int main(int argc, const char * argv[]) {
//======這里可以調(diào)用函數(shù)
printf("Hello, World!\n");
return 0;
}
//=======函數(shù)的實(shí)現(xiàn)部分========
void test(){
printf("這是沒有返回值,沒有參數(shù)的函數(shù)\n");
}
void test1(int a){
int k = 10;
k = k + a;
printf("%d\n",k);
printf("這是沒有返回值,有形參的函數(shù)\n");
}
int test2(){
printf("有函數(shù)返回值,沒有形參的函數(shù)\n");
return 10;
}
int test3(int a,int b){
printf("有函數(shù)返回值,有形參的函數(shù)\n");
return a + b;
}
在實(shí)際開發(fā)中,需要哪一種函數(shù),就可以去自己按照函數(shù)的形式進(jìn)行聲明和實(shí)現(xiàn)那種函數(shù)。
函數(shù)的調(diào)用、參數(shù)的傳遞和返回值
1、函數(shù)的調(diào)用
函數(shù)的使用也叫函數(shù)的調(diào)用,形式如下:
函數(shù)名稱(實(shí)際參數(shù))
注意:
<函數(shù)名稱>是一個(gè)標(biāo)識(shí)符,符合標(biāo)識(shí)符的命名規(guī)則;
(實(shí)際參數(shù))需要確切的數(shù)據(jù),也可以是具有確定值的表達(dá)式。實(shí)參就是在使用函數(shù)時(shí),調(diào)用函數(shù)傳遞給被調(diào)用函數(shù)的數(shù)據(jù),用以完成所要求的任務(wù)。
函數(shù)的參數(shù)分為形式參數(shù)和實(shí)際參數(shù)兩種。
形式參數(shù)指的是出現(xiàn)在函數(shù)定義中的參數(shù)列表,簡稱形參。實(shí)際參數(shù)簡稱實(shí)參,出現(xiàn)在主調(diào)函數(shù)中。發(fā)生函數(shù)調(diào)用時(shí),主調(diào)函數(shù)把實(shí)參的值傳送給被調(diào)函數(shù)的形參,從而實(shí)現(xiàn)主調(diào)函數(shù)向被調(diào)函數(shù)的數(shù)據(jù)傳送。
提示
函數(shù)調(diào)用可以作為一個(gè)運(yùn)算量出現(xiàn)在表達(dá)式中,也可以單獨(dú)形成一個(gè)語句。對(duì)于無返回值的函數(shù)來講,只能形成一個(gè)函數(shù)調(diào)用語句。
如果是調(diào)用無參函數(shù),則實(shí)參列表可以沒有,但括弧不能省略。如果實(shí)參列表包含多個(gè)實(shí)參,則各參數(shù)間用逗號(hào)隔開。實(shí)參與形參的個(gè)數(shù)應(yīng)相等,類型應(yīng)一致。實(shí)參與形參按順序?qū)?yīng),一一傳遞數(shù)據(jù)。這里,對(duì)實(shí)參列表取值的順序并不是確定的,有的系統(tǒng)按自左至右順序求實(shí)參的值,有的系統(tǒng)則按自右至左順序。
示例代碼:
#include <stdio.h>
//=======函數(shù)的聲明部分========
void test();
//========主函數(shù)===========
int main(int argc, const char * argv[]) {
//======這里可以調(diào)用函數(shù)
test();
return 0;
}
//=======函數(shù)的實(shí)現(xiàn)部分========
void test(){
printf("這是沒有返回值,沒有參數(shù)的函數(shù)\n");
}
上面的寫法,相當(dāng)于把函數(shù)的定義寫在了調(diào)用語句之前,test函數(shù)沒有參數(shù),也沒有返回值。當(dāng)被調(diào)用的函數(shù)有參數(shù)時(shí),就涉及了參數(shù)的傳遞問題。
注意事項(xiàng):被調(diào)函數(shù)必須是已經(jīng)聲明了的函數(shù),或者被調(diào)函數(shù)的位置位于調(diào)用函數(shù)之前。
在有形參的時(shí)候,函數(shù)調(diào)用的時(shí)候就會(huì)傳一個(gè)確切的實(shí)參來替代形參,這里要注意的是函數(shù)調(diào)用一般有3種用途:
① 函數(shù)語句:把函數(shù)調(diào)用作為一個(gè)語句。這時(shí)不要求函數(shù)帶返回值,只要求函數(shù)完成一定的操作,如:
printf("Hello C world\n");
② 函數(shù)表達(dá)式:函數(shù)出現(xiàn)在一個(gè)表達(dá)式中,這種表達(dá)式稱為函數(shù)表達(dá)式。這時(shí)要求函數(shù)返回一個(gè)確定的值以參加表達(dá)式的運(yùn)算,如:
sum1 = sum(a,b);
③ 函數(shù)參數(shù):函數(shù)調(diào)用作為一個(gè)函數(shù)的實(shí)參。函數(shù)調(diào)用作為函數(shù)的參數(shù),實(shí)質(zhì)上也是函數(shù)表達(dá)式形式調(diào)用的一種,因?yàn)楹瘮?shù)的參數(shù)本來就要求是表達(dá)式形式,如:
printf("a和b的和是: %d\n",sum(a, b));
2、函數(shù)的參數(shù)傳遞
函數(shù)的參數(shù)傳遞,可以分為3種方式:
- 值傳遞
- 地址傳遞
- 全局變量傳遞
2.1、值傳遞
#include <stdio.h>
void exchange(int a, int b){
int t;
printf("&a=%p &b=%p\n", &a, &b);
t = a;
a = b;
b = t;
printf("a=%d b=%d\n\n", a, b);
}
int main(int argc, const char * argv[]) {
int m = 10, n = 20;
exchange(m, n);
printf("&m=%p &n=%p\n", &m, &n);
printf("m=%d n=%d\n", m, n);
return 0;
}
輸出結(jié)果:
&a=0xbfcff4f0 &b=0xbfcff4f4
a=20 b=10
&m=0xbfcff50c &n=0xbfcff508
m=10 n=20
Program ended with exit code: 0
在該程序中,main函數(shù)調(diào)用了exchange函數(shù)。在exchange函數(shù)的定義中,參數(shù)a、b是形參,它在exchange函數(shù)體內(nèi)都可以使用,離開函數(shù)體,則不能使用。主調(diào)函數(shù)是main函數(shù),調(diào)用語句是“exchange(m,n)”,其中的m就是實(shí)參,且m是局部變量,只能在main函數(shù)體內(nèi)使用。在執(zhí)行了函數(shù)調(diào)用后,m的值就傳給了形參a,這個(gè)傳參過程自動(dòng)進(jìn)行,相當(dāng)于執(zhí)行了代碼“int a = m; int b = n”。形參和實(shí)參是兩個(gè)不同的變量,占用不同的存儲(chǔ)空間,因此,當(dāng)形參的值發(fā)生變化時(shí),并不影響實(shí)參的值。
函數(shù)的形參和實(shí)參具有以下特點(diǎn):
a.形參變量只有在被調(diào)用時(shí)才分配內(nèi)存單元,在調(diào)用結(jié)束時(shí),即刻釋放所分配的內(nèi)存單元。因此,形參只有在函數(shù)內(nèi)部有效。調(diào)用結(jié)束返回主調(diào)函數(shù)后則不能再使用該形參變量。
b.實(shí)參可以是常量、變量、表達(dá)式、函數(shù)等,無論實(shí)參是何種類型的量,在進(jìn)行函數(shù)調(diào)用時(shí),它們都必須具有確定的值,以便把這些值傳送給形參。因此應(yīng)先用賦值、輸入等辦法使實(shí)參獲得確定值。
c.實(shí)參和形參在數(shù)量上、類型上、順序上應(yīng)嚴(yán)格一致,否則會(huì)發(fā)生“類型不匹配”的錯(cuò)誤。
從圖8-1中可以看出,實(shí)參和形參所占用的存儲(chǔ)單元完全是獨(dú)立的。在函數(shù)調(diào)用時(shí),實(shí)參把存儲(chǔ)單元中的數(shù)據(jù)賦值給形參的存儲(chǔ)單元;而在函數(shù)調(diào)用后,若形參的值發(fā)生了改變,它也無法傳遞給實(shí)參(由于參數(shù)的傳遞是單向的,只能從實(shí)參傳遞給形參)。
2.2、地址傳遞
當(dāng)你希望通過形參來改變實(shí)參的值的時(shí)候,那么你可以用地址傳遞,因?yàn)樯婕暗降刂?,其?shí)就是我們以前說的指針,對(duì)于操作指針,是不是就相當(dāng)于在操作指針指向的內(nèi)存地址里面的對(duì)象。
#include <stdio.h>
void exchange(int *p, int *q){
int t;
printf("&p=%p &q=%p p=%p q=%p\n", &p, &q, p, q);
t = *p;
*p = *q;
*q = t;
printf("*p=%d *q=%d\n\n", *p, *q);
}
int main(int argc, const char * argv[]) {
int m = 10, n = 20;
exchange (&m, &n);
printf ("&m=%p &n=%p\n", &m, &n);
printf ("m=%d n=%d\n", m, n);
return 0;
}
輸出結(jié)果:
&p=0xbfeb67c0 &q=0xbfeb67c4 p=0xbfeb67dc q=0xbfeb67d8
*p=20 *q=10
&m=0xbfeb67dc &n=0xbfeb67d8
m=20 n=10
Program ended with exit code: 0
在該程序中,實(shí)參是&m和&n,形參是兩個(gè)指針。傳參的過程相當(dāng)于兩條語句“int p = &m; int q = &n;”。在函數(shù)exchange中,交換了p和q,即交換了p和q的目標(biāo),交換了變量m和n的值。在C程序中,通過傳遞變量的地址,可以起到改變主調(diào)函數(shù)中變量的作用。
2.3、全局變量傳參
全局變量就是在函數(shù)體外說明的變量,它們?cè)诔绦蛑械拿總€(gè)函數(shù)里都是可見的。實(shí)際上,全局變量也是一種靜態(tài)型的變量。將它初始化為0。全局變量一經(jīng)定義后就會(huì)在程序的任何地方可見。使用全局變量傳遞數(shù)據(jù)的先后順序的不同會(huì)影響計(jì)算結(jié)果,應(yīng)用順序不當(dāng),會(huì)導(dǎo)致錯(cuò)誤,這種方式盡量少用。(建議不要用)。
#include <stdio.h>
int n;//全局變量
double factorial();
int main(int argc, const char * argv[]) {
double s = 0;
printf("input:");
scanf("%d", &n);
s = factorial();
printf("%e\n", s);
return 0;
}
double factorial(){
double ret = 1;
int i;
for (i = 1; i <= n; i++)
ret *= i;
return ret;
}
輸出結(jié)果:
input:15
1.307674e+12
Program ended with exit code: 0
3、函數(shù)的返回值
函數(shù)的返回值是指被調(diào)用函數(shù)返回給調(diào)用函數(shù)的值。
對(duì)于函數(shù)的返回值,可以通過以下幾點(diǎn)解釋:
① 函數(shù)的返回值只能通過return語句返回主調(diào)函數(shù),return 語句的一般形式為
return 表達(dá)式;
或者
return (表達(dá)式);
該語句的功能是計(jì)算表達(dá)式的值,并返回給主調(diào)函數(shù)。在函數(shù)中允許有多個(gè)return語句,但每次調(diào)用只能有一個(gè)return語句被執(zhí)行,因此只能返回一個(gè)值。
② 函數(shù)返回值的類型和函數(shù)定義中函數(shù)的類型應(yīng)保持一致。如果兩者不一致,則以函數(shù)定義中的類型為準(zhǔn),自動(dòng)進(jìn)行類型轉(zhuǎn)換。
③ 如函數(shù)返回值為整型,在函數(shù)定義時(shí)可以省去類型說明。
④ 沒有返回值的函數(shù),可以明確定義為空類型,類型說明符為void。
#include <stdio.h>
int fun(int n);
int main(int argc, const char * argv[]) {
int sum = 0, n;
printf("input:");
scanf("%d", &n);
s = fun(n);
printf("1+2+…+%d=%n\n", n, sum);
return 0;
}
int fun(int n){
int i, sum = 0
for (i = 1; i <= n; i++)
sum += i;
return sum;
}
輸出結(jié)果:
input:100
1+2+…+100=5050
Program ended with exit code: 0
當(dāng)然,對(duì)于函數(shù),在C99標(biāo)準(zhǔn)下,系統(tǒng)還給我們提供了很多函數(shù),對(duì)于那些函數(shù)我們成為庫函數(shù),我們?cè)诔绦蛑锌梢灾苯诱{(diào)用,對(duì)于庫函數(shù),可以參考https://baike.baidu.com/item/C語言函數(shù)參考手冊(cè)/3866969?fr=aladdin
函數(shù)和數(shù)組之間的聯(lián)系
1、傳遞數(shù)組
當(dāng)形參是數(shù)組形式時(shí),其本質(zhì)上也是一個(gè)指針。數(shù)組作為實(shí)參傳遞時(shí),形參并沒有復(fù)制實(shí)參所有的內(nèi)容,而是復(fù)制了實(shí)參數(shù)組的首地址。由于數(shù)組的特殊性,只要知道了數(shù)組的首地址,就可以依次訪問數(shù)組中的所有元素。
#include <stdio.h>
int test_array(int a[], int n, int *p){
int i, sum = 0;
*p = 0;
for (i = 0; i < n; i++)
{
sum += a[i];
if (a[i] % 2)
(*p)++;
}
return sum;
}
int main(int argc, const char * argv[]) {
int a[] = {9, 12, 2, 3, 29, 31, 40, 80}, n;
int sum = 0, odd= 0;
n = sizeof(a) / sizeof(int);
sum = test_array(a, n, &odd);
printf("sum=%d add numbers count =%d\n",
sum, odd);
return 0;
}
輸出結(jié)果:
sum=206 add numbers count =4
Program ended with exit code: 0
在該程序中,子函數(shù)用來計(jì)算主調(diào)函數(shù)中的整型數(shù)組的元素的和,并統(tǒng)計(jì)數(shù)組中奇數(shù)的個(gè)數(shù)。實(shí)參傳的是數(shù)組名、數(shù)組的元素個(gè)數(shù)及一個(gè)整型的指針(除了傳遞數(shù)組名以外,還要傳遞數(shù)組中元素的個(gè)數(shù))。形參是一維數(shù)組的形式,在這里,讀者要特別注意:當(dāng)形參是數(shù)組形式時(shí),本質(zhì)是同級(jí)別的指針。該程序中的形參int a[],實(shí)際上是int *a。上面程序演示的是一維數(shù)組,對(duì)于二維數(shù)組這里就不演示了,其實(shí)差不多。讀者可以自行去摸索。
溫馨提示:當(dāng)形參是數(shù)組形式時(shí),本質(zhì)是同級(jí)別的指針。
2、傳遞指針
前面介紹了函數(shù)形參是數(shù)組形式的用法,下面介紹另一種很常見的寫法即傳遞指針。其實(shí),讀者如果熟練掌握了上一章中數(shù)組和指針部分的內(nèi)容,這里就很容易理解。若需要給子函數(shù)傳遞一維數(shù)組,可以寫成下面的形式:
int test_array(int *a, int n, int p)
若需要給子函數(shù)傳遞二維數(shù)組,應(yīng)該寫成下面的形式:
int test_array(int n, int m, int (a)[m], int p)
在這種形式中,int (a)[m]是一種數(shù)組指針或稱為行指針,指針加1,移動(dòng)m個(gè)數(shù)據(jù),和m列的二維數(shù)組名是類似的。這就就提這么多,下篇文章會(huì)詳細(xì)講解。
main函數(shù)的參數(shù)介紹
普通函數(shù)可以帶參數(shù),其實(shí),main函數(shù)也可以帶參數(shù)。當(dāng)執(zhí)行程序時(shí),也可以在命令行上給main函數(shù)傳參。完整的main函數(shù)原型如下:
int main(int argc, const char *argv[])
上面是數(shù)組的形式,也可以寫成指針的形式:
int main(int argc, char **argv)
其中,argc是傳給main函數(shù)的參數(shù)的個(gè)數(shù),argv指向具體的參數(shù)。
#include <stdio.h>
int main(int argc,const char *argv[]){
int i;
printf("argc=%d\n", argc);
for (i = 0; i < argc; i++)
printf("argv[%d]=%s\n", i, argv[i]);
return 0;
}
輸出結(jié)果:
argc=1
argv[0]=...(這里打印的是路徑)Build/Products/Debug/函數(shù)的簡單示例
Program ended with exit code: 0
總結(jié)
這篇文章對(duì)剛接觸C語言函數(shù)的讀者應(yīng)該有一個(gè)很好的幫助,這篇文章將的是基礎(chǔ)和理論,下一篇文章將講解函數(shù)的重點(diǎn),當(dāng)然也希望讀者能夠認(rèn)真的閱讀,這里面有很多細(xì)節(jié)的,望讀者揣摩。
結(jié)尾
希望讀者真誠的對(duì)待每一件事情,每天都能學(xué)到新的知識(shí)點(diǎn),要記住,認(rèn)識(shí)短暫,開心也是過一天,不開心也是一天,無所事事也是一天,小小收獲也是一天,所以,你的路你自己選擇。