【C语言】传值调用和传址调用
慕雪年华

今天学习C语言的时候,接触到了两个全新的概念
话不多说,就此和大家分享一下c语言中函数的传调用和传调用,希望有所帮助
欢迎dalao无情指正

引子 : 交换函数

我们先来写一个简单的函数

目的是交换a和b

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int tmp = 0;
printf("a=%d b=%d\n", a, b);//显示a和b最初的值
tmp = a;
a = b;
b = tmp;
printf("a=%d b=%d\n", a, b);//查看交换结果
return 0;
}

运行的结果如图

image

需要注意的是,这是在主函数main里面直接进行的交换程序,而我们想要的其实是一个可以在任何地方使用的交换函数

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
printf("a=%d b=%d\n", a, b);
swap1(a, b);
printf("a=%d b=%d\n", a, b);
return 0;
}

若想达成以上目的,我们要先把上述代码优化成以下形式。其中swap1就是我们即将要写的自定义函数

1
2
3
4
5
6
7
void swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
  • void表示自定义函数无返回类型

这个自定义函数看起来没什么问题对吧!

但是结果却告诉我们a和b并没有被交换

image

这是为什么?

查错方法

这里给大家介绍一个老师教授的查错方法。虽然我估计大家都知道(小声bb)

那就是用vs编译器自带的监视器了(快捷键f10,我用的是最新的vs2019,官网即可免费下载使用)

image

  • 左边有个很Q的黄色小箭头,告诉你正在监视那一步
  • 每按一次f10代码就会往下走一行
  • 当遇到swap此类函数时,按f11进入函数,f10会直接跳过函数

image

可以看到代码在第25行的时候,x和y已经获得了a和b的值,tmp此时为0

image

下一行继续,tmp变成10

image

再下一行,x获得y的值,x=20

image

然后y获得tmp里面保存的x的值,y=10

image

可当我们下一步跳出函数,回到主函数的时候

会发现a和b仍然是10和20,并没有发生交换

image

聪明的你肯定已经想到这是为什么了

函数中的x和y与主函数的a和b是独立的,x和y的变化无法影响a和b

那怎么样函数中的x和y才能影响函数外的a和b,从而达到我们交换的目的呢?

这里需要涉及一个新的大家都知道的概念

& 取地址操作符

先用一个简单的例子让大家了解&取地址操作符的作用

在我们的c语言中,每个字符都对应了一个它独自的地址

而&符号就是取出这个字符的地址

1
2
3
4
5
6
7
8
int main()
{
int a = 10;
int* pa = &a;//pa指针变量
*pa = 20;//解引用操作
printf("%d\n", *pa);
return 0;
}

这里面的*pa获取了a的地址,在打印的时候会顺着地址找到a,显示a的结果

让我们继续

我们将上述交换函数代码的主函数改成如下形式

1
2
3
4
5
6
7
8
9
10
int main()
{
int a = 10;
int b = 20;
printf("a=%d b=%d\n", a, b);
//swap1(a, b);
swap2(&a, &b);
printf("a=%d b=%d\n", a, b);
return 0;
}

比较swap1和swap2,会发现swap2括号中的a和b前面都多了一个**&操作符**

这个操作符会让swap2不再读取a和b的数值,即10和20

而是会读取a和b在内存中的地址

相对应的,swap2函数也要改成如下形式

其实形式和swap1基本一样,只是x和y被替换成了**pa 与 pb

1
2
3
4
5
6
7
void swap2(int* pa, int* pb)
{
int tmp = 0;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
  • 表明 pa和pb都是*指针变量
  • pa和pb保存的是a和b的地址

这时候我们再运行代码,a和b被很好的调换了

我们的目的也达成了

image

实参和形参

要想了解为什么两个函数会有这样的不同

我们需要学习实参(实际参数)和形参(形式参数)

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}

void swap2(int* pa, int* pb)
{
int tmp = 0;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}

int main()
{
int a = 10;
int b = 20;
printf("a=%d b=%d\n", a, b);
//swap1(a, b);
swap2(&a, &b);
printf("a=%d b=%d\n", a, b);
return 0;
}

在这串代码中,a和b是实参

x、y、*pa、 *pb都是形参

可以借用另外一个代码的例子进一步给大家了解实参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int get_max(int x, int y)
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 10;
int b = 20;
int max = get_max(a, b);
printf("max= %d\n", max);
max = get_max(100,300);
max = get_max(100,300+1);//实参可以是表达式
max = get_max(100, get_max(3, 5));//也可以是函数
printf("max = %d\n", max);
return 0;
}

在这串代码中,抛去上面的a和b

max = get_max(100,300);是我们使用这个函数的基本方式

  • 这里面的100和300是实参
  • 同时**300+1和get_max(3, 5)**也是实参
  • 对应的函数中的x和y就是形参

当实参传给形参的时候
形参其实是实参的一份临时拷贝
对形参的改变是不会改变实参的

这样解释大家应该对实参和形参有一定的认识了

接下来就有请我们的标题人物隆重登场

传值调用,传址调用

这两个调用方式十分重要,而且两个字从读音到长相(bushi)都十分相似

所以大家一定要注意区分!

image

简而言之

当我们需要在函数内部操作函数外部的变量的时候→传址调用

如果我们只需要外部变量的值,不需要改变外部变量→传值调用

以上面的交换函数为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void swap1(int x, int y)//传值调用
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}

void swap2(int* pa, int* pb)//传址调用
{
int tmp = 0;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}

因为swap1里面的x和y是传值调用,x和y只获取了a和b的值,自然无法影响函数外面的a和b

而swap2中的*pa 和 *pb获取的是a和b的地址(址→地址)所以可以影响函数外的a和b,实现它们的交换

注:x,y,*pa , *pb 都是局部变量,它们在出了函数外之后就无法使用,也失去了使用它们的意义

结束语

本篇小博客到这里也结束啦,感谢你看到最后!

如果感觉看完之后仍有不明白的地方

可以去观看这期视频里面的讲解,swap函数的讲解在1小时17分【点我跳转】

我也是刚学C语言的小白,博文有错在所难免,请各位大佬无情指正

这篇博文虽然字不多,但依旧花了我2小时的时间,如果觉得不错就点个赞再走吧!

求求了,跪谢!