C-为什么更改小数的阵列尺寸值操纵字节



这是来自计算机系统的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 

教授解释说,访问乐趣(2)操纵double d的字节。但是,我没有得到:(a)为什么要操纵双重字节启动 fun(2),(b)每个字节如何处理每个字节都与诸如 fun(2) ➙ 3.1399998664856fun(3) ➙ 2.00000061035156等的值相关,直到 fun(6),以及(c)为什么它准确地达到关键状态fun (6)?有关我的问题的更多参考,请参见此处的第8和9号幻灯片。此外,关于幻灯片的说明图我不理解。感谢您是否可以花一些时间来解释。

幻灯片9上的图表示fun调用中的本地内存。每一行代表4个字节(从右至左列出),并且随着您下降时的内存地址减小。如果要列出地址0、1、2,...以这种格式,它看起来像这样:

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

幻灯片9上的图显示了如何在内存中布置s(类型struct_t的变量)。该系统使用4字节int s和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个元素)。

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

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

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

但是,很常见的是,在C中向本地数组的界限写入界限会在某个时候破坏返回地址。这是因为编译器几乎普遍实现函数调用和局部("自动")通过堆栈存储,因此包含本地变量并将返回地址交互。

最新更新