来自this article。
将变量声明为
register
和const
的另一个用途是禁止该变量的任何非局部更改,即使通过获取其地址然后转换指针也是如此。即使您认为自己永远不会这样做,一旦将指针(即使使用const属性)传递给其他函数,您也永远无法确定这可能是恶意的并且更改变量在你的脚下。
我不明白我们如何通过指针修改const
变量的值。是不是未定义的行为?
const int a = 81;
int *p = (int *)&a;
*p = 42; /* not allowed */
答案 0 :(得分:11)
作者的观点是,声明具有register
存储类的变量会阻止您获取其地址,因此无法将其传递给可能通过丢弃const
来更改其值的函数。 / p>
void bad_func(const int *p) {
int *q = (int *) p; // casting away const
*q = 42; // potential undefined behaviour
}
void my_func() {
int i = 4;
const int j = 5;
register const int k = 6;
bad_func(&i); // ugly but allowed
bad_func(&j); // oops - undefined behaviour invoked
bad_func(&k); // constraint violation; diagnostic required
}
通过将潜在的UB更改为约束违规,需要进行诊断并且在编译时诊断出错误:
5.1.1.3诊断
1 - 如果是预处理翻译单元或翻译单元,符合要求的实施方案应至少产生一条诊断消息 包含违反任何语法规则或约束的行为,即使行为也是明确的 指定为未定义或实现定义。
6.5.3.2地址和间接运算符
约束1 - 一元
&
运算符的操作数应为[...]左值,指定一个对象[...] 未使用register
存储类说明符声明。
请注意,register
数组对象上的数组到指针衰减是未定义的行为,不需要诊断(6.3.2.1:3)。
另请注意,在C ++中允许使用register
左值的地址,其中register
只是一个优化提示(并且在此处不推荐使用)。 / p>
答案 1 :(得分:2)
我们可以修改
const
变量的值吗?
是的,您可以通过各种方式修改const
变量:指针hackery,强制转换等...
请阅读下一个Q !!
修改
const
变量的值是否有效?
没有!给你的是未定义的行为。
技术上,您的代码示例具有未定义行为
修改const
后,该程序不符合c标准,因此可能会给出任何结果。
请注意,未定义的行为并不意味着编译器需要将违规报告为诊断。在这种情况下,您的代码使用指针hackery来修改const
,并且不需要编译器来为其提供诊断。
C99标准3.4.3 说:
未定义的行为:行为,在使用不可移植或错误的程序构造或错误数据时,本国际标准不对其施加任何要求。
注意可能的未定义行为包括完全忽略具有不可预测结果的情况,在转换或程序执行期间以环境特征(有或没有发出诊断消息)的文档方式执行,终止转换或执行(发布诊断信息)。
答案 2 :(得分:2)
您的代码已编译,但它具有未定义的行为。
作者的观点是使用const
和 register
,以便代码不再编译:
const int a = 81;
int *p = (int *)&a; /* no compile error */
*p = 42; /* UB */
register const int b = 81;
int *q = (int *)&b; /* does not compile */
答案 3 :(得分:1)
代码片段确实会调用未定义的行为。
我不确定作者的观点是什么:为了不让外国代码"更改变量const
的值,以便调用... UB?这怎么可取?坦率地说,没有意义。
答案 4 :(得分:1)
我认为作者也在谈论这个案例,这是对const
的误解:
int a = 1;
int* const a_ptr = (int* const)&a; //cast not relevant
int function(int* const p){
int* malicious = (int*)p;
*malicious = 2;
}
变量本身不是常量,而是指针。恶意代码可以转换为常规指针并合法修改下面的变量。
答案 5 :(得分:0)
我不明白我们如何通过指针修改
const
变量的值。这不是未定义的行为吗?
是的,这是未定义的行为:
引用自C18,6.7.3 / 7:
“如果试图通过使用具有非const限定类型的左值来修改以const限定类型定义的对象,则该行为是不确定的。”
但是仅仅因为行为是不确定的,并不意味着您潜在地无法做到这一点。据我所能想到的,确实如此,在大多数情况下,编译器会在程序中包含任何未定义的行为,不会警告您-这是一个大问题。< / p>
幸运的是,在这种情况下,编译f.e。:
#include <stdio.h>
int main(){
const int a = 25;
int *p = &a;
*p = 26;
printf("a = %d",a);
}
编译器将发出警告:
初始化会从指针目标类型[-Wdiscarded-qualifiers(gcc)
中丢弃'const'限定符
或
警告:使用类型为'const int *'的表达式初始化'int *'会丢弃限定符[-Wincompatible-pointer-types-discards-qualifiers](clang)
但是,尽管代码中包含导致未定义行为的部分,并且您无法确定在任何执行中将打印出什么内容,但还是会编译该恶意程序(当然没有-Werror
选项)。
我们可以修改
const
变量的值吗?
是的,很不幸。人们实际上可以修改const
对象,但是您永远都不应这样做,无论是有意还是无意的。
使用register
关键字的方法可能很有效,因为标记为register
的变量的地址无法获取其地址-意味着您不能为指针分配相对变量的地址,也不能将其作为相应指针类型的参数传递给函数。