我只花了半天的时间在 x86-64 代码中计算出一个微妙的崩溃,所以这是对其他人的提醒——我还没有在其他地方看到过这种情况。
如果您在没有正确声明的情况下使用 libc 函数,gcc将假定它返回 int。例如setlocale()被假定为int setlocale(),在 EAX 中返回一个 32 位的 int 值。
尝试通过隐式或显式转换将此返回转换为指针,将强制通过符号扩展从 32 位转换为 64 位,即使被调用函数在 RAX 中返回了有效的 64 位指针值!例如
char *p = setlocale(0, 0); // bear with me for a second
被编译为
1c: b8 00 00 00 00 mov $0x0,%eax
21: e8 00 00 00 00 callq 26 <hard_locale+0x26>
26: 48 98 cltq ; <--- eax is expanded in rax
GCC 甚至试图告诉你:
warning: initialization makes pointer from integer without a cast
如果您添加显式强制转换,警告将更改为,这表明问题:
warning: cast to pointer from integer of different size
如果幸运的话,什么都不会发生,但如果它恰好为内存中的向上指针返回一个大值,它就会搞砸了,如下所示:
function returns in RAX: 0x07ffff7b9705e
cltq considers EAX with negative sign: 0xf7b9705e
now RAX is: 0xfffffffff7b9705e
并且您的指针无效。
修复和解决方案:
始终使用正确的函数声明
-Wall -Werror在 x86-64 编译器中应该是默认的。