Возможная утечка памяти при использовании строки C ++ - PullRequest
9 голосов
/ 23 ноября 2010

Рассмотрим следующую программу на C ++:

#include <cstdlib> // for exit(3)
#include <string>
#include <iostream>
using namespace std;

void die()
{
    exit(0);
}

int main()
{
    string s("Hello, World!");
    cout << s << endl;
    die();
}

Запуск этого через valgrind показывает это (некоторые выходные данные урезаны для краткости):

==1643== HEAP SUMMARY:
==1643==     in use at exit: 26 bytes in 1 blocks
==1643==   total heap usage: 1 allocs, 0 frees, 26 bytes allocated
==1643==
==1643== LEAK SUMMARY:
==1643==    definitely lost: 0 bytes in 0 blocks
==1643==    indirectly lost: 0 bytes in 0 blocks
==1643==      possibly lost: 26 bytes in 1 blocks
==1643==    still reachable: 0 bytes in 0 blocks
==1643==         suppressed: 0 bytes in 0 blocks

Как видите, есть вероятность , что 26 байтов, выделенных в куче, были потеряны.Я знаю, что класс std::string имеет 12-байтовую структуру (по крайней мере, на моей 32-битной архитектуре x86 и компиляторе GNU 4.2.4) и "Hello, World!"с нулевым терминатором имеет 14 байтов.Если я правильно понимаю, 12-байтовая структура содержит указатель на строку символов, выделенный размер и счетчик ссылок (кто-то поправит меня, если я ошибаюсь).

Теперь мои вопросы: какстроки C ++ хранятся относительно стека / кучи?Существует ли объект стека для std::string (или других контейнеров STL) при объявлении?

PS Я где-то читал, что valgrind может сообщать о ложном срабатывании утечки памяти в некоторыхПрограммы на C ++, которые используют контейнеры STL (и «почти контейнеры», такие как std::string).Я не слишком беспокоюсь об этой утечке, но она возбуждает мое любопытство к контейнерам STL и управлению памятью.

Ответы [ 5 ]

11 голосов
/ 23 ноября 2010

Вызов exit "завершает программу, не покидая текущий блок и, следовательно, не уничтожая объекты с автоматическим хранением".

Другими словами, утечка или нет, вам на самом деле все равно.Когда вы звоните exit, вы говорите: «Закройте эту программу, мне уже все равно».Так что хватит заботиться.:)

Очевидно, что это приведет к утечке ресурсов, потому что вы никогда не позволяете деструктору строки работать абсолютно независимо от того, как он управляет этими ресурсами.

9 голосов
/ 23 ноября 2010

Другие верны, вы просачиваетесь, потому что вы звоните из выхода. Для ясности, утечка - это не строка, выделенная в стеке, а память, выделенная в куче строкой. Например:

struct Foo { };

int main()
{
    Foo f;
    die();
}

не приведет к тому, что valgrind сообщит об утечке.

Утечка вероятна (вместо определенной), потому что у вас есть внутренний указатель на память, выделенную в куче. basic_string несет ответственность за это. Из шапки на моей машине:

   *  A string looks like this:
   *
   *  @code
   *                                        [_Rep]
   *                                        _M_length
   *   [basic_string<char_type>]            _M_capacity
   *   _M_dataplus                          _M_refcount
   *   _M_p ---------------->               unnamed array of char_type
   *  @endcode
   *
   *  Where the _M_p points to the first character in the string, and
   *  you cast it to a pointer-to-_Rep and subtract 1 to get a
   *  pointer to the header.

Они указывают на то, что _M_p не указывает на начало памяти, выделенной в куче, а указывает на первый символ в строке. Вот простой пример:

struct Foo
{
    Foo()
    {
        // Allocate 4 ints.
        m_data = new int[4];
        // Move the pointer.
        ++m_data;
        // Null the pointer
        //m_data = 0;
    }
    ~Foo()
    {
        // Put the pointer back, then delete it.
        --m_data;
        delete [] m_data;
    }
    int* m_data;
};

int main()
{
    Foo f;
    die();
}

Это сообщит о вероятной утечке в валгринде. Если вы закомментируете строки, в которых я перемещаюсь, m_data valgrind сообщит «все еще достижимо». Если вы раскомментируете строку, где я установил m_data в 0, вы получите определенную утечку.

Документация Valgrind содержит больше информации о возможных утечках и внутренних указателях.

4 голосов
/ 23 ноября 2010

Конечно, это "утечка", если exit до того, как остаётся кадр стека s, вы не даете деструктору s шанс на выполнение.

Что касается вашего вопроса относительно std::string хранилища: разные реализации делают разные вещи. Некоторые выделяют около 12 байтов в стеке, который используется, если длина строки составляет 12 байтов или меньше. Более длинные строки идут в кучу. Другие реализации всегда идут в кучу. Некоторые из них подсчитываются и имеют семантику копирования при записи, некоторые нет. Пожалуйста, обратитесь к Скотту Мейерсу Effective STL, пункт 15.

1 голос
/ 23 ноября 2010

Я бы не использовал exit (). Я не вижу реальной причины использовать этот вызов.Не уверен, что это приведет к немедленной остановке процесса без предварительной очистки памяти, хотя valgrind все еще работает.

1 голос
/ 23 ноября 2010

gcc STL имеет частный пул памяти для контейнеров и строк. Вы можете отключить это; смотри в valgrind FAQ

http://valgrind.org/docs/manual/faq.html#faq.reports

...