Подтвердить что ты не робот

Почему это работает: возврат строкового литерала C из функции std::string и вызов c_str()

Недавно у нас была лекция в колледже, где наш профессор рассказал нам о разных вещах, чтобы быть осторожным при программировании на разных языках. Ниже приведен пример в С++:

std::string myFunction()
{
    return "it me!!";
}

int main(int argc, const char * argv[])
{
    const char* tempString = myFunction().c_str();

    char myNewString[100] = "Who is it?? - ";
    strcat(myNewString, tempString);
    printf("The string: %s", myNewString);

    return 0;
}

Идея, почему это приведет к ошибке, заключается в том, что return "it me!!" неявно вызывает конструктор std::string с char []. Эта строка возвращается из функции, а функция c_str() возвращает указатель на данные из std::string.

Поскольку строка, возвращаемая из функции, не упоминается нигде, она должна быть немедленно освобождена. Это была теория.

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

4b9b3361

Ответ 1

Ваш анализ верен. У вас есть поведение undefined. Это означает, что все может случиться. Кажется, что в вашем случае память, используемая для строки, хотя и выделена, по-прежнему сохраняет исходное содержимое при доступе к нему. Это часто происходит, потому что ОС не очищает выделенную память. Это просто означает, что он доступен для будущего использования. Это не то, на что должен иметь дело язык С++: это действительно деталь реализации ОС. Что касается С++, то применяется поведение "undefined для всех" catch-all ".

Ответ 2

Я предполагаю, что освобождение не подразумевает очистку или обнуление памяти. И, очевидно, это может привести к segfault в других обстоятельствах.

Ответ 3

Как уже упоминалось в соответствии со стандартом С++, это поведение undefined.

Причина, по которой это "работает", заключается в том, что память была возвращена менеджеру кучи, который держится за него для последующего повторного использования. Память не была возвращена ОС и, таким образом, все еще принадлежит процессу. Поэтому доступ к свободной памяти не вызывает ошибки сегментации. Тем не менее проблема остается в том, что теперь две части вашей программы (ваш код и менеджер кучи или новый владелец) получают доступ к памяти, которая, по их мнению, однозначно принадлежит им. Это рано или поздно трахает вещи.

Ответ 4

Я думаю, что причина в том, что память стека не перезаписана, поэтому он может получить исходные данные. Я создал тестовую функцию и вызвал ее перед strcat.

std::string myFunction()
{
    return "it me!!";
}


void test()
{
    std::string str = "this is my class";
    std::string hi = "hahahahahaha";

    return;
}

int main(int argc, const char * argv[])
{
    const char* tempString = myFunction().c_str();


    test();
    char myNewString[100] = "Who is it?? - ";
    strcat(myNewString, tempString);
    printf("The string: %s\n", myNewString);

    return 0;
}

И получите результат:

The string: Who is it?? - hahahahahaha

Это доказало мою идею.

Ответ 5

Тот факт, что строка освобождается, не обязательно означает, что память больше не доступна. Пока вы ничего не делаете, чтобы перезаписать его, память все еще доступна.

Ответ 6

Как сказано выше - это непредсказуемое поведение. Это не работает для меня (в конфигурации Debug). std::string Деструктор вызывается сразу после назначения в tempString - когда выражение, использующее объект временной строки, заканчивается. Оставляя tempString указывать на выпущенную память (в вашем случае все еще есть литералы "it me!!" ).

Ответ 7

Вы не можете заключить, что нет проблем, получив ваш результат по совпадению.

Существуют другие способы обнаружения "проблем":

  • Статический анализ.
  • Valgrind поймает ошибку, показывая вам как действие оскорбления (пытается скопировать из освобожденной зоны - по strcat), так и освобождение, вызвавшее освобождение.

Invalid read of size 1

   at 0x40265BD: strcat (mc_replace_strmem.c:262)
   by 0x80A5BDB: main() (valgrind_sample_for_so.cpp:20)
   [...]
Address 0x5be236d is 13 bytes inside a block of size 55 free'd
   at 0x4024B46: operator delete(void*) (vg_replace_malloc.c:480)
   by 0x563E6BC: std::string::_Rep::_M_destroy(std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.13)
   by 0x80A5C18: main() (basic_string.h:236)
   [...]

код >

  • Единственным правильным способом было бы доказать правильность программы. Но это очень сложно для процедурного языка, и С++ делает это сложнее.

Ответ 8

Если мне что-то не хватает, я думаю, что это проблема масштаба. myFunction() возвращает std::string. Строковый объект напрямую не привязан к переменной. Но он остается в сфере действия до конца main(). Таким образом, tempString будет указывать на совершенно допустимое и доступное пространство в памяти до конца основного() кода блока, в которое время tempString также выпадет из области видимости.

Ответ 9

Собственно, строковые литералы имеют статическую продолжительность хранения. Они упакованы внутри самого исполняемого файла. Они не находятся в стеке и не распределяются динамически. В обычном случае правильно, что это указывает на недопустимую память и поведение undefined, однако для строк память находится в статическом хранилище, поэтому она всегда будет действительной.