|
一、函數(shù)基礎(chǔ)知識(shí)
1、函數(shù)的聲明
函數(shù)只能定義一次,但可以聲明多次。建議在頭文件中聲明而在源文件定義。函數(shù)的聲明和函數(shù)的定義非常類似,唯一的區(qū)別是函數(shù)的聲明無(wú)須函數(shù)體,用一個(gè)分號(hào)替代即可。
2、函數(shù)的定義
函數(shù)包括以下部分:返回類型、函數(shù)名字、由0個(gè)或多個(gè)形參組成的列表以及函數(shù)體。
returntype functionname (type parameter1,type parameter2…) {……}
函數(shù)的形參列表:函數(shù)的形參列表可以為空,但是不能省略。void f1()、voidf2(void)
3、形參的基礎(chǔ)知識(shí)
形參和實(shí)參:實(shí)參是形參的初始值。第一個(gè)實(shí)參初始化第一個(gè)形參,第n個(gè)實(shí)參初始化第n個(gè)形參。
局部變量:形參和函數(shù)體內(nèi)部定義的變量統(tǒng)稱為局部變量,僅在函數(shù)的作用域內(nèi)可見(jiàn)。
自動(dòng)對(duì)象:把只存在于塊執(zhí)行期間的對(duì)象稱為自動(dòng)對(duì)象。形參是一種自動(dòng)對(duì)象,所以函數(shù)一旦結(jié)束,形參也就被銷(xiāo)毀。
局部靜態(tài)變量:有時(shí)有必要令局部變量的生命周期貫穿函數(shù)調(diào)用及之后的時(shí)間?梢詫⒕植孔兞慷x成static從而獲得這樣的對(duì)象。局部靜態(tài)對(duì)象在程序的執(zhí)行路徑第一次經(jīng)過(guò)對(duì)象定義語(yǔ)句時(shí)初始化,并且直到程序終止才被銷(xiāo)毀,在此期間即使對(duì)像所在的函數(shù)結(jié)束執(zhí)行也不會(huì)對(duì)它有影響。
二、參數(shù)傳遞
1、函數(shù)傳參的三種方式:傳值傳遞、地址傳遞、引用傳遞
(1)傳值傳遞
(1)傳值傳遞即將實(shí)參的值拷貝給形參。形參和實(shí)參是兩個(gè)相互獨(dú)立的對(duì)象,各占一個(gè)獨(dú)立的存儲(chǔ)空間。
(2)形參的存儲(chǔ)空間是函數(shù)被調(diào)用時(shí)才分配的,調(diào)用開(kāi)始,系統(tǒng)為形參開(kāi)辟一個(gè)臨時(shí)的存儲(chǔ)區(qū),然后將各實(shí)參傳遞給形參,這是形參就得到了實(shí)參的值。
示例代碼:
#include<stdio.h>
void swap1(int x, inty)//定義中的x,y變量是swap函數(shù)的兩個(gè)形參
{
int tmp;
tmp = x;
x = y;
y = tmp;
printf("x=%d,y=%d\n", x, y);
}
int main()
{
int a = 2;
int b = 3;
swap1(a, b);//a,b變量為swap函數(shù)的實(shí)際參數(shù)
printf("a=%d,b=%d", a, b);
return 0;
}
輸出結(jié)果為:x=3,y=2; a=2,b=3
代碼分析:a雖然賦值給x,但是a的值并沒(méi)有改變,對(duì)x的任何修改都不會(huì)改變a的值。函數(shù)是通過(guò)賦值把a(bǔ),b賦給x,y,這是一個(gè)隱含操作,我們不能把它顯式的寫(xiě)出來(lái),進(jìn)行函數(shù)中變量的值進(jìn)行交換時(shí)只是形參x,y的交換,并沒(méi)有對(duì)實(shí)參進(jìn)行真正交換,所以a,b值不變。
(2)地址傳遞
因?yàn)橹羔樖刮覀兛梢蚤g接地訪問(wèn)它所指的對(duì)象,所以通過(guò)指針可以修改它所指對(duì)象的值。
地址傳遞與值傳遞的不同在于,它把實(shí)參的存儲(chǔ)地址傳送給形參,使得形參指針和實(shí)參指針指向同一塊地址。因此,被調(diào)用函數(shù)中對(duì)形參指針?biāo)赶虻牡刂分袃?nèi)容的任何改變都會(huì)影響到實(shí)參。
示例代碼:
void swap2(int *px, int*py)
{
int tmp;
tmp = *px;
*px = *py;
*py = tmp;
printf("px=%d,py=%d\n", *px, *py);
}
int main()
{
int a = 2;
int b = 3;
swap2(&a, &b);/*調(diào)用了swap函數(shù),同樣也有隱含動(dòng)作px=&a;py=&b;*/
printf("a=%d,b=%d", a, b);
return 0;
}
運(yùn)行結(jié)果為px=3,py=2;a=3,b=2;
代碼分析:有了兩行隱含賦值操作,我們可以清晰的看出指針*px,*py是對(duì)變量a,b的值操作。函數(shù)里面對(duì)a和b的值進(jìn)行了交換。這就是傳址。
(3)引用傳遞
引用傳遞是以引用為參數(shù),則既可以使得對(duì)形參的任何操作都能改變相應(yīng)數(shù)據(jù),又使函數(shù)調(diào)用方便。引用傳遞是在形參調(diào)用前加入引用運(yùn)算符“&”。引用為實(shí)參的別名,和實(shí)參是同一個(gè)變量,則他們的值也相同,該引用改變則它的實(shí)參也改變。
代碼示例:
#include<stdio.h>
void swap3(int &x,int &y)
{
int tmp = x;
x = y;
y = tmp;
printf("x=%d,y=%d\n", x, y);
}
int main()
{
int a = 2;
int b = 3;
swap3(a, b);//調(diào)用方式與傳值一樣
printf("a=%d,b=%d", a, b);
system("pause");
return 0;
}
輸出結(jié)果:x=3,y=2; a=3,b=2;
代碼分析:我們看到該代碼只與傳值中swap函數(shù)定義不同,swap3中參數(shù)都加了取地址符號(hào)&,有了這個(gè)函數(shù)會(huì)將a,b分別替代了x,y,這樣函數(shù)里面操作就是a,b本身了。
2、結(jié)論
(1)除非實(shí)參是具體數(shù)值(5等)或者實(shí)參和形參是相同的名字外,避免使用傳值傳遞。
(2)當(dāng)某種類型不支持拷貝時(shí),函數(shù)只能通過(guò)引用形參訪問(wèn)該類型的對(duì)象。
(3) 一個(gè)函數(shù)只能返回一個(gè)值,然而有時(shí)候函數(shù)需要同時(shí)返回多個(gè)值,引用形參為我們一次返回多個(gè)結(jié)果提供了有效的途徑。
三、數(shù)組形參
1、數(shù)組的兩個(gè)特殊性質(zhì):
①不允許拷貝數(shù)組(arry1=arry2):因?yàn)椴荒芸截悢?shù)組,所以無(wú)法以值傳遞的方式使用數(shù)組參數(shù)。
②使用數(shù)組時(shí)會(huì)將其轉(zhuǎn)換成指針:因?yàn)閿?shù)組會(huì)被轉(zhuǎn)換成指針,所以當(dāng)為函數(shù)傳遞一個(gè)數(shù)組時(shí),實(shí)際傳遞的是只想數(shù)組首元素的指針。
2、數(shù)組數(shù)據(jù)的傳送
要確定一個(gè)一維數(shù)組只需要知道:一個(gè)是數(shù)組的首地址,另一個(gè)是數(shù)組的長(zhǎng)度,這樣就可以唯一地確定一個(gè)一維數(shù)組。因此,要想通過(guò)實(shí)參和形參將一個(gè)數(shù)組從主調(diào)函數(shù)傳到被調(diào)函數(shù),那么只需要傳遞這兩個(gè)信息即可。
因?yàn)閿?shù)組是連續(xù)存放的,只要知道數(shù)組的首地址和數(shù)組的長(zhǎng)度就能找到這個(gè)數(shù)組中所有的元素。對(duì)于一維數(shù)組來(lái)說(shuō),其數(shù)組名就表示一維數(shù)組的首地址。所以只需要傳遞數(shù)組名和數(shù)組長(zhǎng)度這兩個(gè)參數(shù)就可以將數(shù)組從主調(diào)函數(shù)傳入被調(diào)函數(shù)中。
當(dāng)數(shù)組名作為函數(shù)的實(shí)參時(shí),形參列表中也應(yīng)定義相應(yīng)的數(shù)組(或用指針變量),且定義數(shù)組的類型必須與實(shí)參數(shù)組的類型一致,如果不一致就會(huì)出錯(cuò)。但形參中定義的數(shù)組無(wú)須指定數(shù)組的長(zhǎng)度,而是再定義一個(gè)參數(shù)用于傳遞數(shù)組的長(zhǎng)度。所以在傳遞實(shí)參的時(shí)候,數(shù)組名和數(shù)組長(zhǎng)度也只能用兩個(gè)參數(shù)分開(kāi)傳遞,而不能寫(xiě)在一起。因?yàn)榧词箤?xiě)在一起,系統(tǒng)在編譯時(shí)也只是檢查數(shù)組名,并不會(huì)檢查數(shù)組長(zhǎng)度。所以數(shù)組長(zhǎng)度要額外定義一個(gè)變量進(jìn)行傳遞。
當(dāng)將數(shù)組從一個(gè)函數(shù)傳到另一個(gè)函數(shù)中時(shí),并不是將數(shù)組中所有的元素一個(gè)一個(gè)傳過(guò)來(lái)(那樣效率就太低了)。而是將能夠唯一確定一個(gè)數(shù)組的信息傳過(guò)來(lái),即數(shù)組名(數(shù)組首地址)和數(shù)組長(zhǎng)度。此時(shí)主調(diào)函數(shù)和被調(diào)函數(shù)操作的就是同一個(gè)數(shù)組。
數(shù)組名就是數(shù)組的首地址。因此在數(shù)組名作函數(shù)參數(shù)時(shí)所進(jìn)行的傳送只是地址的傳送,也就是說(shuō)把實(shí)參數(shù)組的首地址賦予形參數(shù)組名。形參數(shù)組名取得該首地址之后,也就等于有了實(shí)在的數(shù)組。實(shí)際上是形參數(shù)組和實(shí)參數(shù)組為同一數(shù)組,共同擁有一段內(nèi)存空間。
當(dāng)用數(shù)組名作函數(shù)參數(shù)時(shí),情況則不同。由于實(shí)際上形參和實(shí)參為同一數(shù)組,因此當(dāng)形參數(shù)組發(fā)生變化時(shí),實(shí)參數(shù)組也隨之變化。當(dāng)然這種情況不能理解為發(fā)生了“雙向”的值傳遞。但從實(shí)際情況來(lái)看,調(diào)用函數(shù)之后實(shí)參數(shù)組的值將由于形參數(shù)組值的變化而變化。
一般來(lái)數(shù)參數(shù)的傳遞是值傳遞,也就是說(shuō)實(shí)參傳給形參,形參發(fā)生改變時(shí)實(shí)參并不會(huì)改變,(單向)。但是數(shù)組在傳遞的時(shí)候是地址傳遞,只要形參發(fā)生了變化,實(shí)參也會(huì)發(fā)生變化(“雙向”)。
3、管理指針形參
因?yàn)閿?shù)組是以指針的形式傳遞給函數(shù)的,所以開(kāi)始函數(shù)并不知道數(shù)組的確切大小,調(diào)用者應(yīng)該為此提供一些額外的信息。管理指針形參有三種常用技術(shù):
(1)使用標(biāo)記指定數(shù)組長(zhǎng)度
指要求數(shù)組本身包含一個(gè)結(jié)束字符。這種方法適用于有明顯結(jié)束標(biāo)記且該標(biāo)記不會(huì)與普通數(shù)據(jù)混淆的情況,倒是對(duì)像int這樣所有取值都是合法值的數(shù)據(jù)就不太有效了。
void printf(char *cp)
{
if(cp) /*若數(shù)組不是一個(gè)空指針*/
while(*cp) /*只要指針?biāo)傅淖址皇强兆址?/
cout<<*cp++;
}
(2)使用標(biāo)準(zhǔn)庫(kù)規(guī)范
指?jìng)鬟f指向數(shù)組首元素和尾后元素的指針。為了調(diào)用這個(gè)函數(shù),需要定義兩個(gè)指針:一個(gè)指向要輸出的首元素,另一個(gè)指向尾元素的下一位置。
(3)顯式傳遞一個(gè)表示數(shù)組大小的形參
指專門(mén)定義一個(gè)表示數(shù)組大小的形參。
# include<stdio.h>
int AddArray(intarray[], int n); //函數(shù)聲明
int main(void)
{
inta[] = {1, 2, 3, 4, 5, 6, 7, 8};
/*數(shù)組所占內(nèi)存總大小除以該數(shù)組中一個(gè)元素所占內(nèi)存的大小, 從而得到數(shù)組元素的個(gè)數(shù)*/
int size = sizeof(a) / sizeof(a[0]);
printf("sum = %d\n", AddArray(a,size));
return 0;
}
int AddArray(intarray[], int n) //形參數(shù)組中不需要寫(xiě)長(zhǎng)度
{
int i, sum = 0;
for (i=0; i<n; ++i)
{
sum += array[ i];
[ i]
}
return sum;
}
輸出結(jié)果:sum=36
int test1(int *p)
{
for(int i=0;i<5;i++)
{
printf("%d",p[ i]); //我們?cè)谶@里還可以用)*(p+i)來(lái)輸出數(shù)組中的值
[ i]
}
}
int test2(int a[])
{
for(int i=0;i<5;i++)
{
printf("%d",a[ i]);
[ i]
}
}
int main()
{
int a[5] = {1,2,3,4,5},*p;
p = a;
test1(p);
test2(a);
}
#include<stdio.h>
floataver(float a[5]){
int i;
float av,s=a[0];
for(i=1;i<5;i++)
s=s+a[ i];
[ i]
av=s/5;
return av;
}
intmain(void){
float sco[5],av;
int i;
printf("\ninput 5 scores:\n");
for(i=0;i<5;i++)
scanf("%f",&sco[ i]);
[ i]
av=aver(sco);
printf("average score is%5.2f",av);
return 0;
}
在函數(shù)形參表中,允許不給出形參數(shù)組的長(zhǎng)度,或用一個(gè)變量來(lái)表示數(shù)組元素的個(gè)數(shù)。例如,可以寫(xiě)為:
void nzp(int a[])
或?qū)憺?br />
void nzp( int a[], int n )
其中形參數(shù)組a沒(méi)有給出長(zhǎng)度,而由n值動(dòng)態(tài)地表示數(shù)組的長(zhǎng)度。n的值由主調(diào)函數(shù)的實(shí)參進(jìn)行傳送。
4、傳遞多維數(shù)組
多維數(shù)組也可以作為函數(shù)的參數(shù)。在函數(shù)定義時(shí)對(duì)形參數(shù)組可以指定每一維的長(zhǎng)度,也可省去第一維的長(zhǎng)度。因此,以下寫(xiě)法都是合法的:
int MA(int a[3][10]) 或 int MA(int a[][10])。
因?yàn)槎嗑S數(shù)組是數(shù)組的數(shù)組,所以首元素本身就是一個(gè)數(shù)組,指針就是一個(gè)指向數(shù)組的指針。數(shù)組第二維(以及后面所有維度)的大小都是數(shù)組類型的一部分,不能省略。
Void fun1( int (*matrix)[10], introwsize ) {……}
Void fun1( int matrix[][10], introwsize ) {……}
靈活的去定位到某一行某一列:
void test2(int m,int n,int **p)
{
//m,n是行和列,
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
printf("%d ",*((int*)p+n*i+j));
}
}
}
int main()
{
int a[5][5],i,j;
for(i = 0; i < 5; i++)
{
for(j = 0; j < 5; j++)
{
a[ i][j] = i*5 + (j +1);
[ i]
}
}
test2(5,5,(int **)a);
return 0;
}
或者:
#include<stdio>
void test2(int m,int n,int *p)
{
//m,n是行和列,
for(int i = 0; i<m; i++)
{
for(int j = 0; j < n; j++)
{
printf("%d ",*(p+n*i+j));
}
}
}
void main()
{
int a[5][5],i,j;
for(i = 0; i <5; i++)
{
for(j = 0; j < 5; j++)
{
a[ i][j] = i*5 + (j +1);
[ i]
}
}
test2(5,5,(int *)a);
}
|
|