必备技能之——函数调用、指针的传递、参数的传递问题、引用问题和调用栈的详细理解(小白)

2023-05-16

目录

  • C语言指针巩固
  • 总结

C语言指针巩固

在这里不会给出那种三四个*的那种神级代码,都是很常规的操作,大家可以放心参考,有忘记这部分内容的朋友也可以加以巩固

首先,来一段小代码:
在这里插入图片描述

在这里插入图片描述
试问程序输出的是什么?
在VS上运行的话,会发现别说输出了,这个程序是有问题的,程序会告诉你:

在这里插入图片描述
程序运行发生了错误,导致程序崩溃了
在上面的调试界面上str显示一直是NULL
再来看一个:
在这里插入图片描述
这段程序和上面的很像,试想一下它的输出

同样很抱歉的通知您,程序会乱码,像这样:
在这里插入图片描述

在这里插入图片描述
在这里就牵扯到了——栈的问题,我一直觉得栈、指针还有引用这一块一直就像连体婴儿一样,谁也离不开谁,所以在这里我将一块说明和理解。
在这里插入图片描述
在第一个程序,有点基础的朋友都看出来了,是在GetMemory()函数传递过来的参数应该是带有才两个**才对,在这里我再详细一些:
我对于指针的理解就是:没什么特别的,就是有一个类似箭头的东西在内存里面,这个箭头指向的可以是基本的那些类型或者指向别的箭头,我也同样可以指向一块空白的区域,就如我上面的,我直接就让指针“=”了一块malloc的区域,此时这块区域还没有添加什么东西,所以我在上面就暂且称之为“空白区域”
在参数传递的时候有些人不理解为什么要加两个,那么在理解两个 ** 之前,先理解一下为什么要加星号

还记得一段很简单的三个数交换的小程序:

void swap(int a,int b){
   int t=a;
   a=b;
   b=t;
}
int main(){
  int a=3,b=4;
  swap(a,b);
  printf("a=%d,b=%d",a,b);
  return 0;
}

这段程序大家都懂最终的输出结果根本就没有改变依旧是a=3,b=4 至于为什么会这样我也相信每个人有每个人不同的理解,下面讲讲我自己的理解:
回想“赋值”这个操作:
简单的 a=a+1,其实这句话在常人的理解上,当然是诡异的,但在计算机上是这样理解的:
分为两步:
1、首先先执行等号右边的a+1 ,这个a+1相当于一个新的东西,且计算机也为你打包好了这个东西
2、再将这个打包好的东西覆盖原来的那个a
其实这就有点像一个隐藏式的函数一样,不过这个函数是“特别”的

那么在函数调用里面我们同样来分析一下:
1、先计算参数的值
在main函数里面的那个swap(a,b)----(就是swap(3,4)啦)在这里的3和4是实际参数(实参),什么意思,就是我的的确确有两个变量,并且这两个变量的值分别为3和4
2、将实参的值赋值给函数声明中的a和b,我传递到了我的swap函数里面,首先我问你,我在swap函数里有没有定义像在main函数里那样:int a,b这种,并没有吧,这与我上面说的1 实参概念稍有不合,说明这两个变量不是实参,在这里计算机给了他一个别名,叫做“形参”
(大家都懂实参才是最后算出来的那个值,只有实参才是改变你变量的那个量)

在这里我对于形参和实参的理解:
实参就是我明明确确我知道这个东西是怎么来的,比如在这里最简单的我声名了,我定义了int a=3,b=4我是知道他“怎么来的”,但是在形参里我不知道这个3和4是怎么来的,我只知道有这个东西我不知道这个东西是经过了多少复杂步骤得来的值我只知道有这个值,就此而已,所以我去改变这个值的时候,并不会影响到真真正正的那个3和4也就是a和b,因为——你就是个假货!
在这里插入图片描述
这样子一来程序里就有两个变量a和b,一个在main函数里定义,一个是swap函数里的形参,有人会问一个问题:相同的两个变量名字不是不能相同的吗或者类似这种不能够有两个相同的变量的问题,那计算机不会混淆?
回答:不会滴,这两个变量在计算机中被称为“局部变量”,不同函数之间的局部变量之间是相互独立的:无法访问其他函数的局部变量(局部变量的存储空间是临时分配的,函数执行完毕之后就会被系统释放掉)相当于你是你的我是我的就像在这个世界上有和你同名的人但是你们的父母不是同一个人,所以没什么影响(额…这样好像不太有道理,但是大概理解了吧)
函数的形参和函数内部声明的变量都是该函数的局部变量

所以呢,到最后函数的调用过程就可以理解成计算实参的值,然后赋值给对应的形参,然后将当前代码行转移到函数首部(就是又回到调用它的地方)
在这里就更深入的理解就是——调用栈的理解
在这里插入图片描述

调用栈讲的就是函数之间的调用关系,它由多个栈帧组成,每个栈帧都对应一个未运行完的函数,在栈帧中保存了该函数的返回地址和局部变量
这样就可以实现我们所说的不同函数之间的局部变量互不相干——因为是在不同的栈帧里面
在这里如果有gdb调试器的朋友就可以用bt命令打印所有的栈帧信息
在这里插入图片描述
解决的办法就是:使用指针作为参数,前面我也说过了,指针就是指向一个东西的箭头而已,那么我们现在让形参指向实参所在区域,有一种“重合”的感觉
正确的代码:

void swap(int* a,int* b){
   int t=*a;
   *a=*b;
   *b=t;
}
int main(){
  int a=3,b=4;
  swap(&a,&b);
  printf("a=%d,b=%d",a,b);
  return 0;
}

在main函数这里的那个引号‘&’,最初是在scanf里面看到的,这个引号的作用同样有一种指向本体的那种感觉,就是引用嘛,改变的也是本质
在上面的代码有些人可能会对 :int t=*a这个东西有点好奇,怎么左边是变量右边是指针的那种感觉,所以有些人可能会这样写:int *t=*a
这种写法怎么说呢,它可能会得到正确的答案,也看上去没什么问题,但是但是,我们分析一波:
在这里插入图片描述
首先,t是一个指向int类型的指针,因此 *t 是一个整数,这个没问题,ok 那么用一个整数作为辅助变量去交换两个整数有什么不妥呢?
问题就在于,我问你,t的地址什么?t指向的是什么,在这里其实t也是一个变量来的(指针也是变量,不过这个变量的类型是“指针类型”而已),那么就如我们一开始比如说 : int a;我定义了一个int类型的变量a,在没有赋值的时候你知道程序可能会给一个乱码值(随随便便的一个值),所以根据规则,它在赋值之前是不确定的。
那么这个不确定的值在执行赋值操作的时候,如果这个不确定的值所代表的内存单元恰好是可以写入的,那么ok没什么问题;但是如果说这个东西它是不可以读入的,那么你的程序就会直接崩溃
举个例子,比如像:int *t=0你可以试试看看内存地址为0的能不能写入
所以,归根结底,习惯问题,这一个简简单单的语句,就体现出你的代码习惯是否良好,甚至你的代码功底很多时候就是在你的代码的习惯中得以体现的在这里插入图片描述

好了好了回到我们的第二个程序代码中来,那个乱码的那个程序,理解了前面的现在就好解释了,解释一下为什么会乱码:
首先,那个GetMemeory()那个函数的返回值是一个指针,但是我们知道这个函数是在他自己的“栈帧”里面的,所以这个指针同样指向的肯定是它自己嘛,不然指向谁,有些人会说指向参数的那个函数,这个…有时候你的两个参数可能一个来自这个函数,一个又来自另外的函数啊,而且由此可以知道这个指针的地址不为NULL,而且我们也知道在一个函数执行完毕之后它的局部变量会被系统释放掉,那现在就是p就是被释放了,那么新的内容不得而知了,所以系统就直接随便抛出了一个值给你在这里插入图片描述

总结

这里只是小小的讲了一下我自己对这些问题的理解,比较基础但是够详细,有问题欢迎私信和评论!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

必备技能之——函数调用、指针的传递、参数的传递问题、引用问题和调用栈的详细理解(小白) 的相关文章

随机推荐