C語言入門(十三)函數參數的傳遞和值返回

 函數參數的傳遞和值返回 

前言:

前面我們說的都是無參數無返回值的函數,實際程序中,我們經常使用到帶參數有返回值的函數。



一、函數參數傳遞
1.形式參數和實際參數
函數的調用值把一些表達式作爲參數傳遞給函數。函數定義中的參數是形式參數,函數的調用者提供給函數的參數叫實際參數。在函數調用之前,實際參數的值將被拷貝到這些形式參數中。
2.參數傳遞
先看一個例子:
void a(int); /*注意函數聲明的形式*/
main()
{
int num;
scanf(%d,&num);
a(num); /*注意調用形式*/
}
void a(int num_back) /*注意定義形式*/
{
printf(%d\n,num_back);
}
在主函數中,先定義一個變量,然後輸入一個值,在a()這個函數中輸出。當程序運行a(num);這一步時,把num的值賦值給num_back,在運行程序過程中,把實際參數的值傳給形式參數,這就是函數參數的傳遞。
形參和實參可能不只一個,如果多於一個時,函數聲明、調用、定義的形式都要一一對應,不僅個數要對應,參數的數據類型也要對應。
void a(int,float);
main()
{
int num1;
float num2;
scanf(%d,&num1);
scanf(%f,&num2);
a(num1,num2);
}
void a(int num1_back,float num2_back)
{
printf(%d,%f\n,num1_back,num2_back);
}
上面的例子中,函數有兩個參數,一個是整型,一個是浮點型,那麼在聲明、調用、定義的時候,不僅個數要一樣,類型也要對應。如果不對應,有可能使的編譯錯誤,即使沒錯誤,也有可能讓數據傳遞過程中出現錯誤。
再看一個例子:
void a(int);
main()
{
int num;
scanf(%d,&num);
a(num);
}
void a(int num)
{
printf(%d\n,num);
}
看上面的例子,形式參數和實際參數的標識符都是num,程序把實際參數num的值傳遞給形式參數num。有些人可能就不明白了,既然兩個都是num,爲什麼還要傳遞呢?乾脆這樣不就行了嗎:
void a();
main()
{
int num;
scanf(%d,&num);
a();
}
void a()
{
printf(%d\n,num);
}
其實不然,這就要涉及到標識符作用域的問題。作用域的意思就是說,哪些變量在哪些範圍內有效。一個標識符在一個語句塊中聲明,那麼這個標識符僅在當前和更低的語句塊中可見,在函數外部的其實地方不可見,其他地方同名的標識符不受影響,後面我們會系統講解作用域的問題。在這兒你就要知道兩個同名的變量在不同的函數中是互不干擾的。
前面將的都是變量與變量之間的值傳遞,其實函數也可以傳遞數組之間的值。看下面的例子:
void a(int []);
main()
{
int array[5],i;
for(i=0;i<5;i++) scanf(%d,&array[i]);
a(array);
}
void a(int array[])
{
int i;
for(i=0;i<5;i++) printf(%d\t,array[i]);
printf(\n);
}
這就是數組之間的值傳遞。注意他們的聲明和定義形式,和變量參數傳遞有什麼區別?有了後面的[]就表明傳遞的是一個數組。其中在定義的時候,也可以寫成void a(int array[5]);想想,如果我們寫成了int array[4]會有什麼情況發生?
目前我們只學了數組和變量,以後還會知道指針、結構,到那是,函數也可以傳遞它們之間的值。


二、函數值的返回
其實我們也可以把函數當作一個變量來看,既然是變量,那一定也可以有類型。還舉最前面的例子,現在要求在main()函數裏輸入一個整數作爲正方形的邊長,在子函數裏求正方形的面積,然後再在主函數裏輸出這個面積。
我們前面的程序都是在子函數裏輸出的,現在要求在主函數裏輸出,這就需要把算好的值返回回來。先看例子:
int a(int); /*聲明函數*/
main()
{
int num,area;
scanf(%d,&num);
area=a(num); /*調用時的形式*/
printf(%d,area);
}
int a(int num)
{
int area_back;
area_back=num*num;
return area_back; /*返回一個值*/
}
和前面的程序有幾點不同:
(1).聲明函數類型時,不是void,而是int。這是由於最後要求的面積是整型的,所以聲明函數的返回值類型是整型。
(2).return語句 它的意思就是返回一個值。在C語言中,return一定是在函數的最後一行。
(3).調用函數的時候,由於函數有一個返回值,所以必須要用變量接受這個返回值(不是絕對的),如果我們不用一個變量接受這個值,函數還照樣返回,但是返回的這個值沒有使用。

上面的例子運行過程是這樣的,先把實參的值傳遞給形參,然後在子函數裏計算面積得到area_back,然後返回這個面積到主函數,也就是把area_back賦值給area,最後輸出。
前面說了,返回值有時不一定非要用一個變量來接受,我們可以把上面的程序簡化爲:
int a(int);
main()
{
int num;
scanf(%d,&num);
printf(%d,a(num)); /*函數調用放在這兒*/
}
int a(int num)
{
int area_back;
area_back=num*num;
return area_back;
}
這樣函數返回的值就可以直接放到輸出緩衝區直接輸出了。
還可以再簡化爲:
int a(int);
main()
{
int num;
scanf(%d,&num);
printf(%d,a(num));
}
int a(int num)
{
return num*num; /*直接在這兒返回*/
}
對於函數而言,一個函數只能返回一個值,如果想返回一組數值,就要使用數組或者結構或者指針。其實對於這些,還是返回一個值,只是這個值是一個地址而已。但是對於數組的返回有和變量不同,因爲數組和地址是聯繫在一起的。看一個例子:
void a(int []);
main()
{
int array[5]={1,2,3,4,5},i;
a(array);
for(i=0;i<5;i++) printf(%d,array[i]);
}
void a(int array[])
{
int i;
for(i=0;i<5;i++) array[i]++;
}
看看這個程序,好象函數沒有返回值,但是函數的功能的確實現了,在主函數當中輸出的值的確都各加了1上來。這就是因爲數組和變量不同的緣故,在後面講指針的時候再詳細說明。
下面看一個實際例子,加深對函數的理解:
用函數實現,判斷一個整數是不是素數?在主函數裏輸入輸出,子函數裏判斷。
#include math.h
int judge(int);
main()
{
int num,result;
scanf(%d,&num);
result=judge(num);
if(result==1) printf(yes\n);
else printf(no\n);
}
judge(int num)
{
int i,flag=1;
for(i=2;i<=sqrt(num);i++)
if(num%i==0)
{
flag=0;
break;
}
return flag;
}
可以看出,函數的功能就是爲了讓程序看起來有條理,一個函數實現一個特定的功能。如果我們還和以前那樣,把所有代碼都放在main()函數,好象程序就顯的臃腫了。而且函數有一個顯著的好處就是很方便的使用。這裏面的judge()函數判斷一個數是不是素數,如果我們以後還有判斷某個數是不是素數,就可以直接使用這個函數了。我們這樣,把下面的代碼:
judge(int num)
{
int i,flag=1;
for(i=2;i<=sqrt(num);i++)
if(num%i==0)
{
flag=0;
break;
}
return flag;
}
保存爲judge.h文件,放到include目錄裏面。
以後就可以直接使用這個函數了,就好象直接使用abs(),sqrt()這些函數一樣方便。
#include math.h /*必須要有它*/
#include judge.h
main()
{
int num,result;
scanf(%d,&num);
result=judge(num);
if(result==1) printf(yes\n);
else printf(no\n);
}
看上面的例子,我們在程序中直接使用了函數judge(),這就是我們自己編寫的第一個所謂的庫函數。但是程序的第一行要包含math.h文件,這是因爲在judge.h裏面使用了sqrt()函數,所以爲了方便,我們可以把math.h放到judge.h裏面,也就是在judge.h文件的第一行加上include math.h,這樣,我們的主程序中就不需要包含它了,但是這樣做也有副作用,具體有什麼副作用,我們以後接觸到時再介紹。
我們實際用到的一些程序,也許代碼有很長,上千行,甚至上萬行,這些代碼不可能放在一個*.c文件中,所以我們經常把一些功能做成*.h,*c的文件形式,然後在主程序中包含這些文件,這樣就把一個大程序分割成幾個小塊,不僅瀏覽方便,對以後的修改也有很多好處。
我們在平時就應該有這樣的好習慣,把一些經常使用的功能做成庫函數的形式保存下來,也許剛開始你會覺得很煩瑣,可到了後來,也許幾年過去了,你會發現,一個好幾千行上萬行的程序,有一大半的功能你都有,直接調用就可,這會大大縮短你的程序開發週期的。就好象這裏的判斷素數一樣,如果以後還需要判斷一個數是不是素數,就沒必要再寫那些代碼了,直接調用judge()函數就可。
發佈了101 篇原創文章 · 獲贊 540 · 訪問量 74萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章