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编译器通常不使用这种调用约定,但标准允许它。
如果int
,void*
和int*
都发生在相同的大小(因为他们经常是在32位系统),代码是可能的工作 - 但即使这不能保证。例如,调用约定可能会使用一个寄存器来返回int
结果,而另一个返回指针结果。再次,大多数现有的C调用约定允许使用这样的假设的旧的错误代码。
调用malloc
需要#include <stdlib.h>
,即使有些编译器可能不会执行该要求。添加#include
(并放下演员)要比花时间思考如果没有时会发生什么更容易。
首先,它被(隐式)转换为“int”,从而从8个字节截断为4个字节。除非原始8字节值的第一个(更重要的)4个字节包含全零,否则某些信息会丢失。将结果转换回'int *'不会检索到这些信息,并留下一个“假”指针。 –
当编译器遇到'int * p'声明时,它会在声明时确定此指针需要8个字节(例如)的存储空间。接下来,在运行时,执行malloc时,它会返回一个默认的'int',因为没有函数原型声明存在(由于缺少stdlib.h)。当新分配的内存的地址(8个字节)存储在一个int(4个字节)并返回给您时,可能会失去重要性。现在,在这一点上,如果您尝试将int转换为int *并将其分配给您的指针'p',那么可能会出现意想不到的行为。 – iammowgli