2015-05-23 151 views
0

我正在通过here,发现malloc可能会导致不需要的行为,如果我们不包括stdlib.h,转换返回值,如果指针和整数大小在系统上不同。malloc返回类型混淆

下面是该问题给出的代码片段。这是在指针和整数大小不同的64位机器上尝试的。

int main() 
{ 
     int* p; 
     p = (int*)malloc(sizeof(int)); 
     *p = 10; 
     return 0; 
} 

如果我们不包括stdlib.h,编译器将承担malloc返回类型为int,铸造,并分配给不同大小的指针可能会导致不必要的行为。 但我的问题是为什么铸造intint*并将其分配给不同大小的指针可能会导致此问题。

+1

首先,它被(隐式)转换为“int”,从而从8个字节截断为4个字节。除非原始8字节值的第一个(更重要的)4个字节包含全零,否则某些信息会丢失。将结果转换回'int *'不会检索到这些信息,并留下一个“假”指针。 –

+0

当编译器遇到'int * p'声明时,它会在声明时确定此指针需要8个字节(例如)的存储空间。接下来,在运行时,执行malloc时,它会返回一个默认的'int',因为没有函数原型声明存在(由于缺少stdlib.h)。当新分配的内存的地址(8个字节)存储在一个int(4个字节)并返回给您时,可能会失去重要性。现在,在这一点上,如果您尝试将int转换为int *并将其分配给您的指针'p',那么可能会出现意想不到的行为。 – iammowgli

回答

1

几乎任何函数都会导致未定义的行为,如果在调用它之前没有给出原型,除非例如它的原型是int function(int x);

但是很明显,如果一个指针的大小比的intmalloc()规模较大返回int,因为隐式声明的,则返回的地址可能不是真正的地址,因为例如,它可能不是可以用较少的位来表示它。

解引用它将是未定义的行为,顺便说一下,你不能测试,因为它是未定义的,你会期望发生什么?它是未定义的!

那么,没有什么可以在那里测试?

5
int main() 
{ 
     int* p; 
     p = (int*)malloc(sizeof(int)); 
     *p = 10; 
     return 0; 
} 

在C99和C2011规则,调用malloc没有可见的声明是一个约束违反,这意味着符合编译器必须发出诊断。 (这与C接近说有些东西是“非法的”)。如果你的编译器没有警告这个调用,你应该找出使用它的选项。

根据C90规则,调用没有可见声明的函数会导致编译器假定该函数实际上返回类型为int的结果。由于malloc实际上定义为返回类型为void*,因此行为未定义;编译器不需要进行诊断,但标准完全没有说明在调用评估时会发生什么。

什么通常发生在实践中是,编译器生成的代码仿佛malloc被定义为返回一个int结果。例如,malloc可能会将其64位void*结果放在某个特定的CPU寄存器中,并且调用代码可能为假定该寄存器包含一个32位的int。 (这不是一种类型转换;它只是错误的代码,错误地将一种类型的值看作是不同类型的值。)(可能是垃圾)int值然后是转换为int*并存储在p中。你可能失去高位返回指针的低位32位 - 但这只是一个可以出错的任意方式。

malloc可能会将其64位结果压入堆栈,调用者可能只弹出堆栈中的32位,导致堆栈未对齐,从而导致所有后续执行不正确。由于历史原因,C编译器通常不使用这种调用约定,但标准允许它。

如果intvoid*int*都发生在相同的大小(因为他们经常是在32位系统),代码是可能的工作 - 但即使这不能保证。例如,调用约定可能会使用一个寄存器来返回int结果,而另一个返回指针结果。再次,大多数现有的C调用约定允许使用这样的假设的旧的错误代码。

调用malloc需要#include <stdlib.h>,即使有些编译器可能不会执行该要求。添加#include(并放下演员)要比花时间思考如果没有时会发生什么更容易。