简体   繁体   中英

Valgrind not showing errors with array copy?

Consider the following pretty simple C++ code:

#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;
}

Now here's what I get when I load this code in 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

I don't see any valgrind errors in this code and I am wondering why. The array a has 7 elements and accessing up to a + 6 is fine, but why is valgrind not showing a + 7 as a valid error?

The memcheck tool in Valgrind does not report stack based memory errors (unless you overrun the top of your stack address space). It reports heap based memory errors. Allocate your array on the heap, and Valgrind should report invalid reads (not from the copy , but from the for loop which goes past the end.)

#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;
}


From the Valgrind manual :

Memcheck is a memory error detector. It can detect the following problems that are common in C and C++ programs.

Accessing memory you shouldn't, eg overrunning and underrunning heap blocks, overrunning the top of the stack, and accessing memory after it has been freed. Using undefined values, ie values that have not been initialised, or that have been derived from other undefined values.

Incorrect freeing of heap memory, such as double-freeing heap blocks, or mismatched use of malloc/new/new[] versus free/delete/delete[]

Overlapping src and dst pointers in memcpy and related functions.

Memory leaks.

Going past the end of the array results in Undefined Behaviour , meaning that anything could happen. However, your pointer into the array, is allowed to go one past the array, if the pointer is not dereferenced.

This is allowed so that you can check for end of an array, and it is used in containers with iterators. The end() function for a container iterator points to one past the end. This however never get's dereferenced, else you're in Undefined Behaviour land.

The a+7 expression isn't an error - that's the end indicator for the copy and is never accessed. The copy is performed up to, but not including a+7 .

However, the cout << b[i] << endl; being executed when i == 7 is another story - that is an error (though I'm not sure if valgrind is designed to catch that kind of error on a non-heap allocated array).

I think I got it. What basically is happening is that there's no access of *(a + 7) anywhere. The call to std::copy ultimately boils down to memmove, and couple with what Seth posted in section 5.7.5 I think we are good here:

(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 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM