"время жизни" строкового литерала в 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 71

9 ответов:

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

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

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

const char*p = "string"; 

вместо,

char*p = "string";    

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

пример программы:

#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: предупреждения рассматриваются как ошибки
еда.c: в функции 'main':
еда.c: 9: ошибка: передача аргумента 1 из 'strcpy' отбрасывает квалификаторы из целевого типа указателя

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


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

в чем дело с интегральными литералами?
Другими словами, действителен ли этот код:

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

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

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

строковые литералы-это l-значения, т. е. вы можете взять адрес строкового литерала, но не можете изменить его содержимое.
Однако, любые другие литералы(int,float,char etc) являются R-значениями(стандарт c использует термин значение выражения для них) и их адрес не может быть взят вообще.


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

в фазе перевода 7 байт или код нулевого значения добавляется к каждой многобайтовой последовательности символов, которая является результатом строкового литерала или литералов. многобайтовая последовательность символов затем используется для инициализации массива статической длительности хранения и длины, достаточной только для того, чтобы содержать последовательность. Для символьной строки литералы, элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой символьной последовательности; для широких строковых литералов элементы массива имеют тип wchar_t и инициализируются последовательностью широких символов...

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

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

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

в фазе перевода 7 байт или код нулевого значения добавляется к каждой многобайтовой последовательности символов, которая является результатом строкового литерала или литералов. Затем используется многобайтовая последовательность символов для инициализации массива статической длительности хранения и длина как раз достаточная чтобы содержать последовательность.

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

8 обычных строковых литералов и строковые литералы UTF-8 также называются узкими строковыми литералами. Узкий строковый литерал имеет тип " массив n const char", где N-размер строки, как определено ниже, и имеет статическую длительность хранения (3.7).

9 строковый литерал, который начинается с U, например u"asdf", это char16_t строковый литерал. Один char16_t строковый литерал имеет тип " массив n const char16_t", где N-размер строки, как определено ниже; он имеет статическую длительность хранения и инициализируется с данными персонажами. Один c-char может производить более одного char16_t символ в виде суррогатной пары.

10 строковый литерал, который начинается с U, например U"asdf", это char32_t строковый литерал. А char32_t строковый литерал имеет тип " массив n const char32_t", где N-размер строки, как определено ниже; он имеет статическую длительность хранения и инициализируется заданными символами.

11 строковый литерал, который начинается с L, например L"asdf", является широким строковым литералом. Широкий строковый литерал имеет тип " массив n const wchar_t", где N-размер строки, как определено ниже; он имеет статическую длительность хранения и инициализируется с данными персонажами.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Спасибо,

Вихарри П Л В.