Valgrind не показывает ошибки с копией массива? - PullRequest
1 голос
/ 08 февраля 2012

Рассмотрим следующий довольно простой код 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 ]

2 голосов
/ 08 февраля 2012

Инструмент memcheck в Valgrind не сообщает об ошибках памяти стека (если только вы не переполните верхнюю часть адресного пространства стека). Он сообщает об ошибках памяти heap . Выделите ваш массив в куче, и 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 :

Memcheck - это детектор ошибок памяти. Он может обнаружить следующее проблемы, которые часто встречаются в программах на C и C ++.

Доступ к памяти вам не следует, например, обгон и недогруз кучи блоки, переполнение вершины стека и доступ к памяти после это было освобождено. Использование неопределенных значений, то есть значений, которые не были инициализированы или были получены из других неопределенных значений.

Неправильное освобождение памяти кучи, например, двойное освобождение блоков кучи, или неправильное использование malloc / new / new [] и free / delete / delete []

Перекрывающиеся указатели src и dst в memcpy и смежных функциях.

Утечки памяти.

1 голос
/ 08 февраля 2012

Пропуск конца массива приводит к неопределенному поведению , означающему, что все может произойти. Тем не менее, ваш указатель на массив может пройти один массив, если указатель с разыменованием не .

Это разрешено, так что вы можете проверить конец массива, и он используется в контейнерах с итераторами. Функция end() для итератора контейнера указывает на один конец. Это, однако, никогда не разыменовывается, иначе вы находитесь в стране неопределенного поведения.

0 голосов
/ 09 февраля 2012

Я думаю, что понял.В основном происходит то, что нигде нет доступа * (+ 7).Вызов std :: copy в конечном итоге сводится к memmove и соединяется с тем, что Сет написал в разделе 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 
0 голосов
/ 09 февраля 2012

Выражение a+7 не является ошибкой - это конечный индикатор для копии, к которому никогда не осуществляется доступ.Копия выполняется до, но не включая a+7.

Однако cout << b[i] << endl;, выполняемый, когда i == 7 - это другая история - это ошибка (хотя я не уверен, что valgrind предназначен для перехвата такого рода ошибки в массиве, выделенном без кучи).

...