C中string字面值的“生命期”
下面的函数不能返回指针吗?
char *foo( int rc ) { switch (rc) { case 1: return("one"); case 2: return("two"); default: return("whatever"); } }
所以C / C ++中的局部variables的生命周期实际上只在函数内部,对吗? 这意味着,在char* foo(int)
终止后,它返回的指针不再意味着什么?
我对本地变种的生存期有点困惑。 谁能给我一个很好的说明?
是的,局部variables的生命周期在其创build的范围( {
, }
)内。
局部variables有自动或本地存储。
自动,因为它们在创build它们的范围结束后自动销毁。
然而,你在这里是一个string文字,它是在一个实现定义的只读存储器中分配的。 string文字与局部variables不同,它们在整个程序生命周期中保持活动。它们具有静态持续时间 [Ref 1]的生命周期。
一个谨慎的话!
但是请注意,任何修改string文字内容的尝试都是未定义的行为。 用户程序不允许修改string文字的内容。
因此,鼓励在声明一个string时使用const
。
const char*p = "string";
代替,
char*p = "string";
事实上,在C ++中,不build议使用const
来声明一个string,尽pipe不在c中。 但是,使用const
声明一个string字面值会给你带来的好处,即如果你试图在第二种情况下修改string,编译器通常会给你一个警告。
示例程序 :
#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:警告被视为错误
prog.c:在函数'main'中:
prog.c:9:错误:传递'strcpy'的参数1丢弃指针目标types的限定符
注意编译器警告第二种情况,但不是第一种情况。
编辑:要回答几个用户在这里问:
积分文字的处理是什么?
换句话说这个代码是有效的:
int *foo() { return &(2); }
答案是,没有这个代码是无效的,它是不合格的,会给编译器错误。
就像是:
prog.c:3: error: lvalue required as unary '&' operand
string文字是l值,即:您可以获取string的地址,但不能更改其内容。
但是,任何其他文字( int
, float
, char
等)都是r值(c标准使用术语expression式的值 ),并且它们的地址根本不可用。
[参考1] C99标准6.4.5 / 5“string文字 – 语义”:
在翻译阶段7,一个字节或值为零的代码被附加到每个多字节字符序列,这是由一个或多个string产生的。 然后使用多字节字符序列来初始化静态存储持续时间和长度的数组,以便足以包含该序列 。 对于string文字,数组元素的types为char,并且用多字节字符序列的单个字节进行初始化; 对于宽string文字,数组元素的types为wchar_t,并用宽字符序列初始化…
没有说明这些数组是否是不同的,只要它们的元素具有适当的值。 如果程序试图修改这样一个数组,行为是不确定的 。
这是有效的,string文字具有静态存储持续时间,所以指针不悬摆。
对于C第6.4.5节第6段的规定:
在翻译阶段7,一个字节或值为零的代码被附加到每个多字节字符序列,这是由一个或多个string产生的。 然后使用多字节字符序列来初始化静态存储持续时间和长度的数组,以便足以包含该序列。
对于第2.14.5节第8-11段中的C ++:
8普通string文字和UTF-8string文字也被称为窄string文字。 窄string常量的types为“n
const char
数组”,其中n是下面定义的string的大小,并具有静态存储持续时间(3.7)。9以u开头的string文字,例如
u"asdf"
,是char16_t
string文字。char16_t
string常量的types为“const char16_t
数组”,其中n是下面定义的string的大小; 它具有静态存储持续时间,并用给定的字符进行初始化。 一个c-char可能以代理对的forms产生多个char16_t
字符。10以U开头的string文字,例如
U"asdf"
,是char32_t
string文字。char32_t
string文字的types为“nconst char32_t
数组”,其中n是下面定义的string的大小; 它具有静态存储持续时间,并用给定的字符进行初始化。11以L开头的string文字,例如
L"asdf"
,是一个宽string文字。 宽string文字的types为“nconst wchar_t
数组”,其中n是下面定义的string的大小; 它具有静态存储持续时间,并用给定的字符进行初始化。
string文字对于整个程序是有效的(并且不被分配不是堆栈),所以它是有效的。
此外,string文字是只读的,所以(为了好样式)也许你应该把foo
改为const char *foo(int)
好问题。 一般来说,你会是对的,但你的例子是例外。 编译器为string文字静态分配全局内存。 因此,你的函数返回的地址是有效的。
这是C的一个相当方便的function,不是吗? 它允许一个函数返回一个预先分解的消息,而不会强迫程序员担心消息存储的内存。
另请参阅@ asaelr的正确观察重新const
。
是的,这是有效的代码,下面的情况1。 至less在这些方面你可以安全地从函数返回Cstring:
-
const char*
为一个string文字。 不能修改,不能被调用者释放。 由于下面描述的释放问题,对于返回默认值的目的很less有用。 如果你真的需要传递一个函数指针,那么可能是有道理的,所以你需要一个返回string的函数。 -
char*
或const char*
到静态字符缓冲区。 不能被调用者释放。 可以修改(如果不是通过调用者,或者通过返回它的函数),但是返回这个函数的函数不能(容易地)有多个缓冲区,所以不是(容易)线程安全的,调用者可能需要复制返回的再次调用函数之前的值。 -
char*
分配给malloc
的缓冲区。 可以被修改,但通常必须被调用者明确地释放,并且具有堆分配开销。strdup
是这种types的。 -
const char*
或char*
作为parameter passing给函数的缓冲区(返回的指针不需要指向参数缓冲区的第一个元素)。 将缓冲区/内存pipe理的责任留给呼叫者。 许多标准的string函数都是这种types的。
一个问题是,将这些function混合在一起可能会变得复杂。 调用者需要知道它应该如何处理返回的指针,它的有效时间以及调用者是否应该释放它,并且在运行时没有(很好的)方法来确定。 所以你不能有一个函数,它有时会返回一个指向调用者需要free
的堆分配缓冲区的指针,有时还会有一个指针指向string文字中的默认值,该调用者不能 free
。
局部variables只在声明的范围内有效,但是不要在该函数中声明任何局部variables。
从函数返回指向string的指针是完全有效的,因为string文字在整个程序执行过程中都存在,就像static
或全局variables一样。
如果你担心你所做的事情可能是无效的,你应该打开你的编译器警告,看看是否有任何事情做错了。
str永远不会是悬摆指针。 Because it points to static address
string文字所在的Because it points to static address
。 当它被加载时,它对于程序来说将是大部分readonly
和global
的。 即使您尝试释放或修改,也会在具有内存保护的平台上引发segmentation fault
。
一个局部variables被分配在堆栈上。 该函数完成后,variables超出范围,不再在代码中可访问。 但是,如果你有一个全局指针(或者简单地说,还没有超出范围),你指定的指针指向该variables,它将指向该variables所在的堆栈中的位置。 它可能是另一个函数使用的值,或者是一个无意义的值。
在上面的例子中,你实际上是将分配的指针返回到调用上面的任何函数。 所以它不会成为本地指针。 此外,需要返回的指针,内存分配在全局段。
感谢您,
Viharri PL V.