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的地址,但不能更改其内容。
但是,任何其他文字( intfloatchar等)都是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_tstring文字。 char16_tstring常量的types为“ const char16_t数组”,其中n是下面定义的string的大小; 它具有静态存储持续时间,并用给定的字符进行初始化。 一个c-char可能以代理对的forms产生多个char16_t字符。

10以U开头的string文字,例如U"asdf" ,是char32_tstring文字。 char32_tstring文字的types为“n const char32_t数组”,其中n是下面定义的string的大小; 它具有静态存储持续时间,并用给定的字符进行初始化。

11以L开头的string文字,例如L"asdf" ,是一个宽string文字。 宽string文字的types为“n const 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 addressstring文字所在的Because it points to static address 。 当它被加载时,它对于程序来说将是大部分readonlyglobal的。 即使您尝试释放或修改,也会在具有内存保护的平台上引发segmentation fault

一个局部variables被分配在堆栈上。 该函数完成后,variables超出范围,不再在代码中可访问。 但是,如果你有一个全局指针(或者简单地说,还没有超出范围),你指定的指针指向该variables,它将指向该variables所在的堆栈中的位置。 它可能是另一个函数使用的值,或者是一个无意义的值。

在上面的例子中,你实际上是将分配的指针返回到调用上面的任何函数。 所以它不会成为本地指针。 此外,需要返回的指针,内存分配在全局段。

感谢您,

Viharri PL V.