Как вернуть строковый литерал из функции - PullRequest
7 голосов
/ 20 марта 2010

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

Например, в приведенном ниже коде, как реализовать foo(), чтобы сделать вывод кода «Hello World»?

void foo (       )              // you can add parameters here.
{

}

int main ()
{
    char *c;
    foo (    );
    printf ("%s",c);
    return 0;
}

Кроме того, если тип возврата foo() не является недействительным, но вы можете вернуть char*, что это должно быть?

Ответы [ 4 ]

10 голосов
/ 20 марта 2010

Я предполагаю, что мы не можем изменить main. Чтобы ваша программа работала без утечек, вам нужно что-то, чтобы иметь статическое хранилище:

void foo(char*& pC)  // reference
{
    static char theString[] = "thingadongdong";

    pC = theString;
}

Но на самом деле, это не очень обычный код C ++. Вы будете использовать std::string и std::cout, поэтому у вас не будет беспокоиться о памяти:

std::string foo(void)
{
    return "better thingadongdong";
}

int main(void)
{
    // memory management is done
    std::cout << foo() << std::endl;
}

Если вам интересно, нужно ли что-то освобождать вручную, это делается неправильно.

5 голосов
/ 16 июня 2012

Поскольку устаревшее использование char * устарело, не можете ли вы просто использовать строку?

const char* func1 () {return "string literal";}

string func2 () {return "another string literal";}

Оба они работают нормально, без предупреждений компилятора.

Однако

char* func3 () {return "yet another string literal";}

не будет компилироваться вообще. И не будет

char* func4 () {return &"a ref to a string literal?";}

Страуструп говорит в «Языке программирования C ++» (третье издание):

"Строковый литерал размещен статически, чтобы можно было безопасно возвращать его из функции.

const char* error_message (int i)`
{
//...
return "range error";
}

Память, содержащая ошибка диапазона , не исчезнет после вызова error_messages (). "

Таким образом, каждый строковый литерал в программе выделяется в своем небольшом фрагменте памяти, который длится на протяжении всей программы (то есть статически выделяется). Помещение const перед char * позволяет компилятору понять, что вы не собираетесь (и не можете) изменять небольшой объем памяти этого строкового литерала, что может быть опасно, поэтому они позволяют этому назначению скользить, несмотря на преобразование строкового литерала в char * устарела.

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

В любом случае нет утечек памяти: каждый строковый литерал получает свой собственный фрагмент памяти, который очищается при завершении программы; return to const char * возвращает указатель на фрагмент памяти литерала (зная, что вы не можете его изменить); и возврат к строке делает копию в строковом объекте, существующем в коде вызывающего, который очищается вызывающим.

Хотя это кажется немного уродливым в плане обозначений, я предполагаю, что они оставили const char *, чтобы сохранить дешевую альтернативу (без копий).

4 голосов
/ 20 марта 2010

Меня всегда смущает возврат строкового литерала или строки из функции.

Неизменяемая буквальная строка

Насколько я понимаю, вы можете безопасно возвращать строковый литерал напрямую, если возвращаемый тип объявлен как const, чтобы объявить, что строка не предназначена для изменения. Это означает, что вам не нужно беспокоиться о сроке службы строки / утечек памяти.

Изменяемая, не-буквальная строка

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

Это место, где возникает вероятность утечки, и где необходимо сделать выбор относительно того, где должно происходить распределение и перераспределение. Можно было бы, чтобы сама функция выделяла память и указала в документации состояние, в котором это происходит, и указала в нем, что вызывающая сторона должна освободить память, когда она больше не требуется (предотвращая утечку). Это означает, что функция может просто возвращать символ *.

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

Наконец, я упомянул, что при использовании изменяемой строки необходимо управлять размером памяти и строки. Распределение должно быть достаточно большим для строки, изначально установленной функцией, а также для любых изменений, которые сделаны после функции, до освобождения памяти. Невыполнение этого требования может привести к переполнению буфера, если записать строку, которая будет длинной, чтобы поместиться в первоначально выделенной памяти; это чрезвычайно опасно для здоровья и безопасности вашей программы. Это может вызвать ошибки и дыры в безопасности, которые чрезвычайно трудно обнаружить (поскольку источник ошибки - переполнение - может быть далек от симптомов, наблюдаемых при сбое программы).

2 голосов
/ 20 марта 2010

Примерно так:

void foo(char ** pChar)
{
    // Make sure the string is shorter
    // than the buffer
    *pChar = new char[256];
    strcpy(*pChar,"Hello World!");
}

Тогда назовите это так:

foo(&c);

Как упомянуто в комментарии, будьте осторожны, сохраняемая строка меньше буфера, иначе вы получите ... переполнение стека! (Пун предназначен)

...