c深入剖析跨函数调用指针(多级指针)问题-程序员宅基地

技术标签: C语言  


在c语言中,如果想要通过函数调用来改变值,有两种方式,第一种是通过指针的传递来改变值(这种可以一次改变多个变量的值),第二种是通过方法的返回值来传递值。第一种,中传递的时候其实只是地址的传递,相对第二种的值传递来说,第一种的效率要高不少,因为第一种传递的是地址,四个字节(部分计算机)大小的地址。特别,是在c中做字符串的处理时,这种第一种情况用的非常的多,我当时也是在做字符串处理的时候遇到这些问题,然后下面我会举例,还有一些思想方法。

一、通过函数调用来交换值(一级指针)

void exchangeValue(int* a,int* b){
	//交换的时候,还是要通过第三个变量来交换两个变量的值
	int temp = *a;//a的值,也就是x的值,因为我们是将x的地址传给了a,所以a就指向了x,同理b也是如此
	*a = *b;
	*b = temp;
}


int main(){
	int x = 10;
	int y = 20;
	exchangeValue(&x,&y);
	cout << x << endl;//这两句话是c++中的输出,为了方便就没写printf了
	cout << y << endl;//输出结果是20和10
	system("pause");
	return 0;
}

上面的程序可以实现x和y值的交换,不知道大家,有没有这样想过,在exchangeValue方法中,为什么不能这样写

int* temp = a;

a = b;

b = temp;

这样能改变x和y的值吗?下面我们就来探讨一下这个问题

void exchangeValue(int* a,int* b){
	//交换的时候,还是要通过第三个变量来交换两个变量的值
	int* temp = a;//a的值,也就是x的值,因为我们是将x的地址传给了a,所以a就指向了x,同理b也是如此
	a = b;
	b = temp;
	cout << "a地址:" << a << endl;//a的地址:00E4FB64
	cout << "b地址:" << b << endl;//b的地址:00E4FB70
}


int main(){
	int x = 10;
	int y = 20;
	cout << "x地址:" << &x << endl;//x的地址:00E4FB70
	cout << "y地址:" << &y << endl;//y的地址:00E4FB64
	exchangeValue(&x,&y);
	cout << x << endl;//这两句话是c++中的输出,为了方便就没写printf了
	cout << y << endl;//输出结果是10和20
	system("pause");
	return 0;
}

上面的程序并不能改变x和y的值,他们其实是把a和b进行交换了,x和y并没有交换,可以从地址的值中可以看出来。 00E4FB70这个地址里面存放的就是10,而00D4FB64这个地址中存放的就是20。地址是一个8位16进制的数,因为我的电脑一个指针时占4个字节也就是32位,而8位十六进制,一个十六进制相当于4个二进制,所以说,8位十六进制其实也就是32位二进制。通过exchangeValue函数传过去的是两个变量的地址,而上一个方法是对两个地址所指向的值进行了交换,而第二个交换的是地址,实际上它们是交换了a和b的值。虽然说,a和x指向的是同一个值,但是并不代表它们是同一个变量,不要误以为我们把x和y的地址进行了交换。整形x和y的地址是不能被改变的,只能改变它们的内容。

下面的这个方法也可以改变x和y的值,但是不是通过方法调用改变的,只是为了深入分析一下指针问题

int main(){
	int* x = (int *)malloc(4);//这里必须的为指针变量x分配空间
	*x = 10;
	int* y = (int *)malloc(4);
	*y = 20;
	//交换x和y的内容(并不是交换他们的值所指向的内容)
	int *temp;
	temp = x;
	x = y;
	y = temp;
	cout << *x << endl;
	cout << *y << endl;
	system("pause");
	return 0;
}

上面的这个程序其实是交换了两个指针变量x和y里面的内容,本来指针变量x里面存放的是10的地址,而指针变量y中存放的是20的地址,后面通过temp指针变量,将指针变量x和y中的内容进行了交换,从而将两个指针变量所指向的int类型的值交换了。为什么指针变量x和y需要用malloc函数为其开辟空间呢,而temp指针却不需要呢?因为,如果你不给指针变量x开辟空间而是直接int* x = 10,这样会报x被使用而未初始化的异常,为什么呢?其实是因为,int* x= 10的意思是说,将10赋值以x中的内容为地址的int类型的变量,而你却没有给这个变量开辟空间,所以导致报错。而int* temp;temp = x;不需要用malloc为其开辟空间那是因为,你是将 指针变量x里面的内容赋值给了temp指针,从而让temp指针变量指向了10。

二、跨函数使用内存之二级指针

1、跨函数调用通过二级指针来给一个指针变量赋值,跨函数分配内存

void test(int **a){
	*a = (int*)malloc(sizeof(int));//这里其实在给指针变量x开辟空间,相当于在main方法中的int* x = (int*)malloc(sizeof(int));
	**a = 10;
}

int main(){
	int *x;
	test(&x);
	cout << *x << endl;
	system("pause");
	return 0;
}

2、通过二级指针实现字符串的分割

//根据第一个分割的字符串分割str字符串,返回分割字符串之前的部分和之后的部分
void str_split(char *str,char * strsubstring,char ** str_before,char ** str_after){
	*str_before = (char*)calloc(strlen(str),1);//为以str_before的内容为地址的字符串开辟空间并赋初值
	//malloc和calloc都是为指针变量开辟内存,malloc不会为开辟的空间初始化保留的是垃圾数据,而calloc会初始化
	*str_after = (char*)calloc(strlen(str),1);//用calloc的原因是为了避免产生乱码
	char * s = strstr(str,strsubstring);//根据第一个strsubstring进行分割,返回的是从strsubstring开始的一个首地址
	//如果字符串str中没有strsubstring这个被分割的字符串
	if (s==NULL){
		**str_before = '\0';
		**str_after = '\0';
	}else{
		//如果被分割的字符串刚好位于字符串的开头部分
		if (strlen(s) == strlen(str)){
			* str_after = s;//如果不想要分割的字符串可以这样写,* str_after = s+strlen(strsubstring);
			**str_before = '\0';
			//被分割的字符串刚好位于字符串的结尾部分
		}else if (strlen(s)==strlen(strsubstring)){
			int i = 0;
			int flag = (strlen(str) - strlen(s));
			while (i < flag){
				*(*str_before + i) = *(str + i);//如果能理解这句代码的含义,说明真正弄懂了二级指针的含义
				i++;
			}
			**str_after = '\0';
			//被分割的字符位于字符串的字符串的中间
		}else{
			* str_after = s;
			int i = 0;
			int flag = (strlen(str) - strlen(s));
			while (i < flag){
				*(*str_before + i) = *(str + i);
				i++;
			}
		}
	
	}
}

接下来,我们主要讨论一下这句代码的含义*(*str_before + i) = *(str + i),当时为了写出这句代码,我花了一下午的时间去理解跨函数使用二级指针。当时,就一直在想使用二级指针的时候**str = 'a';可以这样赋值,那我能不能通过数组去赋值呢?当时,我是这样写的**(str_before+i) = *(str+i),然后就会一直报异常,最后通过调试发现*str_before[0]可以正常赋值,而到了*str_before[1]的时候就会报异常,这是为什么呢?后面,我就仔细想了一想这句代码的含义。二级指针str_before,下面画个图方便理解


上面的图就是str_before的一个二级指针图,对于指针问题,如果不是很清楚的时候,画图理解是一个很好的方法。其中以0X开头的是内存单元的一个8位十六进制的地址。我上面的图画的不是很标准,因为在计算机中内存是一个字节大小的存储单元,也就是8位。而像0XAABB33DD这个值需要四个字节来存储,而地址一般是存的首地址。通过上面的图,我们再思考一下为什么*str_before[1]会报异常呢?因为,在c语言中[]的优先级要高于*,这样的话,*str_before[1]的意思就是,0XAABB33DD这个单元的第二个数据,而str_before[0]等于0XAABBEE11,str_before[1]这个值根本就不存在,所以导致报异常,如果改成这样(*str_before)[1]就没有问题了。现在,再来解释一下*(*str_before+i),*str_before指的就是0XAABB33DD这个单元,*str_before指向的就是0XAABBEE11中的第一个 字符,*str_before+1指向的就是0XAABBEE11中的第二个字符。而**str_before就等于字符a,*(*str_before+1)就等于字符b。最后,说一下个人感悟,作为一个程序员肯定是会遇到问题,在遇到问题的时候,怎么去高效的解决问题,这就体现了一个程序员的能力。我当时,遇到这个问题的时候,就只是想着在网上一顿乱搜,最后也没能找到答案,时间也浪费了,问题还没解决。我觉得,当我们遇到问题的时候,首先应该静下来仔细考虑想想整个程序的逻辑是怎么样的,理解每句代码究竟是什么意思。然后,再去找问题在哪,这样才能提高一个程序员解决问题的能力。最后,如果实在想不出来的话,可以请教他人,再想想自己还有那些知识点没弄明白,然后再去补短,这样才能有所提高。上面纯属个人见解,如有问题尽请留言。



版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sinat_29957455/article/details/63252145

智能推荐

用共享游标提升 MSSQL 性能-程序员宅基地

文章浏览阅读178次。本篇文章由泉州SEOwww.234yp.com 整理发布,mssql数据库www.234yp.com/Article/168188.html谢谢合作!mssql数据库  Boost SQL Performance with cursor_sharing    关键词:cursor_sharing    概述  本文阐述在Oracle8i Release 2和Oracle9i中增强的游标共享设施。这些增强功能被一个新的参数cursor_sharing控制。  cursor_sharin...

OC Extension UIImage+FHXImage(图片扩展)_ocuiffx-程序员宅基地

文章浏览阅读135次。**一直觉得自己写的不是技术,而是情怀,一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的,希望我的这条路能让你们少走弯路,希望我能帮你们抹去知识的蒙尘,希望我能帮你们理清知识的脉络,希望未来技术之巅上有你们也有我。**使用1.输入图片颜色返回一张图片icon.image = [UIImage createImageWithColor:[UIColor blueColor]];2.裁切图片的一个点进行延伸[btn setBackgroundImage:[UIImage _ocuiffx

python xlrd安装_详解python中xlrd包的安装与处理Excel表格-程序员宅基地

文章浏览阅读221次。一、安装xlrd地址下载后,使用 pip install .whl安装即好。查看帮助:>>> import xlrd>>> help(xlrd)Help on package xlrd:NAMExlrdPACKAGE CONTENTSbiffhbookcompdocformattingformulainfolicencessheettimemachinexldatexlsxFUNCTIONScou..._"file \"c:\\program files\\python38\\lib\\site-packages\\xlrd\\__init__.py\", line 17"

GitHub 上 6 款好看的后台模板_好看的静态网页模板github-程序员宅基地

文章浏览阅读1.9k次。一套既美观又方便的后台框架可以大大幅节约开发时间和成本,本文推荐 6 款漂亮、功能强大的后台模板。01.vue-element-adminvue-element-admin 是一个后台模板,它基于 vue 和 element-ui 实现,可以帮助你快速搭建企业级中后台产品原型。功能非常丰富,几乎囊括了你见过的所有组件元素,可以通过下面这个链接体验一下。地址:https://panjiachen.github.io/vue-element-admin地址:https://github.com._好看的静态网页模板github

完美解决导入from PIL import image出现错误的问题_from pil import image报错-程序员宅基地

文章浏览阅读9.3k次。1)运行此命令:import sysfrom PIL import Imagesys.modules['Image'] = Image2)在笔记本中运行以下两行,以确保它们正确指向同一目录(如果不是因为你的PIL旧库弄乱了Pillow库)from PIL import Imageprint(Image.__file__)import Imageprint(Image.__file__)3)如果它正常工作并且两个导入打印指向同一个python3目录,那么继续.如果不,转到您的操作系统_from pil import image报错

(要更新)N沟道和P沟道MOSFET_p沟道mos管电流方向-程序员宅基地

文章浏览阅读8.2k次,点赞5次,收藏27次。一、借鉴基础知识: MOS管基础知识百度文库链接:https://wenku.baidu.com/view/fc0a7d2eccbff121dd3683b2.html 首先,我并没有转载某知名博主的文章,只是觉得PPT的图片截取的还可以; 其次,博客地址:https://blog.csdn.net/zhengyanan815/article/deta..._p沟道mos管电流方向

随便推点

CentOS7安装Oracle21c-程序员宅基地

文章浏览阅读2.1k次,点赞2次,收藏6次。Oracle 21cCentOS 安装Oracle 21c_centos7安装oracle21c

什么是代码评审(Code Review)-程序员宅基地

文章浏览阅读3.9w次,点赞39次,收藏174次。Code Review(CR)即代码评审,又名代码走查,是一种通过复查代码来提高代码质量的过程,一般体现在一个团队的开发过程中。CR要求团队成员有意识地、系统地检查彼此的代码,从而验证需求、发现错误,同时指出其中不合规范的“低质量”代码,从而提高整个团队的代码质量。一次 CR 可以是一次 Commit,也可以是一次 Merge Request。因此,实践课系统支持团队内部的 MR 评审以及 Commit 评审,供大家学习和交流。..._code review

hexo d错误:fatal: unable to auto-detect email address-程序员宅基地

文章浏览阅读2.1k次。1.报错内容 、 *** Please tell me who you are. Run git config --global user.email "[email protected]" git config --global user.name "Your Name" to set your...

cytoscape.js制作数据展示网络图_cytoscape.js 网络推普图-程序员宅基地

文章浏览阅读3k次,点赞2次,收藏5次。开始直接上效果图不墨迹直接上代码html<!DOCTYPE html><!-- This code is for demonstration purposes only. You should not hotlink to Github, Rawgit, or files from the Cytoscape.js documentation in you..._cytoscape.js 网络推普图

AndroidQ适配,Primary directory audio not allowed for content_java.lang.illegalargumentexception: primary direct-程序员宅基地

文章浏览阅读4k次。今天用android Q版本做文件写入适配操作,报了标题的错,具体报错如下:2021-04-29 20:40:54.078 22377-22377/com.example.ringtone E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.ringtone, PID: 22377 java.lang.IllegalArgumentException: Primary directory audio not al_java.lang.illegalargumentexception: primary directory sounds not allowed for

前向传播算法(Forward propagation)与反向传播算法(Back propagation)-程序员宅基地

文章浏览阅读10w+次,点赞217次,收藏892次。虽然学深度学习有一段时间了,但是对于一些算法的具体实现还是模糊不清,用了很久也不是很了解。因此特意先对深度学习中的相关基础概念做一下总结。先看看前向传播算法(Forward propagation)与反向传播算法(Back propagation)。1.前向传播如图所示,这里讲得已经很清楚了,前向传播的思想比较简单。 举个例子,假设上一层结点i,j,k,…等一些结点与本层的结点w有连接,那么结点_反向传播算法

推荐文章

热门文章

相关标签