2010-03-20 2 views
7

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

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

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

} 

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

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

+0

Ваше требование, чтобы оно возвращало 'char *' вместо 'char const *', фактически заставляет нас предположить, что простой литерал строки не будет достаточным и, следовательно, «вернуть» мир привет »,' является недопустимым решением. Каковы ваши требования к константе? –

ответ

9

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

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; 
} 

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

+0

Не всегда. В конце концов, std :: string выполняет выделение вручную. –

+1

Э-э, когда вы не находитесь в режиме записи в библиотеке, я должен сказать. :) – GManNickG

+0

Там мы идем. Намного лучше. Мое мнение точно. –

2

Что-то вроде этого:

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

Затем вызовите его следующим образом:

foo(&c); 

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

+0

И если я делаю 'foo (0)'? – GManNickG

+1

Вы не можете разыгрывать «0». Это приведет к сбою вашей программы. –

+0

зачем нужен указатель на указатель? Почему не просто указатель? – skydoor

4

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

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

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

Mutable, небуквальная строка

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

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

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

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

5

Поскольку устаревшее использование 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 *, чтобы сохранить дешевую альтернативу (без каких-либо копий).

Смежные вопросы