指针取*解引用:表示的是指针所指向内存单元中内容;或者理解为该内存空间。
指针传递其实也是一种值传递,只不过传递的是地址值,如果通过该地址取*解引用,是可以改变形参指针的原值的,但如果只是指针的值发生变化的话,并不会影响实参指针及其原值。
故:可以这么说:
函数参数均为值传递
函数参数为指针时的情况分析:
1)实参已经初始化(重要)数组名作为参数传递,实际也是传递的指针
例如://传递指针改变原值的情况
char ch[1024];
int* func(char *p)
{
memcpy(p,"hello");//是对*ch的值进行改变,地址没有变化,原值变了。
}
//函数调用:
func(ch);
分析:实参已经被初始化了,即:指针ch已经指向了内存空间。
2)实参未初始化或者值为NULL。
//无法改变形参值,仅仅是地址形式的值传递
char *ch = NULL;
int* func(char *p)
{
p = (char*)malloc(sizeof(char)*5);
p[0]='h';
}
//函数调用:
func(ch);
//仅仅是将NULL的值传递给形参,且函数内并没有解引用,故既不能改变实参指针也没有指针原型可以改变。当然也无法初始化实参。
解释:将NULL值复制给了指针p,但是函数内p又被重新赋值了,这只是单纯的值传递,无法改变实参的值。因为,函数内,也没有对指针的原型做改变,而仅仅是对指针进行了赋值。
改正方法一:采用传引用的方式,而不是值传递的方式
char *ch;
int* func(char* &p)
{
p = (char*)malloc(sizeof(char)*5);
p[0]='h';
}
//函数调用:
func(ch);
改正方法二:采用指针传递,而不是值传递,因为实参本身是指针,所以函数参数可以为二重指针
char *ch;
int* func(char** p)
{
*p = (char*)malloc(sizeof(char)*5);
(*p)[0]='h';
}
//函数调用:
func(&ch);
改正方法三:将指针通过返回值返回。
char *ch;
void* func(char* p)
{
p = (char*)malloc(sizeof(char)*5);
p[0]='h';
return p;
}
//函数调用:
ch = func(ch);
//do sth.
free(ch);
实参为NULL的情况
//实参虽然初始化为NULL了,仍然不能直接使用//因为指针仍然没有指向内存
char *ch=NULL;
int* func(char*p)
{
if(!ch)
printf("error");
}
//函数调用:
func(ch);
总结:
1)如果函数形参要通过指针传递,且该指针没有初始化,则要么采用(二重)指针的形式,要么采用引用的形式进行参数初始化;
如果实参指针已经初始化了,则除通过上述方法外,还可以直接通过值传递即可。
其实,函数传的参数都是一样的,包括指针和普通变量,只要实参是未初始化的,肯定是通过指针和引用传参(未初始化,当然不能通过值传递了。。。);如果实参已经初始化,则除可以通过指针、引用传参外,还可以通过原值传递。
所以:
- 可以通过实参是否已经初始化,可以推断是什么类型的传参方式。
- 如果形参为指针,查看函数内是否对该指针进行解引用了,如果解引用了,可以改变原值,如果没有则对原值及实参指针没有影响。
只有指针和引用才能改变实参原值,如果原值是普通变量,则参数一重指针,如果原值为一重指针,则参数为二重指针。
2)函数参数均为值传递。
另,参考示例可以参看本文。
实际中经常用的是结构体指针作为形参的形式
typedef struct{
char *mm;
int nn;
float gg;
}STU;
void func_1(float* p)
{
*p = 3.14;
}
void func(STU* p)
{
p->nn = 2;
p->mm = (char*)malloc(sizeof(char));
func_1(&p->gg);
}
STU ch;
//函数调用:
func(&ch);