2012-04-02 3 views
68

Не будет ли указатель, возвращаемый следующей функцией, недоступным?«срок службы» строкового литерала в C

char *foo(int rc) 
{ 
    switch (rc) 
    { 
     case 1:   return("one"); 
     case 2:   return("two"); 
     default:   return("whatever"); 
    } 
} 

Таким образом, время жизни локальной переменной в C/C++ практически только внутри функции, правильно? Что означает, что после завершения char* foo(int) указатель, который он возвращает, больше ничего не значит?

Я немного смущен о жизни местного var. Может ли кто-нибудь дать мне хорошее разъяснение?

+9

Единственный «var», который у вас есть в вашей функции, - это параметр 'int rc'. Его срок службы заканчивается на каждом из «возвратов». Указатели, которые вы возвращаете, являются строковыми литералами. Строковые литералы имеют статическую продолжительность хранения: их срок службы по крайней мере такой же продолжительный, как и у программы. – Kaz

+0

Что делать, если это не строковый литерал, а другие литералы, такие как: int * foo() {return &(2); // или // int n = 2; // return & n; } –

+0

Отметьте Edit в моем ответе, который обращается к вашему запросу. Обратите внимание, что вы должны были разместить это как комментарий, а не ответ. В комментариях должны быть опубликованы комментарии, сомнения и т. Д., Ответы должны быть опубликованы только в ответах. –

ответ

75

Да, время жизни локальной переменной находится в пределах области ({, }), в которой она создана.
Локальные переменные имеют автоматическое или локальное хранилище.
Automatic потому что они автоматически уничтожаются после того, как область действия, в которой они созданы, заканчивается.

Однако у вас есть строковый литерал, который выделяется в определенной для реализации памяти только для чтения. Строковые литералы отличаются от локальных переменных, и они остаются в живых на протяжении всей жизни программы. Они имеют статическая продолжительность[Ref 1] lifetime.

Осторожно!
Однако обратите внимание, что любая попытка изменить содержимое строкового литерала - это неопределенное поведение. Пользовательским программам не разрешается изменять содержимое строкового литерала.
Следовательно, всегда рекомендуется использовать const при объявлении строкового литерала.

const char*p = "string"; 

вместо того,

char*p = "string";  

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

Sample program:

#include<string.h> 
int main() 
{ 
    char *str1 = "string Literal"; 
    const char *str2 = "string Literal"; 
    char source[]="Sample string"; 

    strcpy(str1,source); //No warning or error just Uundefined Behavior 
    strcpy(str2,source); //Compiler issues a warning 

    return 0; 
} 

Выход:

cc1: warnings being treated as errors
prog.c: In function ‘main’:
prog.c:9: error: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type

Обратите внимание, что компилятор предупреждает для второго случая, но не для первой.


EDIT: Для того, чтобы ответить на Q попросили парой пользователей здесь:

Что такое сделка с целыми литералов?
Других слов этот код действителен:

int *foo() 
{ 
    return &(2); 
} 

Ответ, Нет, это не правильный код, он плохо сформированный & выдаст ошибку компилятора.
Что-то вроде:

prog.c:3: error: lvalue required as unary ‘&’ operand 

Строковые литералы л-значения, то есть: Вы можете взять адрес в строку буквального, но не может изменить его содержание.
Тем не менее, любые другие литералами (int, float, char и т.д.) являются г-значением (C стандарт использует термин значение выражения для них) & их адреса не может быть принят на всех.


[Ссылка 1]C99 стандарт 6.4.5/5 "Строковые литералы - Семантика":

+0

Что делать, если пользователь возвращает что-то вроде этого. char * a = &"abc"; return a; Будет ли это недействительным? – Ashwin

+0

@Ashwin: Тип строкового литерала - 'char (*) [4]'. Это связано с тем, что тип * "abc" * является 'char [4]' и указатель на массив из 4 символов объявляется как 'char (*) [4]', поэтому, если вам нужно принять его адрес, вы нужно сделать это как 'char (* a) [4] = &"abc";' и да, это действительно так. –

+0

@Als "abc" является 'char [4]'. (Из-за '' \ 0'') – asaelr

6

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

Что это такое довольно удобная функция C, не так ли? Он позволяет функции возвращать предварительно скомпонованное сообщение, не заставляя программиста беспокоиться о памяти, в которой хранится сообщение.

См. Также правильное наблюдение @ asaelr re const.

+0

: Что делать, если пользователь возвращает что-то вроде этого. char * a = &"abc"; return a; Будет ли это недействительным? – Ashwin

+0

Справа. Собственно, можно просто написать 'const char * a =" abc ";', опуская '&'. Причина в том, что строка с двумя кавычками разрешает адрес его начального символа. – thb

14

Строковые литералы действительны для всей программы (и не выделены не стеком), поэтому она будет действительна.

Кроме того, строковые литералы только для чтения, так (для хорошего стиля), может быть, вы должны изменить foo к const char *foo(int)

+0

Что делать, если пользователь возвращает что-то вроде этого. char * a = &"abc"; return a; Будет ли это недействительным? – Ashwin

+0

'&" abc "' не 'char *'. это адрес массива, а его тип - 'char (*) [4]'. Однако либо «return &"abc";» и «char * a =« abc »; return a;' действительны. – asaelr

+0

@asaelr: На самом деле, это больше, чем ради * для хорошего стиля *, проверьте мой ответ для деталей. –

3

Локальные переменные действительны только в пределах, они объявлены, однако вы не объявлять любой локальные переменные в этой функции.

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

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

+0

Что делать, если пользователь возвращает что-то вроде этого. char * a = &"abc"; return a; Будет ли это недействительным? – Ashwin

+0

@Ashwin: '&" abc "' не относится к типу 'char *', однако оба '' abc "и' & "abc" 'действительны на протяжении всего выполнения программы. – AusCBloke

0

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

+0

Что делать, если пользователь возвращает что-то вроде этого. char * a = &"abc"; return a; Будет ли это недействительным? – Ashwin

0

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

Благодаря Вам,

Viharri P L V.

74

Это действительно, строковые литералы имеют статическую продолжительность хранения, поэтому указатель не болтались.

Для C, что предусмотрено в разделе 6.4.5, пункт 6:

In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence.

И для C++ в разделе 2.14.5, пункты 8-11:

8 Ordinary string literals and UTF-8 string literals are also referred to as narrow string literals. A narrow string literal has type “array of n const char ”, where n is the size of the string as defined below, and has static storage duration (3.7).

9 A string literal that begins with u, such as u"asdf" , is a char16_t string literal. A char16_t string literal has type “array of n const char16_t ”, where n is the size of the string as defined below; it has static storage duration and is initialized with the given characters. A single c-char may produce more than one char16_t character in the form of surrogate pairs.

10 A string literal that begins with U, such as U"asdf" , is a char32_t string literal. A char32_t string literal has type “array of n const char32_t ”, where n is the size of the string as defined below; it has static storage duration and is initialized with the given characters.

11 A string literal that begins with L, such as L"asdf" , is a wide string literal. A wide string literal has type “array of n const wchar_t ”, where n is the size of the string as defined below; it has static storage duration and is initialized with the given characters.

+0

FYI: этот ответ был объединен с http://stackoverflow.com/questions/16470959/can-we-return-string-literal-in-c – Shog9

5

Да, действительный код, пример 1 ниже. Вы можете безопасно возвращать строки C из функции, по крайней мере, следующими способами:

  • const char* в строковый литерал. Не может быть изменен, не должен быть освобожден вызывающим абонентом. Редко полезен с целью возврата значения по умолчанию из-за проблемы освобождения, описанной ниже. Может иметь смысл, если вам действительно нужно передать указатель функции где-то, поэтому вам нужна функция, возвращающая строку.

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

  • char* в буфер выделен malloc. Может быть изменен, но обычно должен быть явно освобожден вызывающим абонентом и имеет накладные расходы на распределение кучи. strdup имеет этот тип.

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

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

+0

FYI: этот ответ был объединен с http://stackoverflow.com/questions/16470959/can-we-return-string-literal-in-c – Shog9

1

str никогда не будет висящим указателем. Because it points to static address где находятся строковые литералы. Будет главным образом readonly и global программе, когда она будет загружена. Даже если вы попытаетесь освободить или изменить, он будет кидать segmentation faultна платформах с защитой памяти.

+0

FYI: этот ответ был объединен с http://stackoverflow.com/questions/16470959/can-we-return-string-literal-in-c – Shog9

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