为什么vector[n] = val
在保留空向量之前不会给出分段错误或更改向量数据。检查此示例:
#include <iostream>
#include <vector>
int main()
{
std::vector<int> temp;
temp.reserve(8);
temp[0] = 1;
temp[3] = 3; //why no attribution???
temp[7] = 1;
temp[8] = 3; //why no segmentation fault???
std::cout << temp.size();
for(auto&a: temp){ //because the attribution didn't work, no loop needed
std::cout << a;
}
return 0;
}
另外,为什么运营商[]不会抛出'out_of_range&#39;,因为如果使用该程序而不是方法.at()
答案 0 :(得分:1)
您正在通过访问不应该访问的内存来创建未定义的行为。不幸的是,内存仍然分配给您的程序,因此操作系统不会终止您的程序。
要看到你做错了什么,请编译你的程序:
g++ test.cpp -fsanitize=address
当你现在运行它时,它会非常清楚地告诉你,你正在做一些你不应该做的事情:
=================================================================
==17401==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000000030 at pc 0x000000401208 bp 0x7ffee64f71f0 sp 0x7ffee64f71e0
WRITE of size 4 at 0x603000000030 thread T0
#0 0x401207 in main /home/mu/test.cpp:13
#1 0x7fbcba273889 in __libc_start_main (/lib64/libc.so.6+0x20889)
#2 0x400f49 in _start (/home/mu/a.out+0x400f49)
0x603000000030 is located 0 bytes to the right of 32-byte region [0x603000000010,0x603000000030)
allocated by thread T0 here:
#0 0x7fbcbafbe158 in operator new(unsigned long) (/lib64/libasan.so.4+0xe0158)
#1 0x4022d6 in __gnu_cxx::new_allocator<int>::allocate(unsigned long, void const*) /usr/include/c++/7/ext/new_allocator.h:111
#2 0x40222d in std::allocator_traits<std::allocator<int> >::allocate(std::allocator<int>&, unsigned long) /usr/include/c++/7/bits/alloc_traits.h:436
#3 0x402153 in std::_Vector_base<int, std::allocator<int> >::_M_allocate(unsigned long) /usr/include/c++/7/bits/stl_vector.h:172
#4 0x401ebb in int* std::vector<int, std::allocator<int> >::_M_allocate_and_copy<std::move_iterator<int*> >(unsigned long, std::move_iterator<int*>, std::move_iterator<int*>) /usr/include/c++/7/bits/stl_vector.h:1260
#5 0x401671 in std::vector<int, std::allocator<int> >::reserve(unsigned long) /usr/include/c++/7/bits/vector.tcc:73
#6 0x4010c9 in main /home/mu/test.cpp:7
#7 0x7fbcba273889 in __libc_start_main (/lib64/libc.so.6+0x20889)
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/mu/test.cpp:13 in main
Shadow bytes around the buggy address:
0x0c067fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c067fff8000: fa fa 00 00 00 00[fa]fa fa fa fa fa fa fa fa fa
0x0c067fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==17401==ABORTING
答案 1 :(得分:1)
你有两种不同的误解。
reserve
与resize
混淆。目前,您的程序有未定义的行为,因为temp
作为空向量启动,temp.reserve(8);
不会改变其外观所显示的大小,只会改变其内部内存使用情况,temp[0]
超出范围。
未定义的行为意味着C ++语言没有说明会发生什么。您的程序可能会崩溃,也可能不会崩溃。
可能在幕后发生的事情是reserve
不仅为8个元素保留了空间,而且保留了更多的空间,因此内存访问不会导致坏事发生。您的C ++工具集可能就三个指针实现std::vector
:开始,逻辑结束和物理结束。您的reserve
电话会增加物理结束,但只留下逻辑结尾。因此,逻辑结尾仍等于 begin ,因此size
保持为零。
我们鼓励您使用调试器来确定此假设是否真的适用于您的情况。
顺便说一句:如果您使用resize
而不是reserve
,那么越界访问和未定义的行为将在temp[8] = 3;
发生。
另外,为什么
operator []
不会抛出out_of_range
程序将使用,而不是方法.at()
因为不需要这样做。如果您想要保证例外,请使用at()
。但是,您通常应该更喜欢operator[]
,并且不会发生越界错误。
答案 2 :(得分:1)
程序的行为未定义。周期。
分段违规只是未定义行为的一种可能后果。另一个可能的结果是看起来表现正确&#34;但是你可能会定义这个概念。
实际上,分段违规是由unix主机系统检测到您的程序访问它不应该访问的内存,并向您的程序发送信号SIGSEGV
而导致您的程序终止。操作系统的检测不是万无一失的,因此它可能无法检测到程序的所有实例超出范围 - 例如,如果修改向量的不存在元素会修改程序分配的某些内存区域另一个变量。
std::vector
operator[]()
未被指定为抛出异常,因此temp[n]
n
位于0
到temp.size()-1
的范围之外不需要抛出异常。它将具有未定义的行为。如果值超出范围temp.at()
到0
,temp.size()-1
会抛出异常,但不需要operator[]
。
致电temp.reserve(8)
不会影响temp.size()
。 temp.reserve()
会影响temp.capacity()
,唯一的限制是temp.capacity() >= temp.size()
。因为,在您的示例中,temp.size()
为零(由默认构造函数实现),如果temp[n]
介于{{1}之间,则无法保证n
的使用可以访问保留的内存}和0
。根据标准,行为仍未定义。它似乎可以工作&#34;或者它可能没有。
7
时,temp.capacity() > temp.size()
的一种可能实现方式是标记额外的内存不可访问(std::vector::reserve()
到temp.size()
的所有元素)并在代码中触发错误情况当temp.capacity() - 1
超出范围时,temp[n]
的每次使用。这样的实现需要n
(以及影响向量大小的其他操作)来标记temp.resize()
和0
之间可访问的元素。没有要求实现这样做 - 因此您的代码行为与您所看到的一样是允许的。但是,同样地,没有什么能阻止实现执行我所描述的内容 - 因此在代码中temp.size() - 1
之后的任何语句都失败了。
您的测试似乎表明访问最多temp.reserve()
的元素 - 可能更多 - 将会工作&#34;。在您测试时,这特定于您的编译器和标准库。标准无法保证,您的代码将来很容易破解(例如,当编译器或标准库更新到更新版本时)。
答案 3 :(得分:0)
http://www.cplusplus.com/reference/vector/vector/reserve/
顺便说一句,你无法预测未定义的行为。在库的更新版本中,可能会发生崩溃。如果n大于当前矢量容量,则该函数会导致 重新分配其存储容器增加其容量为n (或更高)。
答案 4 :(得分:0)
使用temp.at(0) = 1;
更好地检测问题。
答案 5 :(得分:0)
at() 有一个边界检查,而 [] 不这样做,但 at() 稍微慢一点,你可以使用带有运算符 [] 的断言来获得非常好的结果......>
reserv 为 Vector 节省了额外的空间,从而改变了容量...... 调整大小是完全不同的......