我在运行下面的代码时,把指针和const弄得一团糟:
const
int main() { const int test = 10; int *ptr = &test; printf("%d\n", test); *ptr = 1; printf("%d\n", test); }
字符串在Mac上,结果是:
10 10
型在Linux机器上:
10 1
型为什么值的改变在Mac上不起作用?两台机器都用gcc编译代码。
1tuwyuhd1#
您可以在Godbolt的编译器资源管理器https://godbolt.org/z/5xfEqj9x1上运行测试从输出窗格中可以看出,行为与您的观察结果一致,以下是一些解释:
test
const int
int *
*ptr = 1
static
printf
10
1
执行具有未定义行为的代码意味着任何事情都可能发生,不同的输出是一个常见的例子,这是令人沮丧的,因为环境并不像分段错误那样明确地指出问题。* 任何事情都可能发生 * 不是字面上的意思,它意味着可能发生不可预测的副作用或缺乏副作用,包括看不到任何东西,读取不同的值,出现了一个分段错误......意料之外的事。为了尽量减少此类问题,请始终在编译时添加所有有用的警告并修复其原因(例如:-Wall -Wextra -Werror)。
-Wall -Wextra -Werror
nwsw7zdq2#
您忽略了来自编译器的诊断消息,并在Mac和Linux上使用不同的工具和/或不同的开关进行构建。int * ptr = &test;中的初始化违反了C 2018(C标准的2018版本)中的约束6.5.16.1 1,因为它将int *值分配给const int *类型的对象,而这些类型是不兼容的,并且不包含在允许的情况下。需要符合的编译器发出诊断消息。尽管违反了约束,编译器仍然可以接受代码,但是C标准没有定义这种行为。您的一个编译器将ptr视为指向可以更改和存储新值的对象,并打印更改后的值。另一个编译器观察到您的程序正在使用可以假定不会更改的const int,因此,在优化期间,它使用原始值而不是改变的值。
int * ptr = &test;
const int *
ptr
1这是一个初始化,初始化规则参考赋值规则。
2条答案
按热度按时间1tuwyuhd1#
您可以在Godbolt的编译器资源管理器https://godbolt.org/z/5xfEqj9x1上运行测试
从输出窗格中可以看出,行为与您的观察结果一致,以下是一些解释:
test
定义为一个const int
局部变量,但你通过将其地址存储到一个int *
指针并通过*ptr = 1
设置值来显式地绕过它。两个编译器都会发出关于这个可疑赋值的警告,代码*ptr = 1
具有未定义的行为,因为您正在修改const
变量。如果test
是在全局范围内定义的,(或使用static
限定符)这个赋值可能触发了一个分段错误。在这种情况下,变量的间接修改,如果执行的话,没有任何效果,因为编译器可能会假设test
变量没有改变。const
变量在两个printf
语句之间没有被修改,并将10
作为参数值传递gcc使用**-O2或更高的优化级别执行此操作,但读取-O 0**处的变量值,为Linux上的第二个printf
调用生成1
。执行具有未定义行为的代码意味着任何事情都可能发生,不同的输出是一个常见的例子,这是令人沮丧的,因为环境并不像分段错误那样明确地指出问题。* 任何事情都可能发生 * 不是字面上的意思,它意味着可能发生不可预测的副作用或缺乏副作用,包括看不到任何东西,读取不同的值,出现了一个分段错误......意料之外的事。
为了尽量减少此类问题,请始终在编译时添加所有有用的警告并修复其原因(例如:
-Wall -Wextra -Werror
)。nwsw7zdq2#
您忽略了来自编译器的诊断消息,并在Mac和Linux上使用不同的工具和/或不同的开关进行构建。
int * ptr = &test;
中的初始化违反了C 2018(C标准的2018版本)中的约束6.5.16.1 1,因为它将int *
值分配给const int *
类型的对象,而这些类型是不兼容的,并且不包含在允许的情况下。需要符合的编译器发出诊断消息。尽管违反了约束,编译器仍然可以接受代码,但是C标准没有定义这种行为。您的一个编译器将
ptr
视为指向可以更改和存储新值的对象,并打印更改后的值。另一个编译器观察到您的程序正在使用可以假定不会更改的const int
,因此,在优化期间,它使用原始值而不是改变的值。脚注
1这是一个初始化,初始化规则参考赋值规则。