我在一本书中读到这句话:
可以证明,无法构建一个编译器来实际确定C++函数是否会改变特定变量的值。
该段落谈论了为什么编译器在检查常量性时会保守。
为什么无法构建这样的编译器?
编译器总是可以检查变量是否被重新赋值、是否调用了非const函数以及是否作为非const参数传递...
我在一本书中读到这句话:
可以证明,无法构建一个编译器来实际确定C++函数是否会改变特定变量的值。
该段落谈论了为什么编译器在检查常量性时会保守。
为什么无法构建这样的编译器?
编译器总是可以检查变量是否被重新赋值、是否调用了非const函数以及是否作为非const参数传递...
void foo() {
if (bar() == 0) this->a = 1;
}
想象一下这样的编译器存在。让我们假设为方便起见,它提供了一个库函数,如果传递的函数修改了给定变量,则返回1,当函数没有修改时则返回0。那么这个程序应该打印什么?
int variable = 0;
void f() {
if (modifies_variable(f, variable)) {
/* do nothing */
} else {
/* modify variable */
variable = 1;
}
}
int main(int argc, char **argv) {
if (modifies_variable(f, variable)) {
printf("Modifies variable\n");
} else {
printf("Does not modify variable\n");
}
return 0;
}
f
是否修改了变量,而不是它是否能够修改变量。这个回答是正确的。 - Neil Gmodifies_variable
的代码,从而完全推翻了你的论点。 - orlp我认为在“C++函数是否会改变特定变量的值”中,关键词是“会”。理论上可以构建一个编译器来检查C++函数是否被允许改变特定变量的值,但不能确定这种改变一定会发生:
void maybe(int& val) {
cout << "Should I change value? [Y/N] >";
string reply;
cin >> reply;
if (reply == "Y") {
val = 42;
}
}
const
检查时所考虑的内容。 - Sergey Kalinichenko我认为没有必要引用停机问题来解释为什么你无法在编译时算法地知道一个给定的函数是否会修改某个变量。
相反,只需指出函数的行为通常取决于运行时条件,这是编译器事先无法知道的。例如:
int y;
int main(int argc, char *argv[]) {
if (argc > 2) y++;
}
编译器如何能够确定地预测是否会修改y
的值呢?
有些函数可以做到这一点,编译器也经常为此进行优化。例如,对于简单的内联访问器或许多纯函数来说,这是一个微不足道的优化。
但在普遍情况下,这是不可能知道的。
每当有来自另一个模块的系统调用或函数调用,或者调用可能被重写的方法时,任何事情都可能发生,包括某个黑客使用堆栈溢出来更改不相关变量的恶意接管。
然而,您应该使用const,避免全局变量,优先使用引用而非指针,避免将变量重新用于不相关的任务等。这将使编译器在执行积极的优化时更加容易。
很惊讶没有人直接使用停机问题来回答!这个问题可以非常直接地约简为停机问题。
假设编译器能够判断函数是否更改变量的值。那么,只要我们能够追踪整个程序中对于 x 变量的调用,在后续所有调用中,它肯定能够判断以下函数是否更改了 y 的值:
foo(int x){
if(x)
y=1;
}
int y;
main(){
int x;
...
run the program normally
...
foo(x);
}
y
的值时终止。在我看来,foo()
很快就返回了,然后main()
退出了。(另外,你在没有参数的情况下调用了foo()
...这是我的困惑之一。) - LarsHdo tons of complex stuff
if (condition on result of complex stuff)
{
change value of x
}
else
{
do not change value of x
}
x
的值会改变吗?要确定这一点,首先必须确定do tons of complex stuff
部分是否会触发条件 - 或者更基本的是,它是否会停止。这是编译器无法做到的。
当一个函数调用另一个编译器无法“看到”源代码的函数时,它要么假设变量已经被更改,要么可能会导致下面的事情出错。例如,假设我们在“foo.cpp”中有以下内容:
void foo(int& x)
{
ifstream f("f.dat", ifstream::binary);
f.read((char *)&x, sizeof(x));
}
我们在 "bar.cpp" 文件中有如下代码:
void bar(int& x)
{
foo(x);
}
编译器如何“知道”在 bar
中 x
没有变化(或更恰当地说是正在变化)?
如果这还不够复杂,我相信我们可以想出更复杂的东西。
const_cast
,它仍然会使x
改变 - 我将违反合同,该合同规定您不得更改const
变量,但由于您可以将任何东西转换为“更多const”,而且存在const_cast
,因此语言设计者肯定有这样一种想法,即有时有充分理由相信const
值可能需要更改。 - Mats Petersson通常情况下,编译器无法确定变量是否会被更改,正如已经指出的那样。
在检查const性质时,感兴趣的问题似乎是变量是否可以被函数更改。即使在支持指针的语言中,这也很困难。您无法控制其他代码对指针的操作,甚至可能从外部源读取(尽管不太可能)。在限制访问内存的语言中,这些类型的保证是可能的,并且比C++更具有侵略性的优化。