Valgrind没有显示数组副本的错误?

时间:2012-02-08 19:44:15

标签: c++ valgrind

考虑以下非常简单的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作为有效错误?

4 个答案:

答案 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;
}


来自Valgrind manual

  

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