malloc返回类型混乱

Gee*_*kyJ 0 c malloc pointers

我正在经历这里,发现如果我们不包含stdlib.h,则转换malloc会导致不需要的行为,转换返回值以及系统上的指针和整数大小是否不同.

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

int main()
{
      int* p;
      p = (int*)malloc(sizeof(int));
      *p = 10;
      return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果我们不包含stdlib.h,编译器将假定malloc返回类型为int,并且转换它并分配给不同大小的指针可能会导致不需要的行为.但我的问题是,为什么铸造intint*并将其分配给不同大小的指针可能会导致问题.

Kei*_*son 5

int main()
{
      int* p;
      p = (int*)malloc(sizeof(int));
      *p = 10;
      return 0;
}
Run Code Online (Sandbox Code Playgroud)

在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(并放弃演员表)要比花时间思考如果不这样做可能会发生什么更容易.