C++ 当C++;将元素从函数的返回值存储到std::vector中

C++ 当C++;将元素从函数的返回值存储到std::vector中,c++,gcc,vector,compiler-optimization,compiler-bug,C++,Gcc,Vector,Compiler Optimization,Compiler Bug,当函数涉及重新分配时,我发现一些编译器可能会在函数调用之前保存地址。它引导存储在无效地址中的返回值 有一个例子来解释上述描述中的行为 #include <stdio.h> #include <vector> using namespace std; vector<int> A; int func() { A.push_back(3); A.push_back(4); return 5; } int main() {

当函数涉及重新分配时,我发现一些编译器可能会在函数调用之前保存地址。它引导存储在无效地址中的返回值

有一个例子来解释上述描述中的行为

#include <stdio.h>
#include <vector> 
using namespace std;

vector<int> A; 
int func() { 
    A.push_back(3);
    A.push_back(4);
    return 5; 
} 
int main() { 
    A.reserve(2);
    A.push_back(0);
    A.push_back(1);
    A[1] = func();
    printf("%d\n", A[1]);
    return 0;
}
#包括
#包括
使用名称空间std;
载体A;
int func(){
A.推回(3);
A.推回(4);
返回5;
} 
int main(){
A.储备金(2);
A.推回(0);
A.推回(1);
A[1]=func();
printf(“%d\n”,A[1]);
返回0;
}

有一些常见的C++编译器,测试结果如下:

  • GCC(GNU编译器集合):运行时错误或输出
    1
  • 叮当声:输出
    5
  • VC++:输出
    5

是未定义行为吗?< /P> < P> C++ 17版本之前的所有C++版本中的行为未定义。原因很简单,赋值运算符的两边可以按任意顺序求值:

  • 假设首先对
    A[1]
    求值,则此时会得到一个
    int&
    引用
    A
    的第二个元素
  • 然后,计算
    func()
    ,它可以重新分配向量的存储,使以前检索到的
    int&
    成为悬空引用
  • 最后,执行分配,写入未分配的存储器。由于标准分配器会缓存内存,因此操作系统通常不会捕获此错误
只有在C++17中,才对赋值进行了以下操作:

在每个简单赋值表达式E1=E2和每个化合物中 赋值表达式E1@=E2,每个值的计算和副作用 E2的每一个值在每次计算和 E1


在C++17中,
A[1]
必须在调用
func()
后进行求值,该函数随后提供定义的可靠行为。

如果您在“迭代器无效”下选中,您将看到
push_back()
可能会使每个迭代器在容量发生变化时失效,因为它必须重新分配内存。请记住,对于
std::vector
,指针也是有效的迭代器。由于
push_back()
可能重新分配,也可能不重新分配,而且您无法知道是否会重新分配,因此该行为未定义realloc),编译器可能在调用
push_back
时将数据的地址保存在寄存器中。我遇到了他之前所说的错误,所以你可以说我有偏见,但是…我在重读后知道OP的意思,但是告诉人们事物的正确名称(或者他们所用的词的实际含义)没有坏处。这些都是有用的信息。如果使用
-std=c++17
,用GCC生成的二进制文件不会崩溃。也许C++17中的措辞已经改变了?@HolyBlackCat确实改变了。操作员必须尊重关联性。我认为应该说“它可以为向量重新分配存储”,而不是C++17更改了一些关于评估顺序的规则,可以在cppreference页面@MorrisYang linked上的第20项中找到,代码的行为在C++17中不是未定义的,因此,如果您可以编辑您的答案以显示它是完整的,指针(或引用)和迭代器是分开的,有单独的规则。如果你看一下其他容器,你会发现其中一个容器失效,而另一个容器无效。当然,但这不是其中之一。指向T的指针满足vector::iterator的所有要求。指针可以是迭代器,但不能使其成为迭代器。在
std::vector
的情况下,迭代器和引用同时失效,但这并不意味着它们是相同的。这是一种会让人困惑的答案。如果我编辑我的答案,明确地说“对于
std::vector
而言,指针是一个有效的迭代器”,那会令人满意吗?@WillCrawford我并不否认指针是一个完全有效的迭代器实现,一些早期版本的
vector
就是这样工作的。但是试着比较一个指向
v.end()
的指针,告诉我这是怎么回事。