为啥这段代码在 64 位架构上会出现段错误,但在 32 位上却能正常工作?
Posted
技术标签:
【中文标题】为啥这段代码在 64 位架构上会出现段错误,但在 32 位上却能正常工作?【英文标题】:Why does this code segfault on 64-bit architecture but work fine on 32-bit?为什么这段代码在 64 位架构上会出现段错误,但在 32 位上却能正常工作? 【发布时间】:2011-11-24 14:42:49 【问题描述】:我遇到了以下 C 难题:
问:为什么以下程序在 IA-64 上会出现段错误,但在 IA-32 上可以正常工作?
int main()
int* p;
p = (int*)malloc(sizeof(int));
*p = 10;
return 0;
我知道int
在 64 位机器上的大小可能与指针的大小不同(int
可能是 32 位,指针可能是 64 位)。但我不确定这与上述程序有何关系。
有什么想法吗?
【问题讨论】:
不包括stdlib.h
这样的傻事吗?
这段代码在我的 64 位机器上运行良好。如果您 #include stdlib.h
(对于 malloc),它甚至可以在没有警告的情况下编译
天啊! @user786653 指出了重要的一点。使用#include <stdlib.h>
,可以完美找到,但这不是问题所在。
@delnan - 它不必像那样工作,它可能在sizeof(int) == sizeof(int*)
的平台上合法地失败,例如,如果指针通过不同的寄存器返回到int
s in使用的调用约定。
在 C99 环境中,编译器应该至少给你一个关于 malloc()
隐式声明的警告。 GCC 说:warning: incompatible implicit declaration of built-in function 'malloc'
也是。
【参考方案1】:
很可能是因为您不包括 malloc
的头文件,虽然编译器通常会警告您这一点,但您显式转换返回值的事实意味着您'告诉它你知道你在做什么。
这意味着编译器期望从malloc
返回一个int
,然后将其转换为一个指针。如果它们的大小不同,那会让你感到悲伤。
这就是为什么您从不在 C 中强制转换 malloc
返回。它返回的 void*
将被隐式转换为正确类型的指针(除非您没有包含header 在这种情况下,它可能会警告您潜在的不安全的 int 到指针的转换)。
【讨论】:
抱歉听起来很幼稚,但我一直认为 malloc 返回一个可以转换为适当类型的 void 指针。我不是 C 程序员,因此希望了解更多细节。 @user7: 如果没有#includevoid *
可以隐式转换为任何其他指针类型。如果适当的原型在范围内,int *p = malloc(sizeof(int))
有效,如果不在范围内则失败(因为假设结果为 int
)。使用强制转换,两者都可以编译,而后者在sizeof(int) != sizeof(void *)
时会导致错误。
@user7 但是如果你不包含stdlib.h
,编译器不知道malloc
,也不知道它的返回类型。所以它只是假设int
作为默认值。【参考方案2】:
转换为int*
掩盖了这样一个事实,即如果没有正确的#include
,malloc
的返回类型被假定为int
。 IA-64 恰好有sizeof(int) < sizeof(int*)
,这使得这个问题很明显。
(还要注意,由于未定义的行为,即使在sizeof(int)==sizeof(int*)
成立的平台上,它仍然可能失败,例如,如果调用约定使用不同的寄存器来返回指针而不是整数)
comp.lang.c FAQ 有一个讨论 why casting the return from malloc
is never needed and potentially bad 的条目。
【讨论】:
没有正确的#include,为什么malloc的返回类型被假定为int? @WTP - 这是在 C++ 中始终使用new
并始终使用 C 编译器而不是 C++ 编译器编译 C 的好理由。
@user7 - 这就是规则。如果未知,则任何返回类型都假定为int
@vlad - 更好的办法是总是声明函数,而不是完全出于这个原因依赖隐式声明。 (而不是从malloc
投回)
@user7:“我们有一个指针 p(大小为 64),它指向 32 位内存” - 错误。 malloc 分配的块的地址是根据void*
的调用约定返回的。但是调用代码认为函数返回int
(因为你选择不告诉它),所以它尝试根据int
的调用约定读取返回值。因此p
不一定 一定指向分配的内存。它恰好适用于 IA32,因为 int
和 void*
的大小相同,并且以相同的方式返回。在 IA64 上,您会得到错误的值。【参考方案3】:
这就是为什么您在编译时永远不会没有关于缺少原型的警告。
这就是为什么你从不在 C 中强制执行 malloc 返回的原因。
C++ 兼容性需要强制转换。几乎没有理由(阅读:这里没有理由)省略它。
C++ 兼容性并非总是需要,在少数情况下根本不可能,但在大多数情况下很容易实现。
【讨论】:
为什么我会关心我的 C 代码是否与 C++“兼容”?我不在乎它是否与 perl 或 java 或 Eiffel 或 ... 兼容 如果你保证下线的人不会看你的 C 代码然后走,嘿,我会用 C++ 编译器编译它,因为那应该可以工作! 这是因为大多数 C 代码可以简单地与 C++ 兼容。以上是关于为啥这段代码在 64 位架构上会出现段错误,但在 32 位上却能正常工作?的主要内容,如果未能解决你的问题,请参考以下文章
为啥这个基本链表可以在 MacOS 上工作,但在 Linux 上会出现段错误