在C语言中,那些容易让萌新犯错的特性,也正是编程大佬们为之吸引的特性。
比方说 '=' 和 '==' ,你肯定知道一个是赋值一个是判断是否等于,你也知道什么时候要用什么符号,但是~!!!可能因为手误,少打一个,或者多打一个。
这时你如果要是问大佬我要如何避免这个问题,他可能给你两个答案:1. 认真敲代码! 2. 当你想用 '==' 的时候,把判断的条件放在左边,变量放在右边。
这么一看肯定是2更加有效,毕竟大部分都是很认真在敲,但是还是会疏忽,虽然你这么记了,但是到真打的时候,你可能还会忘~
类似这个错误的还有很多,因为这样不是语法错误,他可以正常编译过去,但是它展示出来的效果不是你想要的效果,当然这个代码就有问题,这种问题是很让人头大的。接下来我要给大家解析一个很经典的题型供大家学习。
首先我们先看这段代码,你们先看看有什么问题。可不可以运行起来,如果可以运行起来,那么运行的结果是什么呢?
int main()
{
int i = 0;
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
for (i = 0; i <= 12; i++)
{
arr[i] = 0;
printf("hehe\n");
}
return 0;
}
我们先能看到的就是arr这个数组里只有10个元素,但是for循环遍历的时候访问了13个元素,首先想到的会不会是对数组遍历的时候越界访问了呢?
答案是肯定的,确实是越界访问了。我在x64下运行不起来,它会出现如下图片的错误:
但是在x86下可以正常运行,运行的结果就是一直打印 'hehe' 死循环,这个结果是不是出乎意料,难道越界访问不就是遍不过去了么,为什么还会打印,还是死循环打印 'hehe' ,接下来我就来分析分析~!
首先我们要先对数组和局部变量的在栈区的存放方式,如下图所示:
当我们了解上图的存储方式后,我们就可以顺利的分析了。
我们先对这个代码进行调试,因为调试可以看出问题所在。接下来我会给大家展示一步一步的调试,让大家看清楚出处。
开始没进行初始化的时候,数组里的数都是随机值,如下图所示:
然后对其进行初始化:
然后我们通过for循环遍历arr数组里的数值:
如此循环后我们从0~9全遍历一遍,arr[0]~arr[9]全赋值为0,此时i遍历到了9:
然后我们接下来再遍历就会产生越界访问了,当然在调试的时候是可以看到的,确实将不属于自己的空间赋值为0了。
接下来重点来了!!!
接下来我们继续调试:
这时候就已经很奇怪了,明明是i++,还没有对arr[12]进行操作,arr[12]为什么会改变呢,而且是和i的值是相同的,我们继续来调试:
诶,这不对啊,为什么将arr[12]赋值为0了以后i也跟着变为0了呢?这时候问题已经彻底显现出来了,i改变arr[12]也会跟着改变,相同arr[12]改变,i也跟着改变。那么这是为什么呢?
这时候我们开始的那张图就很重要了:
这里就可以观察到,如果以这种方式存放i和数组arr时,arr[12]和i就是同一个地方,当然一个地方肯定不可能用两个不同的方式表现,这里的arr[12]是越界过来的。
那么真实情况是不是这么存放的呢?我们继续观察:
所以事实就是这么存放的,当将arr[12]赋值为0的时候,也就是i赋值为0了,那么这个循环就会一直下去,成为死循环,你的屏幕上也就会一直打印 'hehe' ~
到这里我已经讲完了,你们肯定也听懂为什么这样了,其实不同编译器的存放方式不一样,我用的是2022是这样的。
当然这里如果定义i的时候放在定义数组的后面也就是:
这样arr[12]和i就不会指向一个空间,当然这样数组越界访问会产生错误,那么你可能会想,为什么第一种也越界了,还可以死循环的打印 'hehe' 呢,其实我是认为编译器忙着去打印 'hehe' 去了,编译器都忙的不行,哪管得到越界访问的问题~
到这里已经给大家详细讲解了这道题,讲解不易,麻烦各位看官给作者点个赞吧,让我看到创作的动力,谢谢你们~!!!