常见谎言?(因为const可以被抛弃)

gli*_*ite 26 c c++ const

可能重复:
卖给我关于const的正确性

关键字的用处是什么const,C或者C++因为它允许这样的事情?

void const_is_a_lie(const int* n)
{ 
    *((int*) n) = 0;
}

int main()
{
    int n = 1;
    const_is_a_lie(&n);
    printf("%d", n);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:0

很明显,const不能保证论证的不可修改性.

Ben*_*igt 44

const 是你对编译器的承诺,而不是它保证你的东西.

例如,

void const_is_a_lie(const int* n)
{ 
    *((int*) n) = 0;
}

#include <stdio.h>
int main()
{
    const int n = 1;
    const_is_a_lie(&n);
    printf("%d", n);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

http://ideone.com/Ejogb上显示的输出是

1

因为const,编译器被允许假设该值不会改变,因此它可以跳过重读它,如果这会使程序更快.

在这种情况下,由于const_is_a_lie()违反了合同,会发生奇怪的事情.不要违反合同.并且很高兴编译器能帮助您保持合同.演员是邪恶的.

  • 在对非const对象的引用上抛弃const不是UB,所以我不确定这是否真的如此. (5认同)
  • @DeadMG事实上,抛弃常量绝不是UB.它只是修改*一个const对象的UB. (5认同)
  • @ChrisDodd:不,我认为不可能.转义为`const`-qualified的引用不相关的事实 - 对非const对象的引用转义,这就是最重要的.您是否在标准中有参考来备份您的视图? (3认同)
  • @fbafelipe:`const`通常作为一种文档形式有用......但不是你提供的例子.如果一个对象是由原始指针返回的,那么`const`是不是,调用者不应该删除它.在现代C++风格中,如果正在转移所有权,则对象将作为`std :: unique_ptr`返回并带有删除器. (3认同)

cdh*_*wie 10

在这种情况下,n是一个指向常量的指针int.当您将其强制转换为int*删除const限定符时,允许该操作.

如果您告诉编译器删除const限定符,它将很乐意这样做.如果你让它完成它的工作,编译器将帮助确保你的代码是正确的.通过抛弃常量,您告诉编译器您知道目标n是非常量的,并且您确实想要更改它.

如果你的指针指向的东西实际上是const在第一个地方声明的,那么你通过尝试改变它来调用未定义的行为,并且任何事情都可能发生.它可能会奏效.写操作可能不可见.该计划可能会崩溃.你的显示器可以打你.(好吧,可能不是最后一个.)

void const_is_a_lie(const char * c) {
    *((char *)c) = '5';
}

int main() {
    const char * text = "12345";
    const_is_a_lie(text);
    printf("%s\n", text);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

根据您的特定环境,可能存在段错误(也称为访问冲突),const_is_a_lie因为编译器/运行时可能会将字符串文字值存储在不可写的内存页中.

标准有关于修改const对象的说法.

7.1.6.1/4 cv-qualifiers [dcl.type.cv]

除了可以修改声明为mutable(7.1.1)的任何类成员之外,任何在其生命周期内修改const对象的尝试(3.8)都会导致未定义的行为

"医生,我这样做会很疼!" "所以不要这样做."


Ton*_*roy 6

您的...

int n = 1;
Run Code Online (Sandbox Code Playgroud)

...确保n存在于读/写内存中; 它是一个非const变量,因此稍后修改它的尝试将具有已定义的行为.给定这样一个变量,你可以混合使用const和/或非const指针并引用它 - 每个的常量只是程序员防止代码"分支"意外更改的一种方式.我说"分支"是因为您可以将给定的访问可视n化为树,其中 - 一旦标记了分支const,所有子分支(进一步指示/引用n是否有其他局部变量,函数参数等)将会需要留下来const,除非你明确表达了对constness的概念.const对于像你一样可变的变量,抛弃是安全的(如果可能令人困惑)n,因为它们最终仍然写回到可修改/可变/非的内存地址const.您可以想象在这些情况下可能会遇到麻烦的所有奇怪的优化和缓存都不允许,因为标准要求并保证在我刚刚描述的情况下的理智行为.

遗憾的是,它也可能抛弃真正固有const变量的常量,比如说const int o = 1;,任何修改它们的尝试都会有不确定的行为.这有很多实际的原因,包括编译器将它们放在内存中然后标记为只读(例如,参见UNIX mprotect(2))的权利,这样尝试写入将导致CPU陷阱/中断,或者只要原始设置读取变量需要值(即使在代码中从未使用该值提及变量的标识符),或者使用原始值的内联编译时副本 - 忽略对变量本身的任何运行时更改.因此,标准使行为未定义.即使它们碰巧按照您的意图进行了修改,程序的其余部分也将具有未定义的行为.

但是,这不应该是令人惊讶的.类型的情况也是如此 - 如果你有......

double d = 1;
*(int*)&d = my_int;
d += 1;
Run Code Online (Sandbox Code Playgroud)

...你有没有向编译器撒谎的类型d?最终d占用可能在硬件级别上无类型的内存,因此编译器所拥有的只是对它的一种观点,进而改变位模式.但是,根据my_int硬件的值和双重表示,您可能创建了一个无效的位组合,d其中不代表任何有效的double值,以便后续尝试将内存读回CPU寄存器和/或者做一些具有未定义行为的事情,d例如+= 1,可能会生成CPU陷阱/中断.

这不是C或C++中的错误......它们旨在让您对硬件提出可疑请求,这样如果您知道自己在做什么,就可以做一些奇怪但有用的事情,很少需要依赖用于编写低级代码的汇编语言,甚至用于设备驱动程序和操作系统.

但是,正是因为在C++中引入了更明确和有针对性的强制转换符号,强制转换可能是不安全的.不可否认风险 - 你只需要了解你所要求的东西,为什么它有时候是好的而不是其他的,并且与它一起生活.