C/C++函数传参详解

news/2024/7/8 7:04:34

问题引出

在编码过程中,新手经常会遇到的一个问题是,我明明在函数中交换了数据,为什么函数外面显示的不对,最常见的例子就是swap()函数。先看两个代码:

代码一:

#include <stdio.h>
void swap(int a, int b)
{
	int t = a;
	a = b;
	b = t;
}
int main()
{
	int a = 3, b = 2;
	swap(a, b);
	printf("%d %d", a, b);
	return 0;
}

代码二:

#include <stdio.h>

void swap(int *a, int *b)
{
	int x = 100;
	int y = 200;
	a = &x;
	b = &y;
}
int main()
{
	int a = 3, b = 2;
	swap(&a, &b);
	printf("%d %d", a, b);
	return 0;
}

这两个代码输出的都是原来的3和2,为什么?特别是第二段代码,明明传进去的是指针,为什么还是不起作用?

代码分析

在函数传参的时候,实际上会发生一次浅拷贝,浅拷贝暂且不表,这里只需要知道,在函数传参的时候,会发生拷贝就可以了。

先看第一段代码,其实际的执行过程为:

(1)声明两个临时变量,int a,int b; (这两个变量跟main中的a和b重名)

(2)让a = a, b = b,=前面的a和b是swap函数声明的临时变量,=后面的是main中的实际变量的值;

(3)int t = a;  //注意这里的a是swap中声明的临时变量,它只是跟main中的变量a有相同的名字,但是他们在内存上的位置是不一样的

(4)a = b; //这里的a和b都是swap声明的临时变量,跟main中的a和b没有任何关系

(5)b=t;//b是swap声明的临时变量,跟main中的b没有任何关系

所以,整个swap函数运行完后,跟main中的a和b实际没有半点关系,有关系的地方仅仅在于(1)中,用main中的a和b的值初始化了swap声明的a和b,仅此而已。所以,这个swap函数对main中a和b的值没有任何影响。

再看第二段代码,其执行过程如下:

(1)声明两个临时变量,int *a,int *b

(2)a=&a,b=&b;=前面的a和b是swap函数声明的临时变量,=后面的是main中的实际变量a和b,也就是把main中a和b的地址分别赋值给swap中的临时变量a和b

(3)a=&x; 把x的地址赋值给a,a的地址发生改变,但a是swap中的临时变量!!!

(4)b=&y; 把y的地址赋值给b,b的地址发生改变,但b是swap中的临时变量!!!

所以,函数运行结束后,函数的功能实际上是改变了两个临时变量a和b的地址,而这两个临时变量跟main函数中变量a和b的关系仅仅是:在声明临时变量a和b的时候,用main中变量a和b的地址初始化了一下,然后再函数体内改变了临时变量a和b的值(内存地址),所以swap中的操作,跟main中的变量a和b没有半点关系,也就不会对他们的值产生影响。

既然上面两种方式都不能达到想要的结果,该怎么办呢?

函数的传参方式

函数传值分为传值和传址两种,传值方式对函数外的变量没有影响,传址方式对函数外的变量有影响,传值和传址的区别不在于参数,而在于在函数体内操作的对象。上面两个代码都属于传值操作,因为再函数体中操作的都是参数值本身。传址方式的差别在于,函数体中操作的是参数指向的内存。

看代码三:

#include <stdio.h>
void func(int* a, int* b)
{
	static int x = 100;
	static int y = 200;
	*a = x;
	*b = y;
}
int main()
{
	int a = 1;
	int b = 2;
	func(&a, &b);
	printf("%d %d", a, b);
	return 0;
}

这段代码跟第二段代码的区别仅仅在于swap函数中的最后两句。

在上面的代码分析中已经知道,函数体中的a是swap的临时变量,a被初始化为了main中变量a的地址,所以\*a =x,这个操作,其本质上就是把x的值放在a指向的内存中,而a指向的内存跟main中变量a的内存是同一块内存,所以,main中变量a的值就会发生改变。同理, \*b=y;将y的值放在了b指向的内存块中,main中b的值会发生改变。这种方式,就是传址。

在C++中,有引用的说法,引用就是变量的一个别名,他们拥有相同的内存,所以代码四也能实现预期的效果。

代码四:


#include <iostream>
using namespace std;
//这里的参数是引用,这里的参数a和b,是main中变量a和b的别名,他们具有相同的内存
void swap(int &a, int &b)
{
	int x = 100;
	int y = 200;
	a = x;
	b = y;
}
int main()
{
	int a = 3, b = 2;
	swap(a, b);
	cout << a << " " << b;
	return 0;
}


http://lihuaxi.xjx100.cn/news/76849.html

相关文章

Jenkins+git+maven持续集成持续部署java项目(CI/CD)

文章目录一. jenkins的作用二.安装三.maven安装四.git安装五. jenkins官方中文文档六. 集成七.配置自动化发布测试八.端口冲突解决九.构建触发器十.邮件通知一. jenkins的作用 二.安装 官网下载地址: https://www.jenkins.io/download/ 本人采用下载war,上传服务器直接运行方式…

Node.js——初识Node.js

系列文章目录 文章目录系列文章目录一、什么是 Node.js二、下载和安装 Node.js1、普通方式2、使用 nvm 安装三、Node.js 和 JavaScript 的区别1、ECMScript2、JavaScript3、node.js四、commonjs1、什么是 commonjs2、安装 lodash五、debugger六、server 开发和前端开发的区别一…

一个nginx部署多个应用及nginx总结

一、背景知识 1、Nginx是什么&#xff1f; &&#xff1a;Web服务器&#xff0c;静态文件保存、响应http请求。 同时可以提供代理、负载均衡的功能。 2、为什么使用&#xff1f; &&#xff1a;主要是轻量级和高性能&#xff0c;在一般行业不明显&#xff0c;但在互联…

使用docker快速搭建运维神器-spug

大家好&#xff0c;我是早九晚十二&#xff0c;目前是做运维相关的工作。写博客是为了积累&#xff0c;希望大家一起进步&#xff01; 我的主页&#xff1a;早九晚十二 什么是spug&#xff1f; 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理…

C++泛型编程:可变参数模板

最近在看有关智能指针源码的时候make_unique,make_shared(用来创建管理一个新对象)模板都是这么定义的 template<typename T, typename... Ts> std::unique_ptr<T> make_unique(Ts&&... params) {return std::unique_ptr<T>(new T(std::forward<…

(附源码)计算机毕业设计ssm黑河市劳务人员管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

再学 PHP 基础 之 数组排序函数总结

文章目录排序函数汇总对比排序类型标志按【值】排序的函数array_multisort()asort()arsort()natcasesort()natsort()sort()rsort()shuffle()按【键】排序的函数ksort()krsort()uksort()自定义比较函数uasortuksort()usort()打乱数组&#xff08;随机排序&#xff09;shuffle()排…

FIT5217 Natural language processing学习经验贴

一、课程简介 这门课就是我们口中常说的nlp&#xff0c;课程包含三部分&#xff0c;第一部分是上午的lecture,这部分主要是老师上课讲课&#xff0c;kun老师的课讲不太行&#xff08;主要原因还是口语太工地&#xff09;&#xff0c;建议大家自己看课件。第二部分是Tutorial&a…