为什么更改数组大小值操纵十进制字节?

时间:2017-02-25 08:07:25

标签: c memory reference system

这是来自计算机系统的CMU课程。在以下示例中:

 typedef struct { 
  int a[2]; 
  double d; } struct_t; 

double fun(int i) { 
volatile struct_t s;
  s.d = 3.14; 
  s.a[i] = 1073741824; /* Possibly out of bounds */ 
  return s.d; } 
fun(0)  ➙ 3.14 
fun(1)  ➙ 3.14 
fun(2)  ➙ 3.1399998664856 
fun(3)  ➙ 2.00000061035156 
fun(4)  ➙ 3.14 
fun(6)  ➙ Segmentation fault 

教授解释说,访问fun(2)会操纵double d的字节。但是,我没有得到:(a)为什么这会操纵双字节开始fun(2),(b)每个字节操作如何与fun(2) ➙ 3.1399998664856fun(3) ➙ 2.00000061035156之类的值相关联,直到{{ 1}},和(c)为什么它恰好在fun(6)达到临界状态?有关我的问题的更多参考,请参阅here幻灯片编号8和9.此外,幻灯片上有一个我不明白的说明图。感谢您是否可以花些时间解释一下。

1 个答案:

答案 0 :(得分:0)

幻灯片9上的图表表示对fun的调用中的本地内存。每行代表4个字节(从右到左列出),内存地址随着下降而减少。如果您要以此格式列出地址0,1,2,...,它将如下所示:

|...
+--+--+--+--+
|11|10| 9| 8|
+--+--+--+--+
| 7| 6| 5| 4|
+--+--+--+--+
| 3| 2| 1| 0|
+--+--+--+--+

幻灯片9上的图表显示了s(类型为struct_t的变量)如何在内存中布局。系统使用4字节int和8字节double s。因此s.a[0]占用4个字节(图中的行0),s.a[1]另外4个(行1)和s.d 8个字节(行2和3)。

该功能访问s.a[i]。编译器将其转换为代码,该代码采用s.a的起始地址并向其添加i*4个字节以到达所选元素。在图中,这对应于从第0行开始向上行i行。只要i实际上是数组中的有效索引(例如:01,因为a只有2个元素),这样就可以正常工作。 / p>

但如果i更大,那么代码最终会访问内存的其他部分。 s.a[2](图中的第2行)指的是s.d的一部分内存,因此覆盖它会破坏存储在那里的值(s.a[3]相同)。确切的结果值取决于所使用的浮点格式的内部结构(可能是IEEE 754)。 (我对此并不熟悉,所以我不知道这些位是如何被解释为获得3.1399998664856的。)

s.a[4]显然并不重要,因为覆盖它没有任何明显的效果。但覆盖s.a[6]崩溃,表明我们摧毁了至关重要的东西。这可能是返回地址,即保存的位置告诉fun完成后跳转到哪里。通过覆盖它,我们使fun跳转到无效的内存。

要确认这一点(并找出为什么它的索引6特别破坏了东西),你必须查看编译器生成的代码。没有一般性的答案,因为它取决于所讨论的编译器,优化级别,运行的系统等等。

但是,在C中写出超出本地数组的边界通常会在某些时候破坏返回地址。这是因为编译器通常使用堆栈来实现函数调用和本地(“自动”)存储,因此堆栈包含局部变量和返回地址混合。