2017-07-16 45 views
-1
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; 
    return s.d; 
} 

enter image description here存储器引用错误例

我遇到了这个例子,同时了解CSAPP过程。解释如下所示。但我仍然无法弄清楚。 enter image description here

+0

在本例中考虑int的sizeof,double的sizeof以及保证可写的内存。 –

+2

你真正的问题是什么?你不明白什么? –

+0

'double fun(int i){if(i < 0 || i > 1){.. handle error ..} ...}'? –

回答

2

我相信(告诉我,如果我错了),因为结构被存储为一件大事,引用超出范围的数组索引将调出结构中下一个元素的数据。

在内存中,有一个int,另一个int,然后是一个double。它们全都相邻存放。

让我们举个简单的例子:

typedef struct { 
    int X[2]; 
    int Y; 
} struct_t; 

如果我们访问元素#3(X [2])的struct的,我们会得到Y,就像这样:

void main() { 
    volatile struct_t s; 
    s.X[0] = 99; 
    s.X[1] = 98; 
    s.Y = 97; 
    printf("%i", s.X[2]); 
} 

该输出97,因为struct_t的第三个int大小的块是97.

现在让我们看看你的问题。 struct_t的前四个字节属于int数组。最后八个属于双。 fun()将最后八个设置为3.14,根据我的编译器,它等于0x51EB851F。然后它将A的第I个字节设置为0x4000。因此,通过写入“第三个”数组元素(与double重叠)。这将覆盖double,并改变它的值。

通过写入第6个字节,您开始写出内存不足,这给您段错误。不过,GCC有另一个怪癖。 CPU可以更快地访问偶数地址上对齐的数据,因此GCC会将填充插入到结构中。另外,intdouble的大小也会随着系统的不同而变化,这使得本例在不同的系统上输出不同的东西。

+0

从会计观点来看,您没有关闭,而是从您调用的C标准观点*未定义行为*尝试访问定义范围之外的数组元素。 –

+0

这是真的。 CSAPP课程正在访问一个超出界限的数组来展示一个观点,并且可能不是旨在鼓励导致未定义行为的代码(至少,我希望如此)。 –

+1

未定义的行为通常在stackoverflow中被忽视。我认为这有点失控,这是一个完全教育的例子,只要你添加免责声明,不同的系统可能会表现出不同的行为,并且解释显示在特定情况下发生了什么 – Leeor