Разве указатель, возвращаемый следующей функцией, не будет недоступен?
char *foo(int rc)
{
switch (rc)
{
case 1:
return("one");
case 2:
return("two");
default:
return("whatever");
}
}
Значит, время жизни локальной переменной в C / C ++ находится практически только внутри функции, верно? Это означает, что после char* foo(int)
завершения указатель, который он возвращает, больше ничего не значит, верно?
Меня немного смущает время жизни локальной переменной. Что такое хорошее разъяснение?
c
function
local-variables
string-literals
lifetime
user113454
источник
источник
int rc
. Его время жизни заканчивается при каждой изreturn
-х. Указатели, которые вы возвращаете, относятся к строковым литералам. Строковые литералы имеют статическую продолжительность хранения: их время жизни не меньше времени жизни программы.strerror
Очевидно, вы никогда не видели эту функцию.Ответы:
Да, время жизни локальной переменной находится в области (
{
,}
), в которой она создана.Локальные переменные имеют автоматическое или локальное хранилище. Автоматически, потому что они автоматически уничтожаются, когда заканчивается область, в которой они созданы.
Однако здесь имеется строковый литерал, который выделяется в памяти только для чтения, определенной в реализации. Строковые литералы отличаются от локальных переменных и остаются активными на протяжении всего времени существования программы. Они имеют статическую продолжительность [Ref 1] срока службы.
Предупреждение!
Однако обратите внимание, что любая попытка изменить содержимое строкового литерала является неопределенным поведением (UB). Пользовательским программам не разрешается изменять содержимое строкового литерала.
Следовательно, всегда рекомендуется использовать
const
при объявлении строкового литерала.const char*p = "string";
вместо,
char*p = "string";
Фактически, в C ++ не рекомендуется объявлять строковый литерал без символа,
const
хотя и не в C. Однако объявление строкового литерала с aconst
дает вам преимущество, заключающееся в том, что компиляторы обычно выдают предупреждение, если вы попытаетесь изменить строковый литерал в второй случай.Пример программы :
#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; }
Вывод:
Обратите внимание, что компилятор предупреждает о втором случае, но не о первом.
Чтобы ответить на вопрос, который задают здесь несколько пользователей:
В чем дело с целочисленными литералами?
Другими словами, действителен ли следующий код?
int *foo() { return &(2); }
Ответ: нет, этот код недействителен. Он неправильно сформирован и приведет к ошибке компилятора.
Что-то вроде:
prog.c:3: error: lvalue required as unary ‘&’ operand
Строковые литералы являются l-значениями, то есть: вы можете взять адрес строкового литерала, но не можете изменить его содержимое.
Тем не менее, любые другие литералов (
int
,float
,char
, и т.д.) являются г-значения (стандарт С использует термин значение выражения для них) и их адреса не могут быть приняты на всех.[Ссылка 1] Стандарт C99 6.4.5 / 5 «Строковые литералы - семантика»:
источник
char (*)[4]
. Это связано с тем, что тип «abc» равен,char[4]
а указатель на массив из 4 символов объявлен какchar (*)[4]
, Итак, если вам нужно взять его адрес, вам нужно сделать это какchar (*a)[4] = &"abc";
и Да, он действителен.char[4]
. (Из-за'\0'
)char const s[] = "text";
это не делаетs
буквенный символ, и , следовательно ,s
будет уничтожено в конце области, поэтому все выжившие указатели на него будут свисать.Это действительно так. Строковые литералы имеют статическую продолжительность хранения, поэтому указатель не болтается.
Для C это предусмотрено в разделе 6.4.5, параграф 6:
А для C ++ в разделе 2.14.5, параграфы 8-11:
источник
Строковые литералы действительны для всей программы (и не выделяются не стеку), поэтому они будут действительны.
Кроме того, строковые литералы доступны только для чтения, поэтому (для хорошего стиля), возможно, вам следует изменить
foo
наconst char *foo(int)
источник
&"abc"
нетchar*
. это адрес массива, и его типchar(*)[4]
. Однако либоreturn &"abc";
иchar *a="abc";return a;
действительны.const
, и это будет полностью законно, но все равно плохой стиль.Да, это действительный код, см. Случай 1 ниже. Вы можете безопасно возвращать строки C из функции, по крайней мере, следующими способами:
const char*
в строковый литерал. Его нельзя изменить и не должен освобождать вызывающий абонент. Это редко бывает полезно с целью возврата значения по умолчанию из-за проблемы с освобождением, описанной ниже. Это может иметь смысл, если вам действительно нужно где-то передать указатель на функцию, поэтому вам нужна функция, возвращающая строку.char*
илиconst char*
в буфер статических символов. Вызывающий абонент не должен освобождать его. Его можно изменить (либо вызывающим, если не const, либо функцией, возвращающей его), но функция, возвращающая это, не может (легко) иметь несколько буферов, поэтому она (легко) не является потокобезопасной, и вызывающей стороне может потребоваться чтобы скопировать возвращаемое значение перед повторным вызовом функции.char*
в буфер, выделенный с помощьюmalloc
. Его можно изменить, но обычно он должен быть явно освобожден вызывающей стороной и имеет накладные расходы на выделение кучи.strdup
относится к этому типу.const char*
илиchar*
в буфер, который был передан в качестве аргумента функции (возвращаемый указатель не должен указывать на первый элемент буфера аргументов). Он оставляет ответственность за управление буфером / памятью вызывающей стороне. Многие стандартные строковые функции относятся к этому типу.Одна из проблем заключается в том, что их смешивание в одной функции может быть сложным. Вызывающий должен знать, как он должен обрабатывать возвращаемый указатель, как долго он действителен, и должен ли вызывающий объект освободить его, и нет (хорошего) способа определить это во время выполнения. Таким образом, вы не можете, например, иметь функцию, которая иногда возвращает указатель на буфер, выделенный кучей, который необходим вызывающему
free
, а иногда указатель на значение по умолчанию из строкового литерала, который вызывающий не долженfree
.источник
Хороший вопрос. В общем, вы были бы правы, но ваш пример - исключение. Компилятор статически выделяет глобальную память для строкового литерала. Следовательно, адрес, возвращаемый вашей функцией, действителен.
То, что это так, - довольно удобная особенность C, не так ли? Это позволяет функции возвращать предварительно составленное сообщение, не заставляя программиста беспокоиться о памяти, в которой хранится сообщение.
См. Также правильное наблюдение @ asaelr
const
.источник
const char *a = "abc";
, опуская&
. Причина в том, что строка в двойных кавычках преобразуется в адрес своего начального символа.Локальные переменные действительны только в той области, в которой они объявлены, однако вы не объявляете никаких локальных переменных в этой функции.
Совершенно верно возвращать указатель на строковый литерал из функции, поскольку строковый литерал существует на протяжении всего выполнения программы, как
static
и глобальная переменная или.Если вы беспокоитесь о том, что то, что вы делаете, может быть недопустимым undefined, вам следует включить предупреждения компилятора, чтобы увидеть, действительно ли что-то вы делаете неправильно.
источник
&"abc"
не относится к типуchar*
, однако , как"abc"
и&"abc"
действуют на протяжении всего выполнения программы.str
никогда не будет висячим указателем, поскольку он указывает на статический адрес, в котором находятся строковые литералы.Это будет в основном Когда он будет загружен, доступен только для чтения и будет глобальным для программы.
Даже если вы попытаетесь освободить или изменить, это вызовет ошибку сегментации на платформах с защитой памяти .
источник
В стеке размещается локальная переменная. После завершения функции переменная выходит за пределы области видимости и больше не доступна в коде. Однако, если у вас есть глобальный (или просто - еще не вышедший из области видимости) указатель, который вы назначили для указания на эту переменную, он будет указывать на то место в стеке, где находилась эта переменная. Это может быть значение, используемое другой функцией, или бессмысленное значение.
источник
В показанном выше примере вы фактически возвращаете выделенные указатели на любую функцию, которая вызывает указанное выше. Таким образом, он не стал бы локальным указателем. Причем для указателей, которые необходимо вернуть, память выделяется в глобальном сегменте.
источник