《C陷阱与缺陷》之“语义”陷阱——数组越界导致的程序死循环问题

简介: 《C陷阱与缺陷》之“语义”陷阱——数组越界导致的程序死循环问题

一.问题引入

我们先来一起看一段代码,思考一下它运行的结果可能是什么?

#include <stdio.h>
int main()
{
  int i = 0;
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  for (i = 0; i <= 12; i++)
  {
    arr[i] = 0;
    printf("hehe\n");
  }
  return 0;
}

我们先来简单分析一下这段代码:

我们可以看出来这段代码是通过一个for循环对数组的元素进行遍历重新赋值为0,但是我们很容易发现这段代码在访问数组时越界了,数组只有10个元素,第10个元素的下标应该是9,但是我们访问的下标i却是0~12;我们想到的结果可能是:


1.编译器直接报错(因为数组越界访问了)

2. 不报错的话,循环12次,打印12个"hehe"。


但是结果会和我们想象的一样吗?我们运行一下看看(这里使用的编译器是visual studio 2022)

94bb3316a7a04e0a8ecd347dc51e5fef.png

我们用visual studio 2022运行该代码,程序陷入了死循环,为什么会出现这样的结果呢?

二.问题分析

下面我们通过调式来观察一下,导致死循环的原因是什么:

c36672dd776c4f5ea5c77e1bdf19f3fb.png

那么既然在调试过程中,i 的值和 arr[12] 的值一直相等,我们猜想,i 和 arr[12] 是不是处在同一块内存空间上。

我们继续调试看一看:

da6eb22ddfa440ffb78f9b7a59703cea.png

所以,现在我们就大概明白了,因为arr[12]和i的地址时相同的,所以我们访问arr[12]并把他赋值为0 时,i的值也变成了0,这样i的值一旦增加到12,就会变成0,永远不会大于12,因此for循环永远不会结束,陷入了死循环。


那为什么会这样呢,为啥数组越界会访问到i呢?


三.原理解释

现在我们就给大家解释一下,为什么会这样?为啥数组越界会访问到i呢?


之前的文章里提到过,一般来说,内存分为栈区,堆区和静态区。

7fb9c1f90aa347c99fc3b53e48260a04.png

而我们在这里创建的变量i,还有数组arr,它们都是局部变量

1.局部变量是定义在栈区的,栈区内存的使用习惯是先使用高地址,再使用低地址

2.而数组元素的地址随着下标的增加而增加

3.所以数组元素在向后越界访问(访问的地址逐渐变高)的时候,就有可能访问到i,因为i比数组先创建

4.一旦访问到i并将i置成0,就会发生死循环

bbd409ab451b4c54a7fa702bb45fb6d1.png

在visual studio 2022/2013/2019上,i 和数组 arr 之间都是隔了2个整型的空间(即数组越界2个整型就访问到了i),但i 和数组 arr 之间隔多大空间是取决于编译器的,不同的编译器可能有所差异:


visual studio 2022/2013/2019上,i 和数组 arr 之间隔了2个整型的空间

vc 6.0上, i 和数组 arr 之间没有多余的空间

gcc上,i 和数组 arr 之间隔了1个整型的空间

这就是这个程序出现死循环的原因,当然决定这种结果有一个重要的原因就是我们把 i 定义在了数组 arr 之前,所以它的地址更高,因此数组向后越界才会访问到 i。


如果我们将 i 定义在数组 arr 之后,就不会访问到 i 了,也就不会死循环了。

#include <stdio.h>
int main()
{
  int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int i = 0;
  for (i = 0; i <= 12; i++)
  {
    arr[i] = 0;
    printf("hehe\n");
  }
  return 0;
}

027cd458f53e460c94b8fe88f97722b1.png

这样打印12个“hehe”之后,编译器就直接报错了,程序就挂掉了。

以上就是对该问题(出自《C陷阱与缺陷》第3章“语义”陷阱 的第6节 )的全部讲解,欢迎大家指正!!!

目录
相关文章
|
存储 自然语言处理 编译器
C陷阱与缺陷
C陷阱与缺陷
62 0
C陷阱与缺陷
|
6月前
|
存储 编译器 程序员
C陷阱——数组越界引发的死循环问题
C陷阱——数组越界引发的死循环问题
|
6月前
|
并行计算 安全 开发者
避免Python多线程中的常见陷阱与错误
避免Python多线程中的常见陷阱与错误
|
6月前
|
测试技术
常见测试陷阱
常见测试陷阱
|
6月前
|
存储 程序员 编译器
C陷阱与缺陷:语法陷阱
C陷阱与缺陷:语法陷阱
52 0
|
6月前
|
自然语言处理 编译器 程序员
C陷阱与缺陷:词法陷阱
C陷阱与缺陷:词法陷阱
51 0
|
存储 编译器 C语言
C语言编程陷阱:语义陷阱
C语言中只有一维数组,数组大小必须在编译器就作为一个常数确定下来。 C语言中数组的元素可以是任何类型的对象。
47 1
|
自然语言处理 编译器 程序员
【C陷阱与缺陷】----语法陷阱
由于一个程序错误可以从不同层面采用不同方式进行考察,而根据程序错误与考察程序的方式之间的相关性,可以将程序错误进行划分为各种陷阱与缺陷
118 0
|
存储 人工智能 自然语言处理
【C缺陷与陷阱】----语义“陷阱”
那获得该下标为0的元素的指针,如果给这个指针加1,就能得到指向该数组中下一个元素的指针。也就是指针+一个整数得到的还是指针,只不过指针的位置发生改变
106 0
|
编译器 C语言
源于《C陷阱与缺陷》----研究程序死循环问题
所以最后答案应该就是打印了12次xiao tao,然后越界访问出现错误,使arr[10]=0,arr[11]=0了 但最后答案却不是这样。
117 0