【问题标题】:C: do all string literals have static storage duration? [duplicate]C:所有字符串文字都有静态存储持续时间吗? [复制]
【发布时间】:2019-06-19 12:35:15
【问题描述】:

我一直在阅读各种来源,字符串文字在程序的整个生命周期内都保留在内存中。那么,这两个函数有什么区别

char *f1() { return "hello"; }
char *f2() {
   char str[] = "hello";
   return str;
}

虽然f1 编译良好,但f2 抱怨我正在返回堆栈分配的数据。这里发生了什么?

  • 如果str 指向实际 字符串文字(具有静态持续时间),为什么会出现错误?
  • 如果将字符串文字复制到局部变量str,那么原始字符串文字会去哪里?它是否保留在内存中而不引用它?

【问题讨论】:

  • char str[] = "hello" 中,"hello" 保持静态,str 包含堆栈上字符串的副本。然后,您返回一个指向堆栈上该副本的指针。
  • (为了过于迂腐,一个字符串字面量就是源代码中引用的字符串。当程序被翻译[编译]时,字符串字面量用于初始化一个静态存储时长的数组。那数组是另一回事;它不是字符串文字,但人们通常将其称为字符串文字。)
  • @EricPostpischil:拜托,学究一点!! char str[] = "hello" 是否将这个静态数组逐个元素地复制到自动数组中?静态的还存在吗?如果是这样,你能指出一个参考,我在规范中找不到它..
  • @blue_note:C 标准将 C 定义为抽象机器,即经过基本步骤的假想计算机。在那个抽象机器中,是的,char str[] = "hello" 中的"hello" 导致创建一个包含 h、e、l、l、o 和空字符的静态存储持续时间数组,并且该数组用于初始化 @987654332 @。编译器不必实现实际的抽象机;他们可以创建任何获得相同结果的程序(与 C 标准定义的相同观察到的行为)。所以他们会优化。
  • @blue_note:再举一个例子,如果char str[] = "a" 出现在函数中,则字符串足够短以至于编译器可能不会将空字符作为静态对象存储在对象模块中。相反,它可能将其实现为指令流中的立即操作数。抽象机有一个字符串字面量,但编译器生成的优化代码不需要。

标签: c arrays initialization implicit-conversion string-literals


【解决方案1】:

这个

char str[] = "hello";

是由字符串文字"hello"初始化的本地数组的声明。

其实和下面这样声明数组是一样的

char str[] = { 'h', 'e', 'l', 'l', 'o', '\0' };

即数组的自己的内存区域(具有自动存储时长)由字符串字面量初始化。

退出函数后,数组将不再存在。

这就是函数

char *f2() {
   char str[] = "hello";
   return str;
}

尝试返回指向本地字符数组str的第一个元素的指针,该元素具有自动存储持续时间。

至于这个函数定义

char *f1() { return "hello"; }

然后该函数返回一个指针,指向字符串文字"hello" 的第一个字符,该字符确实具有静态存储持续时间。

你可以想象第一个函数定义如下

char literal[] = "hello";
char *f1() { return literal; }

现在比较第一个函数定义和第二个函数定义中数组的定义位置。

在第一个函数定义中,数组literal 是全局定义的,而在第二个函数定义中,数组str 是本地定义的。

如果 str 指向实际的字符串字面量(它具有静态 持续时间),为什么会出现错误?

str 不是指针。它是由字符串文字初始化的命名内存范围。也就是说数组的类型为char[6]

在return语句中

return str;

数组被隐式转换为指向char *类型的第一个元素的指针。

C 和 C++ 中的函数可能不返回数组。在 C++ 中,函数可能会返回对数组的引用。

【讨论】:

    【解决方案2】:

    我一直在阅读各种来源中的字符串文字 程序整个生命周期的内存。

    是的。

    在这种情况下,什么是 这两个函数的区别

    char *f1() { return "hello"; }
    char *f2() {
       char str[] = "hello";
       return str;
    }
    

    f1 返回指向由字符串文字表示的数组的第一个元素的指针,该字符串具有静态存储持续时间。 f2 返回指向 自动 数组 str 的第一个元素的指针。 str 有一个用于初始化的字符串文字,但它是一个单独的对象。

    虽然 f1 编译正常,但 f2 抱怨我正在返回堆栈 分配的数据。这里发生了什么?

    • 如果 str 指向实际的字符串字面量(具有静态持续时间),为什么会出现错误?

    它没有。事实上,它本身并不指向任何东西。它是一个数组,而不是一个指针。

    • 如果将字符串文字复制到局部变量str,那么原始字符串文字会去哪里?它是否保留在内存中没有 参考它?

    C 没有指定,但在实践中,是的,字符串字面量的一些表示必须存储在程序中的某个地方,也许是在函数实现中,因为它需要用于每次重新初始化str@987654327 @ 被调用。

    【讨论】:

    • ">为什么会出现错误?"因为return str; 等价于return &str[0];str[0] 是一个具有自动存储期限的对象。所以f2 返回一个指向具有自动存储持续时间的对象的指针。返回的指针对于f2的任何调用者都是无效的。
    【解决方案3】:

    您将在堆栈中看到的字符串不是字符串文字存在的直接结果。对于ELF,该字符串与链接器在链接过程中遇到的其他字符串文字一起存储在可执行二进制文件的一个单独区域中,称为“字符串表部分”。每当实例化实际导致包含字符串的代码的堆栈上下文时,字符串表部分中的字符串内容实际上被复制到堆栈中

    您可能感兴趣的简短阅读: http://refspecs.linuxbase.org/elf/gabi4+/ch4.strtab.html

    【讨论】:

      【解决方案4】:

      char str[] = "hello"; 是一种复制字符串文字的特殊语法,您的函数返回一个指向此局部变量的指针,该指针在函数返回后被销毁。
      char *f1() { return "hello"; } 是正确的,但返回 const char* 可能会会更好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-27
        • 2015-04-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-07-28
        相关资源
        最近更新 更多