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);  //输出20

    change_point(pcat);
    printf("%d",cat.num);  //输出13

    cat.num = 28;
    change_point(&cat);    //输出13
    printf("%d",cat.num);
}

通过上面的例子可以看到,传递结构体指针(或者取它的地址),可以通过结构体指针的引用修改结构体成员的值。直接传递结构体,不能修改结构体成员的值。

其次,如果传递的是结构体的话,因为C语言的参数传递值调用方式是要求把参数的一份拷贝传递给参数,上面的name这个数组的长度是30,那么这个结构体将占用34个字节的空间,要想把它作为参数传递的,我们必须把34个字节都复制到堆栈中,以后再丢弃,所以传递指针比直接传递结构体变量的效率要高非常多。

一般,将结构体传递给函数的方式有如下3种:

  1.用结构体的单个成员作为函数参数,向函数传递结构体的单个成员(属于传值调用,不会影响相应的实参结构体的值)

  2.用结构体变量做函数参数,向函数传递结构体完整结构(属于传值调用,不会影响相应的实参结构体的值)

  3.用结构体指针或结构体数组作函数参数属于按引用调用,会影响相应的实参结构体的值,向函数传递结构体地址,因为仅复制结构体首地址一个值给被调函数,相对于第二种方式,这种传递效率更高

 

总结:

1. 指针变量只能存表示地址类型的数据

2. 指针得到谁的地址,就指向谁

3. 几个指针变量指向同一个地址,通过指针引用改变指向的变量值,其他指针指向的变量值同时也会被改变。地址一样,指向的变量值也一样。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐