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中,
A[1]
必须在调用func()
后进行求值,该函数随后提供定义的可靠行为。如果您在“迭代器无效”下选中,您将看到push_back()
可能会使每个迭代器在容量发生变化时失效,因为它必须重新分配内存。请记住,对于std::vector
,指针也是有效的迭代器。由于push_back()
可能重新分配,也可能不重新分配,而且您无法知道是否会重新分配,因此该行为未定义请参阅至少在C++ 2014标准中的赋值操作符的描述。“当函数涉及重定位时,我发现一些编译器可能在函数调用之前保存地址”——这些都不是你们所想的。这与动态链接无关。“当函数涉及重新分配时”是准确的。这一点很好。重新分配在某种程度上意味着重新分配(创建新的空间,移动东西),但如果这是原作者真正的意思,那么使用重新分配在这里会更清楚。@他的意思很明显,与链接无关。他的意思是,如果向量内容被重新定位(理论上,重新定位可以使数据保持不变,如果库在幕后使用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()
的指针,告诉我这是怎么回事。