Конкатенация строки стека со строкой кучи дает странные результаты - PullRequest
9 голосов
/ 29 июля 2011

Я уверен, что следующее имеет рациональное объяснение, но, тем не менее, я немного озадачен.

Проблема связана с функцией, которая создает _TCHAR[CONSTANT], _TCHAR*, объединяет их и возвращает результат.

По какой-то причине звонок на whatTheHeck() с _tmain() возвращает бред.

_TCHAR* whatTheHeck(_TCHAR* name) {
    _TCHAR Buffer[BUFSIZE];
    DWORD dwRet;
    dwRet = GetCurrentDirectory(BUFSIZE, Buffer);
    _TCHAR* what = new _TCHAR[BUFSIZE];
    what = _tcscat(Buffer, TEXT("\\"));
    what = _tcscat(what, name);
    return what;
}

int _tmain(int argc, _TCHAR* argv[]) {

    _TCHAR* failure = whatTheHeck(TEXT("gibberish);")); // not again ..
    _tprintf(TEXT("|--> %s\n"), failure);

    _TCHAR* success = createFileName(TEXT("readme.txt")); // much better
    _tprintf(TEXT("|--> %s\n"), success);

    return 0;
}

Напротив, при работе с кучей все работает как положено.

_TCHAR* createFileName(_TCHAR* name) {
    _TCHAR* Buffer = new _TCHAR[BUFSIZE];
    DWORD dwRet;
    dwRet = GetCurrentDirectory(BUFSIZE, Buffer);
    Buffer = _tcscat(Buffer, TEXT("\\"));
    Buffer = _tcscat(Buffer, name);
    return Buffer;
}

Почему разница?

Это потому, что _tcscat() объединяет адреса памяти вместо их содержимого и возвращает очистку стека?

Ответы [ 4 ]

14 голосов
/ 29 июля 2011

Есть много проблем с вашим кодом.Давайте разберемся с этим, будем ли:

_TCHAR* whatTheHeck(_TCHAR* name)   // We're inside a local scope
{
    _TCHAR Buffer[BUFSIZE];         // "Buffer" has automatic storage

    _TCHAR* what = new _TCHAR[BUFSIZE];  // "what" points to newly allocated dyn. memory

    what = _tcscat(Buffer, TEXT("\\"));  // Oh no, we overwrite "what" - leak!
                                         // Now what == Buffer.

    what = _tcscat(what, name);  // Equivalent to "_tcscat(Buffer, name)"

    return Buffer;               // WTPF? We're returning a local automatic!
 }

Как вы можете видеть, вы оба вызываете утечку памяти из-за излишней и безрассудной new, и вы также возвращаете адрес локального объекта в прошлом.его время жизни!

Я бы настоятельно рекомендовал

  1. прочитать документацию для strcat и понять "источник" и "пункт назначения",
  2. не используется strcat, но более безопасная версия, такая как strncat,
  3. не использует strncat, но вместо этого std::string.
4 голосов
/ 29 июля 2011

Это происходит потому, что _tcscat объединяет в целевой параметр, который является первым, а затем возвращает его.Таким образом, он возвращает указатель на массив Buffer и хранится в what в этой строке:

what = _tcscat(Buffer, TEXT("\\"));

Затем возвращается этот указатель, и у вас возникает неопределенное поведение, когда вы пытаетесь его использоватьпотому что локальный Buffer больше не существует.

Кроме того, строка выше также вызывает утечку памяти, выделенной для what:

_TCHAR* what = new _TCHAR[BUFSIZE];
what = _tcscat(Buffer, TEXT("\\")); // this loses the memory allocated
                                    // in the previous line
0 голосов
/ 29 июля 2011
_TCHAR* what = new _TCHAR[BUFSIZE];
what = _tcscat(Buffer, TEXT("\\"));

Разве вы не перезаписываете what Buffer, который является локальной переменной для функции. Как только стек размотан, Buffer выходит из области видимости, и, таким образом, вы получаете неожиданные значения. Также это утечка памяти.

В сценариях выделения кучи вы можете объявить указатель const, чтобы избежать таких опасностей:

_TCHAR* const what = new _TCHAR[BUFSIZE];
        ^^^^^ (avoids overwriting)

Лучше всего использовать std::string и избегать таких мелких проблем.

0 голосов
/ 29 июля 2011

Динамически распределенная память, на которую указывает what, не инициализирована. Содержит тарабарщину. _tcscat ожидает, что строка будет правильно завершена нулем.

_TCHAR* what = new _TCHAR[BUFSIZE]();

Заполняет what '\0' символами.

_TCHAR* what = new _TCHAR[BUFSIZE];
what[0] = '\0';

Это правильно завершает пустую строку.

GetCurrentDirectory не ожидает, что буфер завершится нулем. Он что-то записывает и корректно завершает. Затем вы можете передать это функциям объединения.


Как примечание: ваша функция уязвима для переполнения буфера. Очевидно, вы разрешаете GetCurrentDirectory заполнять все, что вы выделили, а затем хотите добавить к нему, не заботясь о том, осталось ли еще место.

...