C语言函数参数是如何传递的?

C语言函数参数是如何传递的?

原创守望的编程珠玑2019-06-22 08:21:22

前言

我们可能听过C语言中的传值和传指针,在其他语言中,也有传引用一说,那么他们到底有什么区别呢?如果你还不能准确地分辨,就该好好了解一下了。

传值

我们在初学C语言的时候就被老师教过,下面的方式是无法交换a和b的值的:

#include<stdio.h>void swap(int a,int b){ int temp = a; a = b; b = temp; printf('swap a = %d,b = %d\n',a,b);}int main(void){ int a = 10; int b = 20; printf('before swap:a = %d,b = %d\n',a,b); swap(a,b); printf('after swap:a = %d,b = %d\n',a,b); return 0;}

运行结果如下:

before swap:a = 10,b = 20 internal swap a = 20,b = 10 after swap:a = 10,b = 20

可以看到,a和b的值最终并没有被交换。开始时a,b的值为10,20,而最终还是同样的值。

为什么呢?因为函数参数在传递的时候,都是传原数据的副本,也就是说,swap内部使用的a和b只是最初始a和b的一个副本而已,所以无论在swap函数内部对a和b做任何改变,都不会影响初始的a和b的值。

正因如此,我们常常被告知,不要把直接把结构体直接作为参数,这样效率会很低。由于结构体本身占用字节数较大,如果直接作为参数,那么将会产生一个较大的”副本“,如此一来,效率也就很低了。

我们再结合下面的图来理解:

值传递

首先图中方框中的上部分a和b代表了main函数中的a和b,即原始数据,而方框中的下部分a和b代表了函数的参数a和b,即原始数据的“副本”。(后面的图都是如此,上部分代表原始值,下部分代表函数参数值)。

调用swap函数前后的情形如下:

调用swap前后

由于在swap中永远只是对a和b的副本进行操作,因此完全不影响原始的a和b的值。最终也不可能达到交换a和b的值的目的。

传指针

那么为解决上面的问题,我们知道,需要传指针。其代码如下:

#include<stdio.h>void swap(int *a,int *b){ int temp = *a; *a = *b; *b = temp; printf('swap a = %d,b = %d\n',*a,*b);}int main(void){ int a = 10; int b = 20; printf('before swap:a = %d,b = %d\n',a,b); swap(&a,&b); printf('after swap:a = %d,b = %d\n',a,b); return 0;}

运行结果:

before swap:a = 10,b = 20 swap a = 20,b = 10 after swap:a = 20,b = 10

可以看到在这种情况下,a,b的值才是真正交换了。

为什么又有传值,又有传指针

看到这里,不知道你是否会疑惑,为什么给函数传递参数的时候,一会是传值,一会是传指针呢?为什么传指针就能改变参数的值呢?实际上,C语言里,参数传递都是值传递!也就是说,你认为的传指针也是传值,只不过它的值是指针类型罢了。

我们再通过图来理解前面为什么传指针就可以交换a,b的值:

传指针

从图中可以看出,虽然传递给函数的是指向a和b的指针的副本,但是它的副本同样也是指向a和b,因此虽然不能改变指针的指向,但是能改变参数a和b指向的内容,即改变原始a和b的值

再看传指针

如果是为指针p申请一段内存,下面的代码能达到目的吗?

#include<stdio.h>#include<stdlib.h>void getMemery(int *p){ /*申请1024个int大小*/ p = malloc(sizeof(int)*1024); if(NULL == p) { printf('malloc failed\n'); p = NULL; }}int main(void){ int *p = NULL; getMemery(p); printf('address of p is %p\n',p); return 0;}

通过前面的内容分析,肯定是达不到预期效果的。

运行结果:

address of p is (nil)

这是为什么呢?我们还是利用前面所知来分析,由于传递给getMemory函数的参数都是一个副本,因此函数内的p也是外部p的一个副本,因此即便在函数内部,将p指向了一块新申请的内存,仍然不会改变外面p的值,即p还是指向NULL。

如何修改呢?我们需要传入p的地址,即指向int类型指针的指针。

#include<stdio.h>#include<stdlib.h>void getMemery(int **p){ /*申请1024个int大小*/ *p = malloc(sizeof(int)*1024); if(NULL == *p) { printf('malloc failed\n'); *p = NULL; }}int main(void){ int *p = NULL; getMemery(&p); printf('address of p is %p\n',p); free(p); p = NULL; return 0;}

运行结果如下:

address of p is 0x144f010

从运行结果可以看到,p的值被改变了。

可配合下面的图进行理解:

总结

本文总结如下:

  • 函数的形参都是原数据的“副本”,因此在函数内无法改变原数据
  • 函数中参数都是传值,传指针本质上也是传值
  • 如果想要改变入参内容,则需要传该入参的地址(指针和引用都是类似的作用),通过解引用修改其指向的内容
  • 以上结论不限于C语言

本文原地址:https://www.yanbinghu.com/2019/06/20/53981.html

思考

  • 如何实现不借助第三个变量,交换两个整数的值?
  • 结合本文,理解C /Java中所谓的传引用

微信公众号【编程珠玑】:专注但不限于分享计算机编程基础,Linux,C语言,C ,数据结构与算法,工具,资源等编程相关[原创]技术文章,号内包含大量经典电子书和视频学习资源。欢迎一起交流学习,一起修炼计算机“内功”,知其然,更知其所以然。

了解更多

收藏
举报
37 条评论
  • 淡淡的愁思2019年6月22日

    我个人的看法,理解函数调用的栈帧调用过程,理解地址,把这个代码反汇编了,更容易理解。说白了,毕竟写的代码是怎么让cpu运行是编译器说了算,它会把代码转换成汇编,然后机器码,为什么C需要学不好,涉及指针就懵逼,就是没有理解它在汇编层面的本质。

    回复 ⋅ 1条回复5 
  • 没有堆栈和内存的分析,如同空中楼阁

    回复0 
  • 3533221023天前

    不想了解编译原理,也可了解下函数调用时堆栈起的作用

    回复0 
  • 说白了就是主函数调用完之后,形参被释放掉,分配的空间也被释放掉,主函数没有执行任何的操作。

    回复1 
  • 想研究深一点,还是得看看编译原理

    回复0 
(0)

相关推荐

  • C语言学习篇(15)-----函数传参详解

    https://m.toutiao.com/is/JpuAcLb/ 前面我们已经介绍过什么是指针,指针变量的用法等等,今天我们就来讲讲什么是函数,函数有啥作用,函数的参数有哪些需要注意的地方以及指针与 ...

  • C语言指针学习总结

    作者:driveby 来源:https://www.cnblogs.com/KKSJS/p/9622812.html 上学的时候学习C语言,最烦的就是里面指针,可是指针也恰恰是C语言的灵魂. 最近在重 ...

  • 【原创】C++指针详解

    指针 一,基础知识 1,内存(memory) 电脑是在CPU里面执行任务的,CPU就相当于人的大脑.CPU计算速度很快,所以我们写程序时,哪怕例如执行100多次循环,看起来似乎都是一秒就能执行好的.然 ...

  • C语言函数参数传递之值传递和地址传递

                              函数调用时需要注意函数参数传递是值传递(传值)还是地址传递(传地址). 传值:形参不影响实参的值 void swap(int m,int n){ i ...

  • C语言函数参数压栈的顺序是?

    今天分享关于C语言函数参数压栈顺序的问题.按照日常习惯,C语言的函数参数压栈顺序是从左到右吧?但是事实却是相反的,C语言函数参数压栈顺序是从右到左的.下面看一个程序: #include <std ...

  • R语言中创建函数参数的问题

    欢迎来到医科研,这里是白介素2的读书笔记,跟我一起聊临床与科研的故事, 生物医学数据挖掘,R语言,TCGA.GEO数据挖掘. Sys.setlocale('LC_ALL','C') ## [1] &q ...

  • C语言二维数组作为函数参数?(陷阱)

    大家有构建过二维数组作为函数的参数吗?有没有遇到什么问题呢?现在,我们先来看一个函数: void func1(int **array, int m, int n) {  int i = 0, j = ...

  • 【易错】C语言二维数组作为函数参数?

    大家有构建过二维数组作为函数的参数吗?有没有遇到什么问题呢?现在,我们先来看一个函数: void func1(int **array, int m, int n) {  int i = 0, j = ...

  • C语言 const 修饰函数参数 - C语言零基础入门教程

    目录 一.const 简介 1.const 修饰变量 2.const 修饰指针 3.const 修饰在函数名前面 4.const 修饰在函数名后面 二.const 修饰函数参数 1.值传递 2.址传递 ...

  • 2.2 if函数参数省略的用法

    2.2 if函数参数省略的用法

  • (6条消息) OpenCV中的findContours函数参数详解

    OpenCV中的findContours函数参数详解 小白的进阶 2017-07-30 18:02:28 55008 收藏 303分类专栏:OpenCV 文章标签:opencvOpenCV中通过使用f ...

  • scatter函数参数

    scatter函数是用来绘制散点图的,如上篇介绍. scatter函数有很多的参数. (此图片来源于网络) 其中,x,y为数据,s为点的大小,示例见上篇. 1.c为点的颜色,可以取'b','r','g ...