函数返回的字符串文字的生命周期是多少?

问题描述 投票:0回答:5

考虑这段代码:

const char* someFun() {
    // ... some stuff
    return "Some text!!"
}

int main()
{
   { // Block: A
      const char* retStr = someFun();
      // use retStr
   }
}

在函数

someFun()
中,
"Some text!!"
存储在哪里(我认为它可能在ROM的某个静态区域中)以及它的scope生命周期是多少?

retStr
指向的内存会在整个程序中被占用还是一旦A块退出就被释放?

c++ c lifetime string-literals
5个回答
53
投票

C++ 标准没有规定字符串文字应该存储在哪里。然而,它确实保证它们的生命周期就是程序的生命周期。因此您的代码是有效的。


35
投票

"Some text!!"
没有范围Scopenamed 实体的属性。更准确地说,它是名称本身的属性。
"Some text!!"
是一个 nameless 对象 - 字符串文字。它没有名称,因此任何关于其“范围”的讨论都没有任何意义。它没有范围。

您似乎要问的不是范围。它是"Some text!!"生命周期

存储持续时间
。 C/C++ 中的字符串文字具有静态存储持续时间,这意味着它们“永远”存在,即只要程序运行。所以,
"Some text!!"
占用的内存永远不会被释放。

请记住(作为旁注)字符串文字是不可修改的对象。写入该内存是非法的。


5
投票

字符串将静态存储在程序二进制文件的特殊部分(在现代操作系统上通常是只读的)。它的内存未分配(单独为字符串,仅在将其加载到内存时为总部分分配)并且不会被释放。


0
投票

我发布了类似问题的答案,所以我也将在这里使用该示例:

简短的答案是字符串文字

message2
将存在于内存中 只要进程执行,但在 .rodata 部分(假设我们正在讨论 ELF 文件)。

我们返回一个指向字符串常量的指针,但正如我们稍后将看到的,没有在任何地方定义单独的内存来存储这个

const char *
指针, 没有必要,因为字符串的地址是在代码中计算的,并在每次调用函数时使用寄存器
$rax
返回。

但是让我们看一下代码中 GNU 调试器 (GDB) 发生了什么:

enter image description here

我们在返回指向常量字符串的指针的函数中放置了一个断点,我们看到了汇编代码和流程图:

enter image description here

代码通过以下指令获取该字符串:

0x000055555555514a <+8>:    lea    0xeb3(%rip),%rax        # 0x555555556004

这条指令的作用是计算

message2
的地址。 我们在这里了解位置无关代码 (PIC) 的含义。

message2
字符串的地址不是硬编码为绝对地址,而是计算为相对地址,作为下一条指令地址(0x555555555151 + 0xeb3)的硬编码偏移0xeb3并放入寄存器
rax
中。

相对寻址(当前地址+/-偏移量)的目的意味着进程将始终获得正确的地址

message2
,无论它加载到内存中的哪个位置。

所以在这里我们看到您要求的

const char *
实际上并不存在于内存中,因为地址是“动态”计算并使用
$rax
返回的:

我们的地址在

$rax
:

(gdb) i r $rax
rax   0x555555556004      93824992239620

它保存着

message2
的地址:

(gdb) x/s 0x555555556004
0x555555556004: "message2"

现在让我们看看地址

0x555555556004
在进程地址映射中的位置:

0x555555556000     0x555555557000     0x1000     0x2000  r--p   /home/drazen/proba/main

因此该部分不可执行且不可写,只是可读且私有(r--p),这是有道理的,因为这不是共享库。

当我们使用

readelf
检查时,它显示它位于 ELF 文件的 .rodata 部分中:

drazen@HP-ProBook-640G1:~/proba$ readelf  -x .rodata main

Hex dump of section '.rodata':
0x00002000 01000200 6d657373 61676532 00       ....message2.

所以答案是,这个字符串不会被硬编码在 ELF 文件的代码段.text、只读数据段.rodata中,但是只要进程存在于内存中,它就会存在。

添加一个小细节,这个常量字符串将通过引用返回到 main() 函数,当然是(地址),但不是在堆栈上;而是在寄存器中

rax

(gdb) i r
rax   0x555555556004      93824992239620
rbx   0x0 

-4
投票

retStr指向的内存会在整个程序中被占用还是一旦A块退出就被释放?

它将发布,但

retStr
将不可用。 (块范围)

const char *ptr;
{   
   const char* retStr = "Scope";
   ptr = retStr;
}   

printf("%s\n", ptr); //prints "Scope"

//printf("%s\n", retStr); //will throw error "retStr undeclared"
© www.soinside.com 2019 - 2024. All rights reserved.