C语言参数传递方式有传值和传地址两种方式。
1、传值方式
原理:形参和实参占不同内存单元,传递的实际上是实参变量或表达式的一个拷贝副本,将这个副本值传给形参,形参内存单元内容保存的正是这个副本值,相当于给形参进行初始化,形参的值发生变化也不会传回给实参,因此是单向传递。
例如:
void increase(int x)
{
x++;
}
当在主函数中调用上面这个函数时,会在ncrease函数内存栈中为形参x分配一个内存单元,然后把实参的值传到这个内存单元中,相当于给形参初初化了,然后形参x自增1,它改变的仅仅是形参内存单元中的内容,而实参内存单元中的内容并没有改变。当被调用函数执行完毕后,形参所分配的内存单元也被收回。
2、传地址方式
原理:和传值方式一样,当调用函数时也要为形参分配内存,被调函数执行完毕后也要收回内存。不同的是传递的是实参变量地址的拷贝值,而不是实参变量的值,在被调函数中对地址所指对象的操作会改变实参的值。但是形参的内容即存放的实参变量地址并不会改变。
例如:
void increase(int * x)
{
(*x)++;
}
int main()
{
int i,*x;
i=10;
*x=20;
increase(&i);//如果定义的不是指针变量,那变要加上个取址号&,当然如果形参实参是数组的话,直接用数组名即可,因为数组名本身代表的就是数组的首地址
increase(x);
}
主函数调用被调函数后,主函数中的i和*x的值都会改变。
我曾经在做关于链表的实验时,想先写一个初始化结点的函数,用malloc给结点分配内存,然后给主函数调用。
struct node
{
int data;
struct node *next;
}
当初始化函数按下面这样写就会有问题:
int init(struct node *n)
{
n=(struct node *)malloc(sizeof(struct node));
if(!n)
{
printf("Failed to create node\n");
return -1;
}
return 0;
}
在主函数中定义struct node *n;然后执行init(n);编译能通过,但运行会报错。
因为虽然用了指针传递方式,但是被调函数只能改变n所指对象即结构体中成员的值,但其本身的值即地址并不会改变。
有两种解决办法,一是形参用双指针变量,一是被调用函数返回值类型设为结构体指针类型。
如:
int init(struct node **n)
{
(*n)=(struct node *)malloc(sizeof(struct node));
if(!(*n))
{
printf("Failed to create node\n");
return -1;
}
return 0;
}
n是一个双指针变量,它存放结构体指针变量的地址。
如:
或是被调用函数返回值类型设为结构体指针类型
struct node * init()
{
struct node *n=NULL;
n=(struct node *)malloc(sizeof(struct node));
return n;
}