C语言中字符串文字的"生命周期"

use*_*454 79 c function local-variables lifetime string-literals

以下函数返回的指针不会无法访问吗?

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

所以C/C++中局部变量的生命周期实际上只在函数内,对吧?这意味着,在char* foo(int)终止后,它返回的指针不再意味着什么?

我对本地var的生命周期有点困惑.谁能给我一个很好的澄清?

Alo*_*ave 81

是的,局部变量的生命周期在创建它的范围({,})内.
局部变量具有自动或本地存储.
自动,因为一旦创建它们的范围结束,它们就会自动销毁.

但是,这里有一个字符串文字,它在实现定义的只读内存中分配.字符串文字与局部变量不同,它们在整个程序生命周期内保持活动状态.它们具有静态持续时间 [Ref 1]的生命周期.

一句谨慎!
但是,请注意,任何修改字符串文字内容的尝试都是未定义的行为.不允许用户程序修改字符串文字的内容.
因此,总是鼓励使用constwhile声明字符串文字.

const char*p = "string"; 
Run Code Online (Sandbox Code Playgroud)

代替,

char*p = "string";    
Run Code Online (Sandbox Code Playgroud)

实际上,在C++中,不推荐使用字符串文字而不使用constc.但是,使用a声明字符串文字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; 
} 
Run Code Online (Sandbox Code Playgroud)

输出:

cc1:警告被视为错误
prog.c:在函数'main'中:
prog.c:9:错误:传递'strcpy'的参数1从指针目标类型中丢弃限定符

请注意,编译器会警告第二种情况,但不会警告第一种情况.


编辑:回答一些用户在问这个问题:

积分文字的处理是什么?
换句话说,此代码有效:

int *foo()
{
    return &(2);
} 
Run Code Online (Sandbox Code Playgroud)

答案是,没有这个代码无效,它是不正确的,会给编译器错误.
就像是:

prog.c:3: error: lvalue required as unary ‘&’ operand
Run Code Online (Sandbox Code Playgroud)

字符串文字是l值,即:您可以获取字符串文字的地址但不能更改其内容.
然而,任何其它文本(int,float,char等)是r值(C标准使用术语的表达式的值对这些)其地址不能在所有服用.


[参考1] C99标准6.4.5/5"字符串文字 - 语义":

在转换阶段7中,将值为零的字节或代码附加到由字符串文字或文字产生的每个多字节字符序列.然后使用多字节字符序列初始化静态存储持续时间和长度的数组,该数组足以包含序列.对于字符串文字,数组元素的类型为char,并使用多字节字符序列的各个字节进行初始化; 对于宽字符串文字,数组元素的类型为wchar_t,并使用宽字符序列进行初始化...

如果这些数组的元素具有适当的值,则这些数组是否不同是未指定的.如果程序试图修改此类数组,则行为未定义.

  • @celtschk:我很乐意,但问题是专门关于字符串文字的,所以我会坚持手头的主题。但是,对于那些对我的答案感兴趣的人,[char a\[\] = “string 之间有什么区别? ” 和 char *p = “string”?](http://stackoverflow.com/a/9631091/452307) 应该相当有帮助。 (2认同)

Dan*_*her 75

它是有效的,字符串文字有静态存储持续时间,所以指针不是悬空.

对于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是字符串的大小,如下所示; 它具有静态存储持续时间,并使用给定的字符进行初始化.


asa*_*elr 14

字符串文字对整个程序有效(并且不分配给堆栈),因此它是有效的.

此外,字符串文字是只读的,所以(为了好的风格)也许你应该foo改为const char *foo(int)


hyd*_*yde 7

是的,这是有效的代码,下面的案例1.您至少可以通过以下方式安全地从函数返回C字符串:

  • const char*到字符串文字.无法修改,不得被调用者释放.由于下面描述的释放问题,很少用于返回默认值.如果你真的需要在某处传递函数指针,那么可能有意义,所以你需要一个函数返回一个字符串..

  • char*const char*静态字符缓冲区.不得被来电者释放.可以修改(通过调用者,如果不是const,或通过返回它的函数),但返回此函数不能(轻松)有多个缓冲区,因此不(轻松)线程安全,调用者可能需要复制返回再次调用函数之前的值.

  • char*分配给的缓冲区malloc.可以修改,但通常必须由调用者显式释放,并具有堆分配开销.strdup属于这种类型.

  • const char*或者char*作为参数传递给函数的缓冲区(返回的指针不需要指向参数缓冲区的第一个元素).将缓冲区/内存管理的责任留给调用者.许多标准字符串函数都属于这种类型.

一个问题是,将这些功能混合在一个功能中会变得复杂.调用者需要知道它应该如何处理返回的指针,它有效的时间,以及调用者是否应该释放它,并且没有(好的)方法在运行时确定它.因此,您不能拥有一个函数,它有时会返回一个指向调用者需要的堆分配缓冲区free的指针,有时还会指向一个指向字符串文字的默认值的指针,调用者不能这样做 free.


thb*_*thb 6

好问题.一般来说,你是对的,但你的例子是例外.编译器静态为字符串文字分配全局内存.因此,函数返回的地址有效.

这是C的一个相当方便的功能,不是吗?它允许函数返回预先组合的消息,而不必强迫程序员担心存储消息的内存.

另见@ asaelr的正确观察结果const.