函數(shù)是任何一門語(yǔ)言中必不可少的部分,正是由這些函數(shù)組成了程序。首先談一下C語(yǔ)言中的函數(shù)指針與指針函數(shù),再了解一下函數(shù)參數(shù)傳遞的相關(guān)原理。
1.函數(shù)指針與指針函數(shù)
(1) 函數(shù)指針 即指向這個(gè)函數(shù)的指針,定義為 數(shù)據(jù)類型 (*fun)(參數(shù)列表) ,()的優(yōu)先級(jí)比*高,所以*fun加括號(hào)。如 void (*fun)(int*,int*);
(2)指針函數(shù) 即返回值是指針的函數(shù),定義為 數(shù)據(jù)類型 * fun(參數(shù)列表). 如 char* fun(int*,int*);即返回值為char*型。
在C語(yǔ)言中,變量有它的地址,同理函數(shù)也是有地址的。那么把函數(shù)的地址賦給函數(shù)指針,再通過(guò)函數(shù)指針調(diào)用這個(gè)函數(shù)就可以了。
第一步: 定義函數(shù)指針,如 int (*pfun)(int*,int*);
第二步: 定義函數(shù) 如 int fun(int*,int*);
第三步: 把函數(shù)的地址賦給函數(shù)指針,即 pfun=fun;
第四步: 通過(guò)函數(shù)指針去調(diào)用這個(gè)函數(shù) (*pfun)(p,q); //pfun是函數(shù)的地址,那么 *pfun當(dāng)然就是函數(shù)本身了。
2.函數(shù)參數(shù)傳遞問(wèn)題
在C語(yǔ)言中,有兩種參數(shù)傳遞的方式 ,一種是值傳遞,另一種是指針傳遞。
值傳遞很好理解,即把實(shí)參的值傳遞給形參。
而指針傳遞傳的是地址在C語(yǔ)言中,形參值的改變并不能改變實(shí)參的值,但形參所指向內(nèi)容值的改變卻能改變實(shí)參,這一點(diǎn)非常的重要,是指針傳遞的精華所在。
3. 指針函數(shù)
當(dāng)函數(shù)的返回值為指針類型時(shí),應(yīng)該盡量不要返回局部變量的指針,因?yàn)?,局部變量是定義在函數(shù)內(nèi)部,當(dāng)這個(gè)函數(shù)調(diào)用結(jié)束了,局部變量的棧內(nèi)存也被釋放了,因此,不能夠正確的得到返回值。實(shí)際上,內(nèi)存已經(jīng)被釋放了,但這個(gè)指針的地址已經(jīng)返回過(guò)去了,但是這個(gè)地址已經(jīng)是無(wú)效的了,此時(shí),對(duì)這個(gè)指針的使用是很危險(xiǎn)的。
4. 野指針
野指針并不是NULL,而是指向垃圾內(nèi)存的指針。
有兩種情況可以導(dǎo)致野指針:
(1) char* p;
(2)malloc,free
第一種情況是定義指針,但沒(méi)有給指針賦地址,此時(shí),對(duì)指針的使用是很危險(xiǎn)的,因?yàn)槟悴恢浪赶蚰睦铮莻€(gè)野指針。
第二種情況,malloc是在堆上分配內(nèi)存,必須由用戶手動(dòng)釋放,當(dāng)釋放之后,指針指向的內(nèi)存已經(jīng)釋放掉了,但指針本身的地址還存在,即指向了一個(gè)無(wú)效的內(nèi)存,所以這時(shí)的指針為野指針,必須把這個(gè)指針p=NULL.
5. 下面舉個(gè)例子說(shuō)明上述幾種情況
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
void fun1(int*,int*);
void fun2(int*,int*);
char* fun(char*,char*);//指針函數(shù),即返回值為指針的函數(shù)
int main(){
//定義3個(gè)函數(shù)指針
void (*pfun1)(int*,int*);
void(*pfun2)(int*,int*);
char*(*pfun)(char*,char*);//定義返回值為指針的函數(shù)指針
int *p;
int *q;
int a=10;
int b=20;
p=&a;//整形指針變量的初始化
q=&b;
printf("%d\n",*p);
printf("%d\n",*q);
pfun1=fun1;//把函數(shù)fun的地址賦值給函數(shù)指針pfun
pfun2=fun2;
(*pfun1)(p,q);//用函數(shù)指針去調(diào)用函數(shù),pfun是fun的地址,那么*pfun當(dāng)然就是函數(shù)本身了
printf("%d\n",*p);
printf("%d\n",*q);
//當(dāng)交換兩個(gè)指針時(shí),發(fā)現(xiàn)值并沒(méi)有發(fā)生改變
//在C語(yǔ)言中,形參的改變并不能改變實(shí)參,實(shí)參中p指向a的地址,q指向b的地址,在形參中,把兩個(gè)地址互換,即形參的p指向b的地址,q指向a的地址,但并不能改變實(shí)參的值,除非改變地址中的內(nèi)容值
(*pfun2)(p,q);
printf("%d\n",*p);
printf("%d\n",*q);
//此時(shí)p與q指向的值發(fā)生了變化,由于把地址中的內(nèi)容交換了,所以交換了p,q
pfun=fun;
char* x="ab";
char* y="bc";
char* s=(*pfun)(x,y);
printf("%s\n",s);
//free(s);//s指向這個(gè)堆內(nèi)存,所以malloc之后要釋放
s=NULL;
if(s!=NULL){//釋放掉這個(gè)內(nèi)存后,指針仍然不為空,此時(shí)的指針?lè)Q為野指針,所以要把s=NULL
printf("%s\n",s);
}
//對(duì)于野指針,有兩種情況
//第一種情況: char* p;只聲明了字符型指針,但沒(méi)明確指向的地址,此時(shí),用這個(gè)指針是很危險(xiǎn)的,因?yàn)檫@個(gè)指針是野指針
//第二種情況: malloc()之后,沒(méi)有free()之后,沒(méi)有把指針設(shè)置為空,因?yàn)榇藭r(shí)指針仍然存有地址,但是這個(gè)地址已經(jīng)是無(wú)效的,所以對(duì)這個(gè)指針的使用是很危險(xiǎn)的
return 0;
}
void fun1(int*p,int*q){
int* temp=p;
p=q;
q=temp;
}
void fun2(int*p,int*q){
int temp;
temp=*p;
*p=*q;
*q=temp;
}
char* fun(char*p,char* q){
//char a[]="abc";//定義一個(gè)內(nèi)存空間,局部變量棧上內(nèi)存
//char* s=a;
//return s;//在這函數(shù)結(jié)束之后,char型指針被釋放掉,因此不能正確返回
//因此,最好別返回一個(gè)局部變量指針
//(1)解決方法:把數(shù)組變成靜態(tài),即 static char a[]="abc";靜態(tài)內(nèi)存在函數(shù)結(jié)束后不會(huì)被釋放
//(2)申請(qǐng)堆內(nèi)存
//(3)定義為常量區(qū)
//char *s=(char*)malloc(sizeof(char*)*10);
// strcpy(s,"abc");
char* s1="ssss"; //定義一個(gè)指針變量指向字符串,在C中,字符串被存放在常量區(qū),靜態(tài)存儲(chǔ)區(qū)域,因此,在這個(gè)函數(shù)結(jié)束之后,這個(gè)地址仍然是有效的,即常量區(qū)的內(nèi)存沒(méi)有被釋放掉,因此能夠返回值
return s1;
}
fun1函數(shù)中交換地址,并不能交換兩個(gè)指針指向的值,因?yàn)樾螀⒌母淖儾荒芤饘?shí)參的改變。
2. fun2函數(shù)
fun2函數(shù)交換的是地址里面的內(nèi)容,所以能交換兩個(gè)指針指向的值。
3. fun函數(shù)
(1)fun函數(shù)里面定義的a是個(gè)局部變量,在函數(shù)返回之后這塊內(nèi)存會(huì)被釋放掉。因此,為了得到返回值,可以聲明為 static char a[]=""abc";
(2)malloc申請(qǐng)的是堆內(nèi)存,因此,可以得到返回值,但必須free掉這塊內(nèi)存,同時(shí)將p=NULL,避免野指針。
(3)可以定義char* s=字符串,在C語(yǔ)言中,字符串是存放是靜態(tài)常量區(qū),因此,函數(shù)結(jié)束后,那塊內(nèi)存不會(huì)被釋放掉。可以得到返回值。
因此,不能返回局部指針變量。
C語(yǔ)言中,形參只有在傳遞時(shí)才分配內(nèi)存單元,實(shí)參到形參的傳遞是單向傳遞,因此,形參的改變并不能引起實(shí)參的改變,另外,實(shí)參與形參占據(jù)著不同的內(nèi)存單元。
聯(lián)系客服