Почему вызов c_str() для функции, которая возвращает строку, не работает?

#c #c 11 #c-strings

#c #строка

Вопрос:

У меня есть функция, которая возвращает строку. Однако, когда я вызываю его и делаю c_str() над ним, чтобы преобразовать в const char* , это работает только тогда, когда я сначала сохраняю его в другую строку. Если я напрямую вызываю c_str() функцию, она сохраняет значение мусора в const char* .

Почему это происходит? Чувствую, что я упускаю здесь что-то очень фундаментальное…

 string str = SomeFunction();
const char* strConverted = str.c_str(); // strConverted stores the value of the string properly
const char* charArray= SomeFunction().c_str(); // charArray stores garbage value

static string SomeFunction()
{
    string str;
    // does some string stuff
    return str;
}
  

Комментарии:

1. c_str() возвращает указатель на базовый массив символов. Проблема в том, что вы вызываете это временно. Итак, после ; объект удаляется, и вуаля: вы получаете мусор.

2. Некоторая информация о том, «как» это не работает, помогла бы.. Ошибки компилятора? Данные мусора? много потенциальных ошибок только в этом фрагменте.

3. В @Yeraze OP четко указано, что в нем хранится значение мусора, поэтому это определенно не ошибка компилятора.

Ответ №1:

SomeFunction().c_str() дает вам указатель на временную (автоматическую переменную str в теле SomeFunction ). В отличие от ссылок, время жизни временных объектов в этом случае не увеличивается, и в итоге вы charArray получаете висячий указатель, объясняющий значение мусора, которое вы увидите позже, когда попытаетесь использовать charArray .

С другой стороны, когда вы делаете

 string str_copy = SomeFunction();
  

str_copy является копией возвращаемого значения SomeFunction() . Вызов c_str() этой функции теперь дает вам указатель на действительные данные.

Комментарии:

1. Nowdays str_copy на самом деле просто переместил владение, но точка все еще действительна.

Ответ №2:

Объект value, возвращаемый функцией, является временным. Результаты c_str() действительны только в течение срока службы временного. Срок службы временного в большинстве случаев равен концу полного выражения, которое часто является точкой с запятой.

 const char *p = SomeFunction();
printf("%sn", p); // p points to invalid memory here.
  

Обходной путь заключается в том, чтобы убедиться, что вы используете результат c_str() до окончания полного выражения.

 #include <cstring>

char *strdup(const char *src_str) noexcept {
    char *new_str = new char[std::strlen(src_str)   1];
    std::strcpy(new_str, src_str);
    return new_str;
}

const char *p = strdup(SomeFunction.c_str());
  

Обратите внимание, что strdup это функция POSIX, поэтому, если вы являетесь платформой, поддерживающей POSIX, она уже есть.

Ответ №3:

  1. « string str » в методе SomeFunction() является локальной переменной в SomeFunction() и сохраняется только внутри области SomeFunction() ;

  2. Поскольку возвращаемый тип метода SomeFunction() — string, а не ссылка на string, после « return str; «, SomeFunction() вернет копию значения str , которое будет сохранено как временное значение в некотором месте памяти, после вызова SomeFunction() временное значение будет немедленно уничтожено;

  3. « string str = SomeFunction(); » сохранит возвращенное временное значение SomeFunction() в string str , фактически является копией этого значения и хранится в str , выделяется новый блок памяти, а время жизни str больше, чем возвращенное временное значение SomeFunction() , после « ; » вызов SomeFunction() завершен, и возвращенное временное значение немедленно уничтожается, память перерабатывается системой, но копия этого значения все еще хранится в str . Вот почему « const char* strConverted = str.c_str(); » может получить правильное значение, фактически c_str() возвращая указатель на начальный элемент str (адрес памяти первого элемента str указанного строкового значения), а не возвращаемое временное значение SomeFunction() ;

  4. « const char* charArray= SomeFunction().c_str(); » отличается, « SomeFunction().c_str() » вернет указатель на начальный элемент возвращаемого временного значения (адрес памяти первого элемента возвращаемого временного строкового значения), но после вызова SomeFunction() возвращаемое временное значение уничтожается, и этот адрес памяти повторно используется системой, charArray может получить значение этого адреса памяти, но не то значение, которое вы ожидали;

Ответ №4:

Используйте strcpy для копирования строки в локально определенный массив, и ваш код будет работать нормально.

Комментарии:

1. В любом случае SomeFunction().c_str() выдаст вам недопустимый адрес. Если вы попытаетесь strcpy это сделать, это приведет к неопределенному поведению.

2. Нет. Сделал это сегодня. Отлично сработало.

3. Не могли бы вы предоставить небольшой, но полный рабочий пример, демонстрирующий это поведение?

4. Это существенно отличается. Проблема в том, что c_str() вызывается в результате вызова функции. Это делает недействительным внутренний char указатель, который c_str() выдает вам, потому что string объект, который является локальным для вызываемой вами функции, вышел из области видимости. Он также не был скопирован, как если бы у вас было что-то вроде string a = SomeFunction(); ... a.c_str(); . Этого не происходит в коде, на который ссылается, потому что c_str() не вызывается напрямую в результате вызова функции.

5. Я чувствую, что вы не очень ясно объяснили это. Пример действительно прошел бы долгий путь. На самом деле, я все еще не до конца уверен, имеете ли вы в виду что-то вроде char* str = new char[...length goes here...]; strcpy(str, SomeFunction().c_str()); .