一、函數基礎知識
1、函數的聲明
函數只能定義一次,但可以聲明多次。建議在頭文件中聲明而在源文件定義。函數的聲明和函數的定義非常類似,唯一的區別是函數的聲明無須函數體,用一個分號替代即可。
2、函數的定義
函數包括以下部分:返回類型、函數名字、由0個或多個形參組成的列表以及函數體。
returntype functionname (type parameter1,type parameter2…) {……}
函數的形參列表:函數的形參列表可以為空,但是不能省略。void f1()、voidf2(void)
3、形參的基礎知識
形參和實參:實參是形參的初始值。第一個實參初始化第一個形參,第n個實參初始化第n個形參。
局部變量:形參和函數體內部定義的變量統稱為局部變量,僅在函數的作用域內可見。
自動對象:把只存在于塊執行期間的對象稱為自動對象。形參是一種自動對象,所以函數一旦結束,形參也就被銷毀。
局部靜態變量:有時有必要令局部變量的生命周期貫穿函數調用及之后的時間。可以將局部變量定義成static從而獲得這樣的對象。局部靜態對象在程序的執行路徑第一次經過對象定義語句時初始化,并且直到程序終止才被銷毀,在此期間即使對像所在的函數結束執行也不會對它有影響。
二、參數傳遞
1、函數傳參的三種方式:傳值傳遞、地址傳遞、引用傳遞
(1)傳值傳遞
(1)傳值傳遞即將實參的值拷貝給形參。形參和實參是兩個相互獨立的對象,各占一個獨立的存儲空間。
(2)形參的存儲空間是函數被調用時才分配的,調用開始,系統為形參開辟一個臨時的存儲區,然后將各實參傳遞給形參,這是形參就得到了實參的值。
示例代碼:
#include<stdio.h>
void swap1(int x, inty)//定義中的x,y變量是swap函數的兩個形參
{
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函數的實際參數
printf("a=%d,b=%d", a, b);
return 0;
}
輸出結果為:x=3,y=2; a=2,b=3
代碼分析:a雖然賦值給x,但是a的值并沒有改變,對x的任何修改都不會改變a的值。函數是通過賦值把a,b賦給x,y,這是一個隱含操作,我們不能把它顯式的寫出來,進行函數中變量的值進行交換時只是形參x,y的交換,并沒有對實參進行真正交換,所以a,b值不變。
(2)地址傳遞
因為指針使我們可以間接地訪問它所指的對象,所以通過指針可以修改它所指對象的值。
地址傳遞與值傳遞的不同在于,它把實參的存儲地址傳送給形參,使得形參指針和實參指針指向同一塊地址。因此,被調用函數中對形參指針所指向的地址中內容的任何改變都會影響到實參。
示例代碼:
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);/*調用了swap函數,同樣也有隱含動作px=&a;py=&b;*/
printf("a=%d,b=%d", a, b);
return 0;
}
運行結果為px=3,py=2;a=3,b=2;
代碼分析:有了兩行隱含賦值操作,我們可以清晰的看出指針*px,*py是對變量a,b的值操作。函數里面對a和b的值進行了交換。這就是傳址。
(3)引用傳遞
引用傳遞是以引用為參數,則既可以使得對形參的任何操作都能改變相應數據,又使函數調用方便。引用傳遞是在形參調用前加入引用運算符“&”。引用為實參的別名,和實參是同一個變量,則他們的值也相同,該引用改變則它的實參也改變。
代碼示例:
#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);//調用方式與傳值一樣
printf("a=%d,b=%d", a, b);
system("pause");
return 0;
}
輸出結果:x=3,y=2; a=3,b=2;
代碼分析:我們看到該代碼只與傳值中swap函數定義不同,swap3中參數都加了取地址符號&,有了這個函數會將a,b分別替代了x,y,這樣函數里面操作就是a,b本身了。
2、結論
(1)除非實參是具體數值(5等)或者實參和形參是相同的名字外,避免使用傳值傳遞。
(2)當某種類型不支持拷貝時,函數只能通過引用形參訪問該類型的對象。
(3) 一個函數只能返回一個值,然而有時候函數需要同時返回多個值,引用形參為我們一次返回多個結果提供了有效的途徑。
三、數組形參
1、數組的兩個特殊性質:
①不允許拷貝數組(arry1=arry2):因為不能拷貝數組,所以無法以值傳遞的方式使用數組參數。
②使用數組時會將其轉換成指針:因為數組會被轉換成指針,所以當為函數傳遞一個數組時,實際傳遞的是只想數組首元素的指針。
2、數組數據的傳送
要確定一個一維數組只需要知道:一個是數組的首地址,另一個是數組的長度,這樣就可以唯一地確定一個一維數組。因此,要想通過實參和形參將一個數組從主調函數傳到被調函數,那么只需要傳遞這兩個信息即可。
因為數組是連續存放的,只要知道數組的首地址和數組的長度就能找到這個數組中所有的元素。對于一維數組來說,其數組名就表示一維數組的首地址。所以只需要傳遞數組名和數組長度這兩個參數就可以將數組從主調函數傳入被調函數中。
當數組名作為函數的實參時,形參列表中也應定義相應的數組(或用指針變量),且定義數組的類型必須與實參數組的類型一致,如果不一致就會出錯。但形參中定義的數組無須指定數組的長度,而是再定義一個參數用于傳遞數組的長度。所以在傳遞實參的時候,數組名和數組長度也只能用兩個參數分開傳遞,而不能寫在一起。因為即使寫在一起,系統在編譯時也只是檢查數組名,并不會檢查數組長度。所以數組長度要額外定義一個變量進行傳遞。
當將數組從一個函數傳到另一個函數中時,并不是將數組中所有的元素一個一個傳過來(那樣效率就太低了)。而是將能夠唯一確定一個數組的信息傳過來,即數組名(數組首地址)和數組長度。此時主調函數和被調函數操作的就是同一個數組。
數組名就是數組的首地址。因此在數組名作函數參數時所進行的傳送只是地址的傳送,也就是說把實參數組的首地址賦予形參數組名。形參數組名取得該首地址之后,也就等于有了實在的數組。實際上是形參數組和實參數組為同一數組,共同擁有一段內存空間。
當用數組名作函數參數時,情況則不同。由于實際上形參和實參為同一數組,因此當形參數組發生變化時,實參數組也隨之變化。當然這種情況不能理解為發生了“雙向”的值傳遞。但從實際情況來看,調用函數之后實參數組的值將由于形參數組值的變化而變化。
一般來數參數的傳遞是值傳遞,也就是說實參傳給形參,形參發生改變時實參并不會改變,(單向)。但是數組在傳遞的時候是地址傳遞,只要形參發生了變化,實參也會發生變化(“雙向”)。
3、管理指針形參
因為數組是以指針的形式傳遞給函數的,所以開始函數并不知道數組的確切大小,調用者應該為此提供一些額外的信息。管理指針形參有三種常用技術:
(1)使用標記指定數組長度
指要求數組本身包含一個結束字符。這種方法適用于有明顯結束標記且該標記不會與普通數據混淆的情況,倒是對像int這樣所有取值都是合法值的數據就不太有效了。
void printf(char *cp)
{
if(cp) /*若數組不是一個空指針*/
while(*cp) /*只要指針所指的字符不是空字符*/
cout<<*cp++;
}
(2)使用標準庫規范
指傳遞指向數組首元素和尾后元素的指針。為了調用這個函數,需要定義兩個指針:一個指向要輸出的首元素,另一個指向尾元素的下一位置。
(3)顯式傳遞一個表示數組大小的形參
指專門定義一個表示數組大小的形參。
# include<stdio.h>
int AddArray(intarray[], int n); //函數聲明
int main(void)
{
inta[] = {1, 2, 3, 4, 5, 6, 7, 8};
/*數組所占內存總大小除以該數組中一個元素所占內存的大小, 從而得到數組元素的個數*/
int size = sizeof(a) / sizeof(a[0]);
printf("sum = %d\n", AddArray(a,size));
return 0;
}
int AddArray(intarray[], int n) //形參數組中不需要寫長度
{
int i, sum = 0;
for (i=0; i<n; ++i)
{
sum += array[ i];
[ i]
}
return sum;
}
輸出結果:sum=36
int test1(int *p)
{
for(int i=0;i<5;i++)
{
printf("%d",p[ i]); //我們在這里還可以用)*(p+i)來輸出數組中的值
[ 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;
}
在函數形參表中,允許不給出形參數組的長度,或用一個變量來表示數組元素的個數。例如,可以寫為:
void nzp(int a[])
或寫為
void nzp( int a[], int n )
其中形參數組a沒有給出長度,而由n值動態地表示數組的長度。n的值由主調函數的實參進行傳送。
4、傳遞多維數組
多維數組也可以作為函數的參數。在函數定義時對形參數組可以指定每一維的長度,也可省去第一維的長度。因此,以下寫法都是合法的:
int MA(int a[3][10]) 或 int MA(int a[][10])。
因為多維數組是數組的數組,所以首元素本身就是一個數組,指針就是一個指向數組的指針。數組第二維(以及后面所有維度)的大小都是數組類型的一部分,不能省略。
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);
}
|