来自本文。
将变量声明为
register
和const
的另一个用途是禁止该变量的任何非本地更改,即使是通过获取其地址然后投射指针。即使您认为自己永远不会这样做,一旦您将指针(即使带有const属性)传递给其他函数,您也永远无法确定这可能是恶意的,并更改您脚下的变量。
我不明白如何通过指针来修改const
变量的值。这不是不明确的行为吗?
const int a = 81;
int *p = (int *)&a;
*p = 42; /* not allowed */
作者的观点是,用register
存储类声明变量会阻止您获取其地址,因此不能将其传递给可能通过丢弃const
来更改其值的函数。
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更改为违反约束,需要进行诊断,并在编译时诊断错误:
c11
5.1.1.3诊断
1-如果预处理翻译单元或翻译单元包含违反任何语法规则或约束的行为,即使该行为也是显式的指定为未定义或定义了实现。
6.5.3.2地址和间接运算符
限制1-一元
&
运算符的操作数应为[…]一个左值,用于指定[…]为未使用register
存储类说明符声明。
请注意,register
阵列对象上的阵列到指针衰减是不需要诊断的未定义行为(6.3.2.1:3)
还要注意的是,在C++中允许使用register
左值的地址,其中register
只是一个优化器提示(并且是一个不推荐使用的提示)。
我们可以修改
const
变量的值吗
是的,您可以通过各种方式修改const
变量:指针技巧、强制转换等。
请阅读下一个Q
修改
const
变量的值是否为有效代码
不!这会给您带来未定义的行为。
从技术上讲,您的代码示例具有未定义的行为
一旦修改const
,程序就不符合c标准,因此可能会给出任何结果。
请注意,未定义的行为并不意味着编译器需要将冲突报告为诊断。在这种情况下,您的代码使用指针技巧来修改const
,并且不需要编译器来为其提供诊断
C99标准3.4.3规定:
未定义的行为:使用不可移植或错误的程序结构或错误的数据时的行为,本国际标准对此没有任何要求。
注:可能的未定义行为包括完全忽略具有不可预测结果的情况,到在翻译或程序执行过程中以环境特征的记录方式行事(有或没有发布诊断消息),再到终止翻译或执行(有诊断消息发布)。
您的代码可以编译,但它有未定义的行为。
作者的观点是使用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 */
代码片段确实调用了未定义的行为。
我真的不确定作者的观点是什么:为了不让"外来代码"改变变量的值,你把它设为const
,这样。。。是否调用UB?这有什么可取之处?坦率地说,这没有道理。
我想作者也在谈论这个案例,这是对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;
}
变量本身不是常量,但指针是常量。恶意代码可以转换为常规指针并合法修改下面的变量。
我不明白如何通过指针来修改
const
变量的值。这不是不明确的行为吗?
是的,这是未定义的行为:
引用C18,6.7.3/7:
"如果试图通过使用具有非常量限定类型的左值来修改具有常量限定类型定义的对象,则行为未定义。"
但仅仅因为行为是未定义的,并不意味着你可能不能这样做。就我所能想到的,事实确实如此,大多数时候,编译器会在程序中包含任何未定义的行为时,而不是警告您,这是一个大问题。
幸运的是,在这种情况下,当编译f.e.:时
#include <stdio.h>
int main(){
const int a = 25;
int *p = &a;
*p = 26;
printf("a = %d",a);
}
编译器将抛出警告:
初始化从指针目标类型中丢弃"const"限定符[-Wdiscarded限定符](gcc)
或
警告:使用类型为"const int*"的表达式初始化"int*"将丢弃限定符[-Wincompatible指针类型将丢弃限定词](clang)
但是,尽管代码中包含导致未定义行为的部分,而且您永远无法确定它在任何执行中会打印出什么,但您还是编译了那个恶意程序(当然没有-Werror
选项)。
我们可以修改
const
变量的值吗?
所以,是的——不幸的是。实际上可以修改const
对象,但您永远不应该这样做,无论是有意还是无意。
使用register
关键字的方法可能很有效,因为标记为register
的变量的地址不能取其地址——这意味着你不能用相对变量的地址分配指针,也不能将其作为相应指针类型的参数传递给函数。