21

还是有其他防止修改它们的保护措施?

如果它们在只读存储器中是有道理的——这就是制作它们的原因const,对吧?

4

9 回答 9

18

const是一个编译时构造,在运行时不知道。它只是为了帮助程序员对他的程序进行推理,并防止通过更改不应该更改的内容而引入错误。 const告诉编译器您不想允许更改此变量,因此编译器将强制执行它。

于 2013-08-05T12:23:36.480 回答
12

不,他们不是必须的。const是编译时间,并允许编译器执行某种优化。但是,将变量放入只读存储器位置并不是强制性的。

请参阅此示例,它是未定义的行为(感谢 Dyp 指出):

#include <iostream>

int     main()
{
  const int bla = 42;
  int       *ptr = const_cast<int *>(&bla);

  std::cout << *ptr << std::endl;
  *ptr = 21;
  std::cout << *ptr << std::endl;
}

它将输出42and 21,但它也可能崩溃。

现在看到这个:

#include <iostream>

int     main()
{
  const int bla = 42;
  int       *ptr = const_cast<int *>(&bla);

  std::cout << bla << std::endl;
  *ptr = 21;
  std::cout << bla << std::endl;
}

在我的编译器上,这个输出4242,因为编译器做了一些优化。请注意,它仍然可能因为*ptr = 21;

于 2013-08-05T12:27:13.147 回答
6

在很多情况下,编译器不可能将 aconst变为只读内存(假设系统中首先存在只读内存)。事实上,我相信几乎所有的编译器,作为一项规则,使const对象存在于常规数据(或堆栈)内存中,就像普通变量一样。

的主要目的const是向编译器声明您不想也不应该更改某些值的意图。我看不出为什么编译器不能在有限的情况下将const变量放入只读内存中。但我也不会依赖这个——标准当然使这成为可能,因为它提到从最初标记的对象中使用const_castremove ,然后写入它是未定义的行为(因此,它不需要编译器允许在使用删除原始值后修改值- 因此确实允许“崩溃,因为我们试图写入只读内存”)。constconstconst_castconst

但是考虑一下:

class X
{
   int x;
  public:
   X(int v) : x(v) {}
}

int c = rand();

const X a(c+1);
const X b(c+2); 

在这种情况下,编译器无法知道c它从中获得的值rand,因此它无法在编译时初始化a和。b

于 2013-08-05T12:50:01.667 回答
4

const关键字有两种用途:

  1. 众所周知的“我保证不会修改这个对象的状态”
  2. 作为一个特殊规则,只要对象没有成员(即整个对象就是对象的状态) ,编译器就可以将定义const在只读内存中的对象放置。mutable

这就是为什么const_cast允许 ist 去掉 const 限定符的原因,但要求是对象定义本身不是const. 关于 const 正确性的 Wikipedia指出:“但是,根据 ISO C++ 标准,任何修改本身声明为 const 的对象的尝试都会导致未定义的行为 const_cast 。”。

于 2013-08-05T13:19:13.753 回答
3

不仅 C++ 标准不保证const对象在只读内存中,实现完全实现也很困难,如下所述。

C++ 实现很难将const具有自动存储持续时间的对象放置在只读内存中。这是因为每个不同的对象必须有不同的地址(因为它们的指针必须比较不相等)。因此,每次执行它所在的块时,都必须创建每个这样的对象。

如果一个const不获取对象的地址,编译器可以仅使用内存中的单个实例来实现它的多个实例(在 C++ 计算模型中)(因为每个实例都是相同的并且不会改变)。因此,这样的对象可以在程序启动时创建一次(可能通过从程序文件的常量数据部分加载),标记为只读,并在程序执行期间保持不变。然而,如果一个对象的地址被获取(并以可观察的方式使用),那么编译器必须创建该对象的每个单独实例(或以某种方式“假装”一个实例具有多个地址)。通常,编译器无法预测一个块可能同时存在多少次执行(例如,当存在递归函数调用时)。因此,它无法在程序启动时创建所需的对象的所有实例。

创建对象需要修改内存,写入对象的初始值。因此,将const具有自动存储持续时间的对象放入只读内存中需要经常将内存从只读更改为可写,然后再返回。要创建新对象,程序必须将只读内存更改为可写内存,写入新对象的初始值,然后将内存改回只读。

此外,这将为没有信号处理程序的单线程程序提供只读内存的外观。具有多个执行线程或单个处理程序的程序可能能够在内存处于可写状态时观察内存。

于 2013-08-05T13:46:55.687 回答
2

就标准而言,没有要求将const变量放入写保护的 RAM。const基本上只是编译器强制文档。

const实际上,如果一个值可以在编译时完全计算,编译器通常会为变量使用只读存储。否则,它们将被放入与其他所有内容相同的读写堆或堆栈中。

于 2013-08-05T12:27:04.147 回答
2

不。考虑一个const带有mutable成员的对象。如前所述, const 是一种编译时构造,可帮助程序员传达意图。

于 2013-08-05T12:28:30.373 回答
1

如果它们在只读内存中,它将阻止const_cast有用和有意义,所以这样的代码是不可能的,将const变量传递给带有非const参数的函数:-

void print (char * str)
{
    cout << str << endl;
}

int main () 
{
    const char * c = "test text";
    print ( const_cast<char *> (c) );
    return 0;
}

请注意,打印函数实际上可以更改最初定义的const变量中的值c

于 2013-08-05T12:41:56.107 回答
1

const是编译器的标志。在编译期间,编译器将常量添加到单独的组中并检查是否尝试更改它。如果某个函数试图这样做,你会得到编译器错误。

但是,可以使用指针欺骗编译器并更改值,但结果是您欺骗的不是编译器,而是您自己(或同事)。constconst_cast

正如我所说,我的朋友一直在寻找如何改变const价值。const不仅适用于编译器,它更适用于开发人员而不是编译器,因为它显示了哪些数据不会被更改。

于 2013-08-08T07:20:24.220 回答