有一个简单的程序
#include <stdio.h>
int counter = 3;
const int& f() {
return counter++;
}
const int& g() {
return counter++;
}
const int& h(int w) {
counter = w;
return counter;
}
void main()
{
const int& x = f();
const int& y = g();
const int& z = g();
const int& t = h(123);
counter = 45678;
printf("%d %d %d %d", x, y, z, t);
}为什么它的输出是5 5 5 45678?特别是,为什么x和y是5?如果它们在初始化后仍然连接到计数器,为什么它们不是45679或类似的东西?
发布于 2022-05-06 14:26:08
因为f和g所做的后增量。
const int& f() {
return counter++;
}
const int& g() {
return counter++;
}counter++实际上并不返回counter,而是一个由operator++(int)返回的临时int对象。在C++中,opreator++有两种形式。
Type& operator++(); //pre-increment
Type operator++(int); //post-increment第二个版本中的int参数是一个用于区分调用的虚拟参数,因为不能仅在返回类型上超载,因为您可以看到增量后返回的值,而不是引用,因此您将返回一个临时变量的引用!
你可以看到GCC和Clang的警告,如果你把他们打开。https://godbolt.org/z/f1fMP463a
因此,总的来说,您调用UB,因为您返回的引用是悬空的。你遇到了最坏的情况,看起来很管用。
此外,main必须在C++中返回int。
发布于 2022-05-06 14:23:50
您的程序有未定义的行为。f和g的返回值在调用它们的语句末尾不再存在,因此x、y和z在声明后不引用任何内容。
发布于 2022-05-06 14:56:48
就像到目前为止所给出的答案的补充:为什么没有明确的行为?让我们考虑一个假设的operator++实现:
template <typename T>
T& operator++(T& t) // pre-increment:
{
t = t + 1;
return t;
}
// should be clear so far...
template <typename T>
T operator++(T& t, int) // post-increment:
{
// t = t + 1; // cannot do now, how to return the old value then???
T b = t; // need to store the value in a backup copy first!
t = t + 1; // NOW we can
return t; // need to return the OLD value -> cannot return by reference either,
// it's a TEMPORARY ...
}当您现在使用operator++ (它本身返回一个临时的)时,您将返回一个对临时的引用,该引用在返回-未定义的行为之后不再存在。
为什么你现在看到5?纯技术(仍然是UB!):由于f和g看上去是相同的,所以它们在被调用时对堆栈的末尾使用相同的偏移量,并且被一个接一个地调用,因此它们都在同一个堆栈端开始存储临时的堆栈地址,这就是引用被绑定到的地方。注意,这最后包含最后一次增量之前的值,因此5,而不是6。h不需要堆栈上的任何附加值,因此那里的值不会被覆盖--只是在执行赋值时是一样的,因此该值仍然保留。如果h出于某种原因使用堆栈本身,您可能会看到完全不同的东西..。
https://stackoverflow.com/questions/72142992
复制相似问题