【问题标题】:Does "const" just mean read-only or something more?“const”只是意味着只读还是更多?
【发布时间】:2011-05-28 01:04:30
【问题描述】:

const 的真正含义是什么?只读对我来说似乎封装了它的含义,但是,我不确定我是对的。

如果只读和const 不同,谁能告诉我为什么?

引发这个问题的是this answer,他在其中声明const“只是”在C 中表示只读。我认为这是全部const 的意思,无论它是C 还是C++。他是什么意思?

对于 C 与 C++ 中 const 的具体差异的答案,我创建了一个新问题:How does "const" differ in C and C++? 根据 R.. 的建议。

【问题讨论】:

  • 您可以将 const 视为只读。你具体有什么不明白的?
  • @Falmarri:在问题中添加了详细信息。
  • 你真的应该为 C 和 C++ 分别提出两个问题。它们是不同的语言,C 的这个问题的答案与 C++ 的答案大不相同。
  • 请明确这是C 还是C++ 问题。你使用了这两个标签...

标签: c++ c constants


【解决方案1】:

通过将变量声明为const,您表明编译器您无意修改该变量。但这并不意味着其他人没有!这只是为了允许一些优化并通过编译错误得到通知(注意,它主要是编译错误,而const == ReadOnly 表示运行时错误)。

const不代表只读,因为你可以写const volatile,那意味着它可以随时自行改变,但我无意修改它。

编辑:这是一个经典示例:考虑我正在编写从内存映射端口读取当前时间的代码。考虑 RTC 映射到内存 DWORD 0x1234。

const volatile DWORD* now = (DWORD*)0x1234;

它是const,因为它是一个只读端口,它是volatile,因为我每次阅读它都会改变。

还请注意,许多架构有效地将声明为 const 的全局变量设为只读,因为修改它们是 UB。在这些情况下,UB 将表现为运行时错误。在其他情况下,这将是一个真正的 UB :)

这是一个很好的阅读:http://publications.gbdirect.co.uk/c_book/chapter8/const_and_volatile.html

【讨论】:

  • @ruslik:“自己”?你能举个例子吗?
  • @Kim:该变量可以映射到某种形式的硬件设备。或者可能是线程 A 具有 const volatile,并且它是从线程 B 更改的,或者......任何数量的类似的东西。
  • 内存中的一个位置,只能被 CPU 之外的某些硬件修改。假设您有一个传感器映射到内存中的某个位置,并且您想读取它报告的值。
  • @ruslik, @Billy, @Matt: ruslik 指出的例子中如果变量只标记为 const 会发生什么?编译器不知道那个位置有什么,所以它不会被优化掉或任何类似的东西,除了使代码对读者更“正确”之外,const/const volatile 之间有什么区别?即,如果它只是被标记为 const,它对代码本身有什么影响吗? const 只保证代码本身不会修改该位置(没有显式转换),而不是任何硬件都不会,对吧?
  • 按照我的英文,“只读”是指只能读,但不一定不会改变。
【解决方案2】:
const char * hello_1{ "Hello!" };
const char   hello_2[]{ "Hello!" };
char       * ptr{};

// take away the const-nes
// ptr = (char *)hello_1;
// *ptr = '*'; <-- write access violation
// hello_1 is in a read only memory

// take away the const-nes
ptr = (char *)hello_2;
*ptr = '*'; // <-- OK
// hello_2 is modifiable

指针指向内存,char *指向只读数据段中的内存。 char *char [] 之间的区别在于,虽然两者在数据段上的声明方式相同,但 char [] 被视为可读,因为如果使用它就会被压入堆栈。

【讨论】:

  • hello_2 可能在堆栈上,或者应用 as-if-rule,它可能不在。无论哪种情况,UB 都会保留。
  • @Deduplicator,请给我们看看 hello_2 不在堆栈中的代码?
  • 您确实应该区分“这就是我现在使用这些选项在我的机器上发生的事情”和“这就是保证会发生的事情”。前者是最糟糕的牛仔编码,它暗示着所有的脆弱性,后者是界面编码,是获得可靠软件的唯一途径。
  • @Deduplicator。好的,这是可以理解的。我们现在可以看看你的评论是什么意思吗?我们不是专家。
【解决方案3】:

两个字节对字节相同(cmets 除外)最小案例示例......

首先在 C 中,gcc 会发出警告...

/* 函数获取指向数组的指针 两个只读整数。*/ void a( const int (* parray)[2]); 无效b(无效) { 整数数组[2] = {1,2}; 常量 int crray[2] = {1,2}; /* C 保留将其存储在只读位置的权利。*/ 一个(&数组); /* 警告:从不兼容的指针类型传递‘a’的参数 1*/ a( &cray); /* 好的!*/ }

现在在 C++ 中也是如此……g++ 对此非常满意。

// 函数获取指向数组的指针 // 它承诺不会修改的两个整数。 // (除非我们抛弃它的 constness ;-P) void a( const int (* parray)[2]); 无效b(无效) { 整数数组[2] = {1,2}; 常量 int crray[2] = {1,2}; 一个(&数组); // C++ 对此没有问题。 a( &cray); // 好的! }

【讨论】:

  • 这个例子实际上并没有显示你认为它显示的内容,它只是 C 语言规范中的一个疏忽。 See here获取相关答案
【解决方案4】:

Const 意味着指针或引用不能用于写入或读取-修改-写入操作,除非丢弃 const。这并不意味着 C++ 标准试图声称它意味着什么(C++ 标准在这方面是错误的)。

这样定义的变量:

 /* auto */ int const x = 1;

显然不是只读的,否则无法初始化。相反,变量 x 的类型是“对 int 的引用 const”(而不是对 const int 的引用)或 int 的左值 const。请注意,“const”与指针或引用相关联,它与存储无关,也与驻留在该存储中的值的类型无关。

这是相当不幸的,因为 const 提供的合约非常弱,特别是无法缓存指向或引用的内存位置,正是因为它并不意味着不可变的存储。

底线是:const 是与符号引用或指针相关联的访问修饰符,程序员使用它来允许符号提供者在符号客户端上建立义务,或者为符号客户端向符号提供者承诺它不会通过此符号修改存储(例如,接受指向 int 的指针 const 的函数承诺不修改指向的 int)。

这与变量无关:

int const *p = (int*)malloc(sizeof(int));

显然与存储无关(malloc 的存储始终是可写的)。

相反,您应该将 const 视为在程序各部分之间传达不变量、义务或要求的一种方式,由程序员为程序员目的而设置,并由类型系统传播。不幸的是,类型系统不健全,无法正确传播 constness:

X *last;
struct X { int a; X() : a(0) { last=this; } };
X const x; // x is const?
last->a = 1; //really ??

恕我直言,编译器必须使存储不可变的唯一机会是用于实际常量,例如字符串文字(可能)或静态(全局)存储。在实践中,自动、堆和临时存储不能设为只读。

【讨论】:

    【解决方案5】:

    很多人告诉你const 意味着你不能修改它。那是显然是错误的const 可以轻易丢弃。注意这个sn-p:

    void foo(const int *somevalue)
    {
       int *p = (int*) somevalue;
       *p = 256;  // OMG I AM EVIL!!!!11
    }
    

    您的编译器不会阻止您这样做。那么,const 的目的是什么?我将其称为更多建议。当你查看你的函数所期望的合约的函数原型时,它会提醒你。如果你不小心破坏了它,你的编译器会对你大喊大叫。 (但如果你故意破坏它,就像上面的演员表一样。)

    在某些情况下,该标准有意破坏const。请注意strstr 的返回值,例如:根据定义,它将向您提供的const 缓冲区返回一些偏移量...但返回值不是const。为什么?好吧,在非const 缓冲区上使用strstr 的返回值会有意义地中断。

    【讨论】:

    • 如果您实际传递指针的对象被声明为const,则此代码具有未定义的行为。只有当最初指向的对象不是const 时,才能通过删除const 限定的指针进行写入。这同样适用于其他限定符,如 volatile
    • @R.. - 实际上,我打算写这样的东西,但认为描述起来可能会分散注意力......一个常见的例子是:如果你直接声明 const,你的编译器可能会将其放在只读数据部分中,您可能会崩溃写入它。
    【解决方案6】:

    C++ 允许定义 const 成员函数。 const 成员函数是唯一在 const 对象上调用的函数。此外,const 成员函数不能修改类的任何数据成员(除非数据成员标记为可变)。

    class Foo
    {
        int data;
    
        void Bar();
        void ConstBar() const;
    };
    
    void Foo::ConstBar() const
    {
        // Error! cannot modify i as ConstBar is a const member function.
        // i = 0;
    }
    
    // Usage:
    const Foo foo_instance;
    
    // Error! cannot call non-const member on a const object.
    // foo_instance.Bar();
    
    // OK
    foo_instance.ConstBar();
    

    【讨论】:

    • 没有意识到您正在寻找一个非常具体的答案。你问题的措辞有点让我失望。
    • 没有 const 对象,也没有 const 类型(尽管标准中的描述不正确)。您的代码是正确的:但它不是 const 的对象,而是指向该对象的引用或指针。顺便说一句:在示例中,您的意思是“数据”而不是“我”。并注意: ((Foo*)this)->data = 1;甚至在 ConstBar 内修改数据 :)
    【解决方案7】:

    编译器不允许修改声明为const 的内容。就像你说的那样。

    它主要用于函数原型中,以告知用户函数在传递指针时不会触及 this 或 that。它还可以为您自己提供故障保护。

    【讨论】:

    • +1 用于“为您自己提供故障保护”。如果您知道不应该更改某些内容,请将其声明为 const 并避免以后出现潜在错误。
    猜你喜欢
    • 2019-02-21
    • 1970-01-01
    • 2015-08-19
    • 2015-01-19
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    • 2012-12-17
    相关资源
    最近更新 更多