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

时间:2012-04-02 02:39:37

标签: c function local-variables string-literals lifetime

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

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

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

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

9 个答案:

答案 0 :(得分:79)

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

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

谨慎提醒!
但是,请注意,任何修改字符串文字内容的尝试都是未定义的行为。 不允许用户程序修改字符串文字的内容 因此,始终鼓励在声明字符串文字时使用const

const char*p = "string"; 

代替,

char*p = "string";    

事实上,在C ++中,不推荐使用const而不是c来声明字符串文字。但是,使用const声明字符串文字可以为您提供以下优势:如果您尝试在第二种情况下修改字符串文字,编译器通常会给您一个警告。

Sample program

#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会丢弃指针目标类型

中的限定符

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


编辑:回答几位用户提出的问题:

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

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

答案是,没有这个代码是无效的,它是不正确的&amp;会给编译错误 类似的东西:

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

字符串文字是l值,即:您可以获取字符串文字的地址,但不能更改其内容。
但是,任何其他文字(intfloatchar等)都是r值(c标准使用术语表达式的值) &安培;他们的地址根本无法获取。


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

  

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

     

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

答案 1 :(得分:74)

它是有效的,字符串文字具有静态存储持续时间,因此指针不是悬空。

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

答案 2 :(得分:14)

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

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

答案 3 :(得分:6)

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

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

另见@ asaelr的正确观察重新const

答案 4 :(得分:6)

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

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

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

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

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

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

答案 5 :(得分:3)

局部变量仅在声明的范围内有效,但是您不在该函数中声明任何局部变量。

从函数返回指向字符串文字的指针是完全有效的,因为字符串文字在整个程序执行过程中都存在,就像static或全局变量一样。

如果你担心你正在做的事情可能是无效的未定义,你应该打开你的编译器警告,看看是否有任何你做错了。

答案 6 :(得分:1)

str永远不会晃来晃去指针。 字符串文字所在的Because it points to static address。 当程序加载时,它将主要是readonlyglobal。 即使您尝试免费或修改,它也会在具有内存保护的平台上抛出segmentation fault

答案 7 :(得分:0)

在堆栈上分配局部变量。函数完成后,变量超出范围,并且在代码中不再可访问。但是,如果您指定了指向该变量的全局(或简单 - 尚未超出范围)指针,它将指向该变量所在的堆栈中的位置。它可能是另一个函数使用的值,也可能是无意义的值。

答案 8 :(得分:0)

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

感谢你,

Viharri P L V。