C语言传参分为值传传递和地址传递。C语言指针传参时,可以通过指针引用方式改变指针指向的值。改变变量,可以使用指针应用方式,改变地址,使用指针的指针引用方式。
C语言值传递:
形参是实参的拷贝,改变形参的值并不会影响外部实参的值。从被调用函数的角度来说,值传递是单向的(实参->形参)
#include <stdio.h>
void swap(int x, int y);
main()
{
int a = 5, b = 9;
swap(a, b);
printf("a=%d\nb=%d\n", a, b);
return 0;
}
void swap(int x, int y)
{
int t;
t = x;
x = y;
y = t;
}
上述代码运行后a、b的值并未改变
C语言地址传递:
形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作
#include <stdio.h>
void swap(int *p1, int *p2);
main()
{
int a = 5, b = 9;
int* pointer_1, *pointer_2;
pointer_1 = &a;
pointer_2 = &b;
swap(pointer_1, pointer_2);
printf("a=%d\nb=%d\n", a, b);
return 0;
}
void swap(int *p1, int *p2)
{
int t;
t = *p1;
*p1 = *p2;
*p2 = t;
}
程序说明:
程序运行时候,先输入a和b的值,然后将a和b的地址赋给指针变量pointer_1和pointer_2,使pointer_1指向a, pointer_2指向b,如图
在swap函数调用时候,将实参的值传给形参,采用的依然是值传递,虚实结合后形参p1的值为&a,p2的值为&b,这时候p1和pointer_1都指向变量a, p2和pointer_2都指向变量b。
接着执行swap函数的函数体,使*p1和*p2的值互换,也就是使a和b的值互换,互换后的情况如下
函数调用结束后,形参p1和p2不复存在(已释放),情况如下
上述代码执行后a和b值交换,a=9、b=5
易错点补充:
#include <stdio.h>
void swap(int *x, int *y);
main()
{
int a = 5, b = 9;
int *pp = &a;
int *kk = &b;
swap(pp, kk);
printf("a=%d\nb=%d\n", *pp, *kk);
return 0;
}
void swap(int *x, int *y)
{
int *t;
t = x;
x = y;
y = t;
}
请读者想一下,上述代码执行后a和b的值是否交换,为什么?
上述代码看起来像交换了指针pp和kk的指向,实际上并没有
代码的运行结果a=5、b=9,运行结果是a和b的值并没有改变,因为这时使用的实参pp,kk是值传递,传递的是指针的值,以指针pp来说,指针的值是变量a的地址,
指针的值传入后用形参int *x和int *y接收,这里x和pp虽然都指向变量a的地址,但指针变量x和pp自己的地址(地址的地址值)是不相同(意思是x拷贝了一份pp的值),意味着你能改变变量a的值,但是不能改变pp的值(这里与值传递相似)
为了更加直观,清晰的看出值的交换,这里添加一些代码来显示内存和变量的值
#include <stdio.h>
void swap(int *x, int *y);
main()
{
int a = 10, b = 20;
int *pp = &a;
int *kk = &b;
printf("a的地址%p----b的地址%p\n\n", &a, &b);
printf("pp的值%p----kk的值%p\n", pp, kk);
printf("pp的地址%p----kk的地址%p\n\n", &pp, &kk);
swap(pp, kk);
printf("a = %d\nb = %d", *pp, *kk);
return 0;
}
void swap(int *x, int *y)
{
int *t;
printf("x的值%p----y的值%p\n", x, y);
printf("x的地址%p----y的地址%p\n", &x, &y);
t = x;
x = y;
y = t;
}
结构体指针地址传参
上面的例子只是传递一个int指针类型做参数,看起来比较简单,在实际应用中,结构体指针做函数参数的比较常见,通过结构体指针的引用,可修改结构体成员的数据内容
#include <stdio.h>
#include <string.h>struct animal
{
char name[30];
int num;
};//使用结构体作为参数 浪费内存 需要建立结构体
void change_struct(struct animal cat)
{
cat.num = 17;
}//函数内部改变需要地址 所以需要指针保存
void change_point(struct animal *cat)
{
cat->num = 13;
}void main
{
struct animal cat = {0};
struct animal *pcat = &cat;
cat.num = 20;change_struct(cat);
printf("%d",cat.num); //输出20change_point(pcat);
printf("%d",cat.num); //输出13cat.num = 28;
change_point(&cat); //输出13
printf("%d",cat.num);
}
通过上面的例子可以看到,传递结构体指针(或者取它的地址),可以通过结构体指针的引用修改结构体成员的值。直接传递结构体,不能修改结构体成员的值。
其次,如果传递的是结构体的话,因为C语言的参数传递值调用方式是要求把参数的一份拷贝传递给参数,上面的name这个数组的长度是30,那么这个结构体将占用34个字节的空间,要想把它作为参数传递的,我们必须把34个字节都复制到堆栈中,以后再丢弃,所以传递指针比直接传递结构体变量的效率要高非常多。
一般,将结构体传递给函数的方式有如下3种:
1.用结构体的单个成员作为函数参数,向函数传递结构体的单个成员(属于传值调用,不会影响相应的实参结构体的值)
2.用结构体变量做函数参数,向函数传递结构体完整结构(属于传值调用,不会影响相应的实参结构体的值)
3.用结构体指针或结构体数组作函数参数属于按引用调用,会影响相应的实参结构体的值,向函数传递结构体地址,因为仅复制结构体首地址一个值给被调函数,相对于第二种方式,这种传递效率更高
总结:
1. 指针变量只能存表示地址类型的数据
2. 指针得到谁的地址,就指向谁
3. 几个指针变量指向同一个地址,通过指针引用改变指向的变量值,其他指针指向的变量值同时也会被改变。地址一样,指向的变量值也一样。