我正在经历here并发现如果我们不包含stdlib.h,则会导致不需要的行为,转换返回值以及系统上的指针和整数大小是否不同。
下面是SO问题中给出的代码段。这是在64位机器上尝试的,其中指针和整数大小不同。
int main()
{
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
}
如果我们不包含stdlib.h
,编译器将假定malloc
返回类型为int
,并且将其转换为分配给不同大小的指针会导致不必要的行为。 但我的问题是为什么将int
投射到int*
并将其分配给不同大小的指针会导致问题。
答案 0 :(得分: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位{{1} }}。 (这不是类型转换;它只是错误的代码,错误地将一种类型的值视为不同的类型。)那么(可能是垃圾)int
值是转换到int
并存储在int*
中。你可能丢失了返回指针的高阶或低阶32位 - 但这只是其中一个可能出错的任意方式
或者p
可能会将其64位结果推送到堆栈上,并且调用者可能只从堆栈中弹出32位,从而导致堆栈错位,导致所有后续执行不正确。由于历史原因,C编译器通常不使用这种调用约定,但标准允许它。
如果malloc
,int
和void*
都大小相同(就像它们通常在32位系统上一样),代码可能会起作用 - 但即便这样也不能保证。例如,调用约定可能使用一个寄存器来返回int*
个结果,而使用另一个寄存器来返回指针结果。同样,大多数现有的C调用约定允许旧的错误代码做出这样的假设。
调用int
需要malloc
,即使某些编译器可能不会强制执行该要求。添加#include <stdlib.h>
(并放弃演员表)比花时间思考如果不这样做会发生什么更容易。
答案 1 :(得分:1)
如果在调用之前没有给出原型,任何函数几乎都会导致未定义的行为,例如,如果它的原型是int function(int x);
。
很明显,如果指针的大小大于int
的大小而malloc()
因隐式声明而返回int
,那么返回地址可能不是真实的地址,因为例如,可能无法用较少的位来表示它。
取消引用它将是未定义的行为,顺便说一下,你无法测试,因为它未定义,你会发生什么?它未定义!!!
那么,没有什么可以测试的吗?