转自微信公众号:编程珠玑
作者:守望,Linux应用开发者

前言

我们可能听过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
",a,b);
}
int main(void)
{int a = 10;int b = 20;printf("before swap:a = %d,b = %d
",a,b);swap(a,b);printf("after  swap:a = %d,b = %d
",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中永远只是对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
",*a,*b);
}
int main(void)
{int a = 10;int b = 20;printf("before swap:a = %d,b = %d
",a,b);swap(&a,&b);printf("after  swap:a = %d,b = %d
",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
");p = NULL;}
}
int main(void)
{int *p = NULL;getMemery(p);printf("address of p is %p
",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
");*p = NULL;}
}
int main(void)
{int *p = NULL;getMemery(&p);printf("address of p is %p
",p);free(p);p = NULL;return 0;
}

运行结果如下:

address of p is 0x144f010

从运行结果可以看到,p的值被改变了,而不再是初始的NULL。

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

总结

本文总结如下:

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

C语言 传值和传指针的区别相关推荐

  1. php中什么时候用传值,php中传值与传引用的区别。什么时候传值什么时候传引用?...

    java中的this与super的区别 java中的this与super的区别 1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位 代码如下: class Base { Ba ...

  2. PHP的传值与引用,php 传值与传引用的区别详解

    摘要 腾兴网为您分享:php 传值与传引用的区别详解,掌上公交,信用管家,天翼阅读,平安知鸟等软件知识,以及k歌达人,ml2010打印机驱动,维也纳大学app,建玛特,网盘快搜,中国禁毒数字展览馆,爱 ...

  3. C++ 函数参数传递:传值,传指针,传引用

    PS:首先理解形参   实参概念.形参是在函数定义的括号内定义的专用变量,它们的目的是保存按实参传递给它们的信息,实参被列在函数调用语句的括号内. int func(int x)//x是形参 {ret ...

  4. 5个php实例,细致说明传值与传引用的区别

    传值:是把实参的值赋值给行参 ,那么对行参的修改,不会影响实参的值 传引用 :真正的以地址的方式传递参数传递以后,行参和实参都是同一个对象,只是他们名字不同而已对行参的修改将影响实参的值 说明: 传值 ...

  5. 【中级软考】函数参数传递传值与传引用的区别(global关键字,函数内定义全局变量)

    传值调用最显著的特征就是被调用的函数内部对形参的修改不影响实参的值. 引用调用是将实参的地址传递给形参,使得形参的地址就是实参的地址. (对于python而言,普通的实参传个变量(或常量)进去就相当于 ...

  6. 一道题目让你看出C语言的数组与指针的区别:

    文章目录 题目概述: 输入格式: 输出格式: 输入输出样例: C语言题解: 错误的运行结果: 错误问题的分析与定位: C库的malloc函数讲解: 函数原型: 函数形参: 函数的返回值: 内存的释放: ...

  7. 【C语言】引用和指针的区别?

    首先解释在C语言中,什么是引用:  1.引用概念 引用是别名,必须在声明的时候初始化.即:是指一个已定义变量的别名.(一个内存空间,有两个名字都可以操作) 引用:在函数调用时,是变量的别名,不可以单独 ...

  8. java的函数传值_java 函数形参传值和传引用的区别[转]

    java方法中传值和传引用的问题是个基本问题,但是也有很多人一时弄不清. (一)基本数据类型:传值,方法不会改变实参的值. publicclassTestFun { publicstaticvoidt ...

  9. C++:传值与传址的区别以及引用的使用

    使用指针的好处其一:指针复制成本低. 不使用指针的高成本: #include<iostream>void fun(int x) {}int main() {int x=42;fun(x); ...

最新文章

  1. udp,tcp软件udp客户端发消息,udp服务器收不到,C#网络编程,多级路由间,UDP发送消息客户端接收不正常?...
  2. java全双工_TCP双向还是全双工?
  3. python 之 分割参数getopt
  4. 【概念原理】四种SQL事务隔离级别和事务ACID特性
  5. xss绕过字符过滤_XSS过滤器绕过总结
  6. 初等数论--同余--MILLER-RABIN素性检测算法
  7. javascript tabIndex属性
  8. python中类的属性一般来说_python中实例属性和类属性之间的关系
  9. Wannafly挑战赛21: C. 大水题(DP)
  10. C++的三种交换数值的方式(值传递、地址传递、引用传递)
  11. 编译OpenJDK12:可以用VS2010到VS2017
  12. phonegap文件上传(java_php),Android应用开发之使用PhoneGap实现位置上报功能
  13. PowerPC PPC460-S MMU
  14. Nginx 集群搭建
  15. Jquery 提交表单
  16. 如何用div实现炫酷3D球体?
  17. discuz mysql cpu 100_Discuz导致MYSQL CPU 占用 100%?
  18. 如何在Windows下统计代码行数
  19. java code reviewer_Java Code Review
  20. Chainfire3D:让普通Android手机玩上高通/Tegra专属游戏

热门文章

  1. videoleap怎么导出本地_Pr导出视频时,如何调节视频文件大小?
  2. flutter英语怎么说_美国人天天说的英语:“你搞反了”英语怎么说?
  3. js读取html元素scr,了解一下JavaScript中的DOM编程
  4. html页面改成thymeleaf,【Thymeleaf】Thymeleaf模板对html实时刷新
  5. 华为热设计工程师待遇_沃得分享 | 华为校招青睐哪些江浙沪高校?
  6. Toast.makeText(MainActivity.this, R.string.alert, Toast.LENGTH_SHORT).show(); 解释
  7. 基于JAVA+Spring+MYSQL的电影票预定系统
  8. php任务奖励体系,phpwind7.5完备的积分体系
  9. c语言字符型运算符,C语言试题:数据类型、运算符与表达式
  10. jqGrid常用方法