相同的 C 代码,在 Mac 和 Linux 上的输出不同

Luk*_*asz 0 c linux macos gcc undefined-behavior

我在运行下面的代码时弄乱了指针和const

int main()
{
    const int test = 10;
    int *ptr = &test;
    printf("%d\n", test);
    *ptr = 1;
    printf("%d\n", test);
}
Run Code Online (Sandbox Code Playgroud)

在 Mac 上,结果为:

10
10
Run Code Online (Sandbox Code Playgroud)

在linux机器上:

10
1
Run Code Online (Sandbox Code Playgroud)

为什么更改值在 Mac 上不起作用?两台机器都使用 gcc 编译代码。

Eri*_*hil 5

您忽略了来自编译器的诊断消息,并在 Mac 和 Linux 上使用不同的工具和/或不同的开关进行构建。

中的初始化int * ptr = &test;违反了 C 2018(C 标准的 2018 版本)6.5.16.1 1 中的约束,因为它为1int *类型的对象分配了一个值const int *,而这些类型不兼容并且不在允许的情况范围内。需要符合标准的编译器为此发出诊断消息。

尽管违反约束,编译器仍可以接受代码,但 C 标准未定义该行为。您的编译器之一将其视为ptr指向可以更改和存储新值的对象,并打印更改后的值。另一个编译器观察到您的程序正在使用const int可以假设不会更改的 a ,因此,在优化期间,它使用原始值而不是更改后的值。

脚注

1这是一个初始化,初始化的规则参考赋值的规则。


chq*_*lie 5

您可以在 Godbolt 的编译器资源管理器上运行测试:https://godbolt.org/z/5xfEqj9x1

从输出窗格中可以看出,该行为与您的观察结果一致,以下是一些解释:

  • 该代码具有未定义的行为:您定义testconst int局部变量,但您通过将其地址存储到指针int *并通过设置值来显式绕过它*ptr = 1。两个编译器都会发出有关此可疑赋值的警告,而您故意忽略了该警告。该代码*ptr = 1具有未定义的行为,因为您正在修改const变量。如果test已在全局范围内定义(或使用限定符static),则此分配可能会触发分段错误。在这种情况下,如果执行了变量的间接修改,则不会产生任何效果,因为编译器可能会假设变量test不会更改。

  • macOS 上的gcc默认是clang的别名,它生成与Linux 上的gcc不同的代码。即使没有优化,clang也会假设该const变量在两个语句之间没有被修改,并在两个调用中作为参数值(通过寄存器)printf传递。 gcc使用-O2或更高的优化级别执行此操作,但读取-O0处的变量值,为Linux 上的第二次调用生成。10esi1printf

执行具有未定义行为的代码意味着任何事情都可能发生,并且不同的输出是一个常见的例子,这是令人沮丧的,因为环境没有像分段错误那样明确地指出问题。 任何事情都可能发生,但不能从字面上理解,这意味着可能会发生不可预测的副作用或缺乏副作用,包括没有可见的东西、正在读取不同的值、发生分段错误……期待意外的情况。

为了尽量减少此类问题,请始终在编译时添加所有有用的警告并修复其原因(例如:)-Wall -Wextra -Werror