我知道以下代码将创建一个字符数组并保留在内存中,直到程序结束:
char* str = "this is a string";
对于这个语句,创建一个本地字符数组,当str超出范围时将被释放:
char str[] = "this is a string";
我很好奇的是,当我这样写时会发生什么:
std::string str = "this is a string";
str应该在它自己的内存(本地)中复制字符串,但字符串文字本身呢?它是否具有程序的生命周期,或者当str超出范围时是否会被释放?
答案 0 :(得分:7)
当你写这个
std::string str = "this is a string";
C ++应该找到std::string
的构造函数,它接受const char*
,调用它来创建一个临时对象,调用复制构造函数将该临时文件复制到str
,然后销毁临时对象对象
但是,有一个优化允许C ++编译器跳过临时对象的构造和销毁,因此结果与
相同std::string str("this is a string");
但字符串文字本身呢?它是否具有程序的生命周期,或者当str超出范围时是否会被释放?
程序无法访问以这种方式使用的字符串文字本身。通常,C ++将它放在与其他字符串文字相同的段中,使用它传递给std::string
的构造函数,并忘记它。允许优化器消除所有字符串文字中的重复项,包括仅在初始化其他对象时使用的字符串文字。
答案 1 :(得分:4)
大多数操作系统会将程序内存分成几个部分
你已经知道堆栈和堆了,但其他的呢?
代码段以二进制形式保存所有操作。
现在它变得有趣: 让我们看一下以下代码:
int x;
class Y{ static int y; };
int main(){
printf("hello world");
return 0;
}
程序在哪里分配x
和y
?它们不是本地的或动态分配的,所以在哪里?
数据段保留所有静态和全局变量,在加载程序时,这些段保留足够的字节来保存所有静态和全局变量。如果变量是一个对象,当程序运行时它会为所有变量分配足够的字节,包括对象。在main
程序调用每个全局对象构造函数之前,在main
完成之后,它以相反的顺序调用每个对象析构函数,它调用构造函数。
BSS段是数据段的一个子集,它保存全局和静态指针,这些指针是空的。
因此,假设字符串文字未被优化,程序将其存储在数据段中。只要程序存在,它就会继续存在。而且,如果它是一个字符串文字,很可能你甚至可以在exe中看到它!将exe作为文本文件打开。沿途有一点,你会清楚地看到字符串。
现在呢
std::string str = "hello world";
?
这是一个时髦的情况。 str
本身就存在于堆栈中。实际的内部缓冲区存在于堆上,但用于分配字符串的字符串文字存在于数据段中,而使str
值变为hello world
的代码存在于代码段中。不用说,如果我们要在集会中进行计划,我们就需要用自己的双手建立这个生态系统。
答案 2 :(得分:0)
您的一些初步陈述不太正确:
对于char *
和char []
示例,在两种情况下,变量本身str
都保留在范围内,并且如果程序在全局名称空间中声明,则可以访问它
如果在功能或方法的范围内声明,则在范围保持活动状态时可以访问它。他们两个。
对于用于存储实际文字字符串的内存实际发生的情况,这是未指定的。只要结果符合C ++标准,特定的C ++实现就可以以更方便的方式管理运行时内存。就C ++而言,您没有访问str
对象使用的内存,而您只是引用str
对象本身。
当然,您可以自由地使用本地char *
指针,指向str
中的一个字符。但是指针是否有效直接与底层对象的范围相关联。当相应的str
对象超出范围时,指针不再有效,并且访问指针的内容将变为未定义的行为。
请注意,在str
位于全局命名空间的情况下,str
的范围是程序的生命周期,因此这一点没有实际意义。但是当str
在本地范围内,并且它超出范围时,使用指针将变为未定义的行为。底层内存发生的事情是无关紧要的。 C ++标准并没有真正定义底层实现中应该或不应该发生什么内存,但是什么是未定义的行为。
基于此,你几乎可以自己找出std::string
案例的答案。这是一回事。您正在访问std::string
对象,而不是底层内存,并且适用相同的原则。
但请注意,除了作用域问题之外,std::string
对象的一些(但不是全部)方法也被指定为使所有现有的直接指针和迭代器与其内容无效,因此这也会影响是否或者char *
中某个字符的直接std::string
仍然有效。
答案 3 :(得分:0)
我会提出一个反问题:你为什么关心?
C ++标准规定了语言的行为,实现时的第一个核心原则基本上称为as-if规则:
§1.9程序执行
1 / 本国际标准中的语义描述定义了参数化的非确定性抽象机器。本国际标准对符合实施的结构没有要求。特别是,它们不需要复制或模拟抽象机器的结构。相反,需要符合实现来模拟(仅)抽象机器的可观察行为,如下所述。
在你的情况下:
std::string str = "this is a string";
有各种有效的方案:
str
?然后可以完全省略整个代码部分'T'
分配给str[0]
?然后两者可能合并为std::string str = "This is a string";
并且无法保证您的编译器将执行的操作。它可能取决于您使用的编译器,您使用的标准库实现,您正在编译的体系结构/ OS,甚至传递给编译器的参数......
因此,如果您想了解 ,则必须检查生成的机器代码。询问coliru,代码如下:
#include <string>
int main(int argc, char** argv) {
std::string str = "this is a string";
}
Clang在IR中产生以下内容:
@.str = private unnamed_addr constant [17 x i8] c"this is a string\00", align 1
反过来提供以下程序集:
.L.str: .asciz "this is a string" .size .L.str, 17
所以你拥有它,对于这些特定条件,"this is a string"
将在二进制文件中按原样加载,并将加载到只读内存中。它将保留在进程的地址空间中,直到结束,操作系统可能会根据RAM压力将其分页或不分页。