【问题标题】:how const keyword works in cconst 关键字在 c 中的工作原理
【发布时间】:2010-05-11 17:15:59
【问题描述】:

我想了解 c 和 c++ 中的 const 内部结构。编译器如何强加恒定性? 谁能帮帮我。

【问题讨论】:

  • 您是对 C 还是 C++ 特别感兴趣,因为可能存在差异。例如,在 C++ 中将方法标记为 const 在 C 中没有等效结构。
  • 有人可以帮我编译器如何创建 const。我只想知道内部原理,而不是 const 在 C 或 C++ 中的工作原理
  • (你甚至可以得到积分)

标签: c++ c


【解决方案1】:

一般const 是 100% 编译器。当您声明 const 时,编译器会限制它允许您编写的内容。它不会让您分配给 const 标量,通过 const 引用或指针分配,或调用 const 对象的非常量函数。

无法保证编译器会安排任何类型的运行时保护。

【讨论】:

  • 优秀的答案。对于任何有兴趣进一步阅读的人,我建议 C++ FQA 的关于 const 正确性的部分:yosefk.com/c++fqa/const.html
  • @Eli 到底怎么有人投票支持 FQA 链接... 更好地阅读关于 const-correctnes 的常见问题解答:parashift.com/c++-faq-lite/const-correctness.html。更少的咆哮,更多的实质
  • @Johannes:我实际上不同意 FQA 关于 C++ 的基本结论,并且我很乐意在日常工作中使用 C++,但我觉得 FQA 可以是富有洞察力和发人深省的,阅读它会有所帮助我理解我在编写 C++ 时遇到的一些问题比常见问题解答要好得多。此外,FQA 直接链接到每个问题中的常见问题解答,因此很容易看到原始答案。我可能应该添加一个免责声明,即 FQA 可能不是新手程序员的好资源,他们绝对应该坚持使用 FAQ。
【解决方案2】:

C和C++中const关键字有两种不同的语义含义。

(1) 可以声明一个对象

const SomeType t;

在上述情况下,对象t 是不可修改的对象。编译器会通过遵守 const-correctness 规则(在 C 和 C++ 中不同)来尽力防止您对其进行修改。 const 正确性规则仅在概念上在语言级别强制执行,这意味着有办法绕过这些规则,这也意味着对象的 constness 不一定会在物理级别实现。 IE。无法保证对象最终会被放入只读内存中。

请务必注意,这种 const 是不可移除的,因为任何通过丢弃 const 来修改上述对象的尝试都会导致未定义的行为(不包括可能的 mutable 成员在 C++ 中)。

(2)它可以声明一个对象的访问路径的常量

const SomeType *p;

上面的p 被声明为一个指向常量的指针。这并不一定意味着p 指向的对象是一个常量对象(由上面的第一种const 定义)。它可能很容易成为一个非常数,在这种情况下,完全合法从上述访问路径中丢弃常量并修改对象,尽管这通常不是一个好的编程习惯。换句话说,访问路径的常量性是潜在的可移除

考虑到上述情况,以下声明

const int* const* const* const p = 0;

包括两种不同的const:最后一个const声明对象p(第一种)的常量,而const的其余部分声明不同级别访问路径的常量,由p(第二种)。

附:作为一个[可能不相关的]旁注,可能值得注意的是,constant 一词在 C 和 C++ 中的含义截然不同。在 C++ 中,constants 是声明为 const 的对象。在 C 中 constants 是文字。声明为 const 的对象不是 C 术语中的常量

【讨论】:

【解决方案3】:

除了使用您的问题的其他答案已经提到的const 关键字提供的编译时强制不变性之外,使用它有时允许编译器将此类数据置于只读 em> 一段二进制和内存。根据 Ulrich Drepper 的文章 How To Write Shared Libraries 中的第 2.4.2 节“永远 const”,这样做可能允许程序 (1) 使用更少的资源并 (2) 更快地启动。

请注意,像往常一样,丢弃此类只读内存区域中的数据 constness 会导致未定义的行为。

【讨论】:

    【解决方案4】:

    当编译器编译代码时,它会计算每个表达式的类型,以便对其进行类型检查并正确发出代码(例如,当您尝试将 int 存储在指针中或将整数正确转换为双)。 const-ness 可以被认为是类型的一部分。由于它有表达式的类型信息,它可以检查 lvalue 的类型(赋值的左侧),如果它的类型中有 'const' 则抛出错误。

    【讨论】:

      【解决方案5】:

      在 C 中,const 关键字将使变量不可变,这意味着它不能被修改。

      通常,这是编译时的区别,对变量的运行时修改没有影响。例如,可以使用指向同一内存地址的指针间接修改 const 变量。

      在 C++ 中,const 关键字具有多个含义。

      例如,类成员函数可以是“const”,表示不允许修改类实例的状态。

      与在 C 中一样,声明为 const 的变量也可以使用指针间接修改,也可以使用“可变”关键字或 const_cast 运算符。

      【讨论】:

        【解决方案6】:

        这在优化编译器中实际上是一件非常复杂的事情。不过,它一开始很简单。

        当您使用 const 关键字声明变量时(让我们忽略指针,因为它们可以是 const 或指向 const 或两者兼而有之),编译器会记住没有代码应该更改该变量(几乎)。如果编译器看到更改 const 变量的代码,那么它会认为这是一个错误(或者偶尔只值得警告,但为了简单起见,我将忽略它)。编译器还假设它看不到的任何代码(现在无论如何{其他 .c 文件或可能的库或 .s 或 .asm 文件中的代码)将更改 const 变量(除非它是 const volatile 在这种情况下它将假设它可以随时更改,但仍会强制不让您的代码更改它 - 这对于用于读取设备状态但无法写入的内存映射 SFR [特殊功能寄存器] 很有用。请记住,c 和 c++ 用于操作系统和嵌入式编程)。

        变量在某些或所有情况下都不会改变的假设允许编译器的优化例程做一些原本不能做的事情。这意味着将变量的字面值放入指令流中,而不是加载变量的地址然后加载变量的值。它还可以假设如果它加载了 if:

        extern const int foo; // note that the value isn't visible, so a load is necessary
        ...
        extern int baz(int, int);
        ...
        int bar(int x, int y) {
           int m, n;
           int r = x / foo; // this would require loading the value of foo from RAM
           m = baz(r, y); // the compiler normally has to assume that a function could change a global
           n = m + r + foo; // but since the global foo is const it shouldn't be able to be changed
                            // by the call to baz and does not need to be reloaded
           return n;
        }
        

        一个可执行文件(或其他目标文件)可能有一个节(.rodata),其中只有常量。在许多情况下,操作系统可能会强制不允许程序写入此数据(或者在某些情况下甚至可能在 ROM 中)。本节还可能包含用于初始化的非常量变量的版本,因为它可能包含任何常量,而不仅仅是声明为 const 的常量。

        所以在 C 中 const 主要只是告诉编译器告诉你你搞砸了并且试图改变不应该改变的东西。但是,允许基于它做出一些假设。

        在 C++ 中,它变得更加复杂。我不记得所有细节,但我记得你可以根据传递给它的值是否为 const 来重载函数名,这很有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-05
          • 1970-01-01
          • 1970-01-01
          • 2010-11-27
          • 1970-01-01
          • 2012-09-02
          相关资源
          最近更新 更多