Some Strange Infinite Loop in C

在斯坦福公开课《编程范式》中见到的几种特殊的无线循环,需要从内存模型、汇编的角度理解,蛮有意思的。

We should understand why these program does what it does.

  • 数组越界
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>

int main(int argc, char const * argv[]){
int i;
int array[4];

for(i=0; i<=4; i++){ // 这里故意数组越界
array[i] = 0;
}

return 0;
}

该程序的内存模型:

--------
| saved PC |
| ———- |
| i = 0 |
| array[3] |
| array[2] |
| array[1] |
| array[0] |

i = 0 时,for 循环条件为真,array[0]=0;
i = 1 时,for 循环条件为真,array[1]=0;
i = 2 时,for 循环条件为真,array[2]=0;
i = 3 时,for 循环条件为真,array[3]=0;
i = 4 时,for 循环条件仍然为真,array[4]=0,也就是说i本来是4,这一步由于数组越界而将其设置为0;
于是,循环条件仍然为真,会无限循环下去。

  • 还是数组越界

将上面程序里面的int array[4]改为short array[4],仍有可能出现无限循环。先来看i=4时内存模型:

Big Endian 系统:

| saved PC |
| 0 0 0 4 |
| array[2] | array[3] |
| array[0] | array[1] |

Little Endian 系统:

| saved PC |
| 4 0 0 0 |
| array[2] | array[3] |
| array[0] | array[1] |

可以看到在Little Endian 系统中,又无限循环了。

  • 仍然是数组越界
1
2
3
4
5
6
7
void foo(){
int array[4];
int i;
for(i=0; i<=4; i++){ // 这里故意数组越界
array[i] -= 4;
}
}

内存模型:

| saved PC |
| array[3] |
| array[2] |
| array[1] |
| array[0] |
| i |

当调用函数foo时,数组array没有初始化,无论没一个元素的值是多少,for循环
会将数组元素的值减4。问题来了,当i=4时,数组越界将saved PC的值减4

我们知道,saved PC是一个指针,指向汇编指令CALL <foo>的下一条指令,减去4过后,
又指向CALL <foo>这条汇编指令,foo返回时又开始调用函数foo,于是无限循环下去。