深入理解C语言函数传参方式

一 背景

如果问C语言函数传参方式,大多数人答案是值传递和地址传递。值传递不改变原参的值,而地址传递(指针)传递会改变原参的值。

但实际上所有的传参方式有且仅有一种,就是值传递。值传递和地址传递都是通过拷贝实参的值,值传递比较好理解,直接拷贝实参,而地址传递拷贝的值是指针变量所指向变量的地址。

说到指针变量,一瞬间要想到3个概念。1 指针变量所指向变量的值;   2 指针变量所指向变量的地址; 3 指针变量本身自己所占的内存地址。

int a = 2;
int *p;
p = &a;

(对应上述代码: *p 对应第1个概念, p 对应第2个概念, &p 对应第3个概念; )

二 值传递

void swap(int a , int b){
int temp = a;
       a = b;
       b = temp;
}

int main(void){

int a = 4, b =10;
printf("a=%d,b=%d",a,b);
swap(a,b);
printf("a=%d,b=%d",a,b);
return 0;
}

这种方式形参拷贝了实参,并不能达到交换的目的。 

三 地址传递

第一种方式:

void swap(int *a , int *b){
int *temp = a;
       a = b;
       b = temp;
}

int main(void){

int a = 4, b =10;
printf("a=%d,b=%d",a,b);
swap(&a,&b);
printf("a=%d,b=%d",a,b);
return 0;
}

上述这种方法虽然传入的是指针变量,但是由于函数传递过程中是值传递,传入的也是a和b的地址,只对地址进行了交换。所有也不能对实参的数进行交换。

另外一种方式交换:

void swap(int *a , int *b){
int temp = *a;
       *a = *b;
       *b = temp;
}

int main(void){

int a = 4, b =10;
printf("a=%d,b=%d",a,b);
swap(&a,&b);
printf("a=%d,b=%d",a,b);
return 0;
}

这种方式能交换成功,思考为什么呢?关键在于传递地址后,进行了取内容(*)操作,此时指针变量指向了原来的值,即从较低级的运算 到较高级的运算操作,就达到了交换实参的目的(笔者这样说这样方便理解和记忆)。现在回想值传递和第一种地址传递,就是因为它们缺少了这一跳转操作,无法完成改变实参。

 

四 进一步思考

在学习初始化链表操作时,往往有2种方法比较常用

直接返回:

#include<stdio.h> 
#include<stdlib.h>


struct node{
int data;
struct node *next;
}; 
typedef struct node* linkList;


//直接返回
linkList create(void){
        linkList p;
        p = (linkList )malloc(sizeof(node));
        p->data = 10;
        p->next = NULL;
return p;
}


int  main(void){
linkList head = create();
printf("head->data=%d",head->data);

return 0;
}

//输出: head->data= 10 

通过传入地址:

#include<stdio.h> 
#include<stdlib.h>


struct node{
int data;
struct node *next;
}; 
typedef struct node* linkList;




//传入链表方式1
create(linkList *p){
      ( *p) = (linkList )malloc(sizeof(node));
       ( *p)->data = 10;
        ( *p)->next = NULL;

}

// 下面这个方法是无法初始化的
//传入链表 方式2
void create2(linkList p){
      p = (linkList )malloc(sizeof(node));
       p->data = 10;
        p->next = NULL;

}






int  main(void){
linkList  head = NULL;//注意 head是指针,传入是head是地址,为什么要传入&head,地址的地址
create(&head);  //方式1有用
//create2(head); //方式2无用
printf("head->data=%d",head->data);

return 0;
}

//输出: head->data= 10 

思考为什么2个方式都是传入地址,但方式1有用,但是方式2无用呢。

表象就是笔者前面所述,没有从较低级的运算 到较高级的运算操作,只是平级操作,达不到影响实参的目的。

其实质就是:函数传参都是值传递,方式2也只是改变形参(拷贝)的内容,没有影响到实参内容。而加了(*p)操作后,此时相当于不是平级操作,跳到实参,达到了改变实参目的。

 

五 总结

以后看到值传递或者指针传递,要想到本质是值传递,都是拷贝实参内容,普通变量是拷贝的是变量值,而指针变量是所指向变量的地址。进行平级操作不能改变实参的值,而要想真正改变实参的值,需要(*)取内容跳级操作,才能影响到。对于想影响普通变量,需要一级指针;对于想影响一级指针(即前面笔者表述初始化链表的第2种方式),需要使用二级指针(地址的地址)。在交换时,都使用了跳转操作。

  • 10
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值