考虑以下非常简单的C ++代码:
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
int a[7] = {1, 2, 3, 4, 5, 6, 7};
int b[7];
copy(a, a+7, b);
for (int i=0; i<8; ++i)
cout << b[i] << endl;
}
现在,这是我在gdb中加载此代码时得到的结果:
(gdb) b 1
Breakpoint 1 at 0x100000a64: file stdcopy.cpp, line 1.
(gdb) r
Starting program: /Users/Babai/pastebin/a.out
Reading symbols for shared libraries ++......................... done
Breakpoint 1, main () at stdcopy.cpp:7
7 int a[7] = {1, 2, 3, 4, 5, 6, 7};
(gdb) n
9 copy(a, a+7, b);
(gdb) s
std::copy<int*, int*> (__first=0x7fff5fbffb8c, __last=0x7fff5fbffba8, __result=0x7fff5fbffb70) at stl_algobase.h:398
398 const bool __in = __is_normal_iterator<_InputIterator>::__value;
(gdb) bt
#0 std::copy<int*, int*> (__first=0x7fff5fbffb8c, __last=0x7fff5fbffba8, __result=0x7fff5fbffb70) at stl_algobase.h:398
#1 0x0000000100000acd in main () at stdcopy.cpp:9
(gdb) up
#1 main () at stdcopy.cpp:10
10 for (int i=0; i<8; ++i)
(gdb) p &a
$1 = (int (*)[7]) 0x7fff5fbffb8c
(gdb) p a + 7
$2 = (int *) 0x7fff5fbffba8
我在这段代码中没有看到任何valgrind错误,我想知道为什么。数组a有7个元素,最多可以访问+ 6,但为什么valgrind没有显示+ 7作为有效错误?
答案 0 :(得分:2)
Valgrind中的memcheck工具不会报告基于堆栈的内存错误(除非您超出堆栈地址空间的顶部)。它报告基于堆的内存错误。在堆上分配您的数组,Valgrind应该报告无效读取(不是来自copy
,而是来自经过最后的for
循环。)
#include <algorithm>
#include <iostream>
#include <cstring>
int main()
{
int* a = new int[7];
int* b = new int[7];
std::memset(a, 0, sizeof(int) * 7);
std::memset(b, 0, sizeof(int) * 7);
std::copy(a, a+7, b);
for (int i=0; i<8; ++i)
std::cout << b[i] << std::endl;
delete[] a;
delete[] b;
}
Memcheck是一个内存错误检测器。它可以检测以下内容 C和C ++程序中常见的问题。
访问内存你不应该,例如超越和低于堆 块,超越堆栈顶部,然后访问内存 它已被释放。 使用未定义的值,即尚未初始化的值,或从其他未定义的值派生的值。
不正确地释放堆内存,例如双重释放堆块, 或不匹配使用malloc / new / new []与free / delete / delete []
在memcpy和相关函数中重叠src和dst指针。
内存泄漏。
答案 1 :(得分:1)
越过数组的末尾会导致 Undefined Behavior ,这意味着任何事情都可能发生。但是,如果指针不取消引用,则允许指向数组的指针经过数组。
这是允许的,以便您可以检查数组的结尾,并在带有迭代器的容器中使用它。容器迭代器的end()
函数指向结束后的一个函数。然而,这永远不会被取消引用,否则你就处于Undefined Behavior的土地上。
答案 2 :(得分:0)
a+7
表达式不是错误 - 这是副本的结束指示符,永远不会被访问。该副本最多可执行,但不包括a+7
。
然而,当cout << b[i] << endl;
是另一个故事时正在执行i == 7
- 这是一个错误(虽然我不确定valgrind是否设计用于捕获非堆分配的那种错误数组)。
答案 3 :(得分:0)
我想我明白了。基本上发生的是在任何地方都无法访问*(a + 7)。对std :: copy的调用最终归结为memmove,并与Seth在5.7.5节中发布的内容结合起来我认为我们在这里很好:
(gdb) down
#0 std::__copy_aux<int*, int*> (__first=0x7fff5fbffb8c, __last=0x7fff5fbffba8, __result=0x7fff5fbffb70) at stl_algobase.h:313
313 && __are_same<_ValueTypeI, _ValueTypeO>::__value);
(gdb) n
315 return std::__copy<__simple, _Category>::copy(__first, __last, __result);
(gdb) s
std::__copy<true, std::random_access_iterator_tag>::copy<int> (__first=0x7fff5fbffb8c, __last=0x7fff5fbffba8, __result=0x7fff5fbffb70) at stl_algobase.h:298
298 std::memmove(__result, __first, sizeof(_Tp) * (__last - __first));
(gdb) list
293 {
294 template<typename _Tp>
295 static _Tp*
296 copy(const _Tp* __first, const _Tp* __last, _Tp* __result)
297 {
298 std::memmove(__result, __first, sizeof(_Tp) * (__last - __first));
299 return __result + (__last - __first);
300 }
301 };
302