【问题标题】:Where does #define or char* strings reside in memory? [duplicate]#define 或 char* 字符串驻留在内存中的什么位置? [复制]
【发布时间】:2012-12-11 17:26:19
【问题描述】:

可能重复:
Is a string literal in c++ created in static memory?

如果我这样做:
const char* StringPtr = "string0",
那么它肯定是在内存的某个地方,我可以得到StringPtr的地址。

但如果我这样做:
#define STRING0 "string0",那么STRING0 驻留在哪里?
或者,内存中不存在STRING0,因为编译器将STRING0 的使用替换为"string0"

据我所知,无论何时在代码中写入任何字符串,编译器都必须将它放在内存中的某个位置,但我不知道它的确切行为。
但我对此不太确定。

谁能解释编译器如何处理#define-ed 或声明为char* 的字符串?

另外,哪个更好? 头文件中的#defineextern const char*extern const std::string字符串?

谢谢!

【问题讨论】:

  • 它不存储内存。在程序编译之前,你可以想象成这样:预处理器会复制你给STRING0的值,然后粘贴到你使用的地方。

标签: c++ string


【解决方案1】:

#define 是一个预处理器宏。它将在编译代码之前的预编译阶段将STRING0替换为"string0"

"string0" 驻留在可执行文件的静态只读内存中。

StringPtr 是一个变量,这就是为什么你可以获取它的地址。它只是指向"string0"的内存地址。

【讨论】:

  • StringPtr 指向的"string0" 是否也驻留在可执行文件的静态只读内存中?
  • 这就是我所说的:"string0" 驻留在可执行文件的静态只读内存中。 StringPtr只是指向"string0"的内存地址,本身并不在静态只读内存中。
  • 这是一个示例,其中“Hello”的实例无法合并并且它们未放置在只读存储器中。查看变量 t1 和 t2:aszt.inf.elte.hu/~gsd/halado_cpp/ch01.html
【解决方案2】:

当您执行#define 时,没有编译器,而是预处理器在预处理的源文件中将STRING0 替换为“string0” /em>,在将其传递给适当的编译器之前。

编译器从不看到 STRING0,但只在你写 STRING0 的所有地方看到“string0”。

编辑:

替换您在源文件中编写的 STRING0 的每个“string0”实例本身就是一个字符串文字。如果这些字符串文字被保证(或声明)为不变,那么编译器可能会通过存储此“string0”的单个副本并将其他用途指向该副本来优化内存分配(我在编辑中改写了这一段)。

(编辑:那些相同的文字字符串常量可能会合并到一个单独的副本中,但这取决于编译器。标准不要求或强制执行它:http://www.velocityreviews.com/forums/t946521-merging-of-string-literals-guaranteed-by-c-std.html

至于你的最后一个问题:最便携的是将它们声明为:const char *

稍后编辑:到目前为止我发现的关于字符串文字的最佳讨论在这里:https://stackoverflow.com/a/2245983/1284631

另外,请注意字符串文字也可能出现在静态分配的 char 数组的初始化中,当它不能与它的其他副本合并时,因为静态数组的内容可能会被覆盖。请参见下面的示例,其中两个相同的字符串文字“hello”无法合并:

#include <stdio.h>
#include <string.h>

int main(){

        char x[50]="hello";

        printf("x=%s, &x[0]=%p\n",x,&x[0]);

        const char *y="hello";

        printf("y=%s, &y[0]=%p\n",y,&y[0]);

        strcpy(&x[0],"zz");

        printf("x=%s, &x[0]=%p\n",x,&x[0]);

        return 0;
}

这段代码的输出是:

x=hello, &x[0]=0x7fff8a964370
y=hello, &y[0]=0x400714
x=zz, &x[0]=0x7fff8a964370

【讨论】:

  • 一些编译器可以选择将重复的字符串合并在一起,因此"string0" 的所有实例,无论使用情况如何,都可能合并到内存中的单个实例中。
  • @RemyLebeau:那么,应该小心使用它,因为人们仍然可以修改字符串的实例之一,而无意中修改了所有其他实例。
  • 我希望所有实例都放置在可执行文件的静态只读内存中,因此任何直接修改数据的尝试都会使应用程序崩溃。这是我的经验。
  • @axeoth 由于字符文字是恒定的(只有与 c 进行反向计算才能编写 char *foo = "this should be illegal"),所以编译器可以 - 并且通常会 - 合并 char 文字。如果某些处于发布模式的生产编译器实际上复制了所有字符串,我会感到非常惊讶。
  • @Voo:这可能是习惯性的,但我认为不能保证 dstrings 是重复的。无论该指针声明的如何“const”,您仍然可以更改字符串的内容。
【解决方案3】:
#define STRING0

STRING0 不在内存中。它甚至在编译期间都不存在。在预编译中,所有出现的STRING0 都被preprocessor 替换为“string0”。在此阶段之后,以下阶段或已编译的应用程序都不知道名称为 STRING0 的任何符号的存在

一旦发生这种情况,并非所有实例中的许多实例最终都会在您的代码中成为唯一的字符串文字(您的 const char* 大小写)。 @Potatoswatter 和@silico 提供的link 更好地回答了这些存储在内存中的位置

【讨论】:

  • 那么"string0" 的所有出现都驻留在某个地方,这是一个有效的问题。
  • @Potatoswatter 是的,但完全不是被问到的问题(据我所知)。 silico 已经涵盖了字符串文字的情况,这可能是它们中的大多数最终的结果。
  • @Potatoswatter 是的,但是您会在源文件的多个位置出现多次“string0”。他们每个人都独立于其他人。
  • 好的,我应该将问题编辑为“string0”所在的位置吗?
  • @MarsonMao 我已经提供了答案:)
【解决方案4】:

stringPtr 位于可执行文件的数据部分。如果您在文本编辑器中打开您的 exe,您将能够搜索它。 Data Segment

宏仅在构建程序的预处理阶段存在。

根据您的编译器,如果您使用宏方法,您最终可以在 exe 中使用相同字符串的多个单独实例,但如果您使用 char* 方法,则可以只使用一个实例。

【讨论】:

    【解决方案5】:

    在几乎所有情况下,编译器都可以将字符串文字放在任何它想要的地方。每次文字出现在源代码中时可能会有一个副本,或者在实例之间共享一个主副本。

    这有时会在 C 中引起麻烦,其中const 并不意味着同样的事情,您可以修改内存。在一个平台上,所有相同的字符串都会被更改,而在另一个平台上,更改不会传播。从 C++11 开始,字符串文字不会隐式丢失 constness,而且更难犯错误。

    字符串将在程序启动之前全部初始化,因此实际上它们是可执行二进制映像的一部分。这是肯定的。

    不同的是:

    const char StringPtr[] = "string0",
    

    这定义了一个具有唯一地址的专用数组对象。

    【讨论】:

    • 您好,是否启用编译器选项“启用字符串池”以仅制作一个主副本?而且,如果我声明const char StringPtr[],它是否与const char* 一样驻留在只读内存中?
    • @MarsonMao 是的,这就是该选项的想法。是的,理论上这是在只读内存中,当然物理机可能不会使用字面上的只读内存来存储它。
    • 请注意,const char* 变量也可以更改为指向其他对象,这与使用 [] 声明的数组不同。指针保存在非常量内存中,而数组没有保存非常量。
    • 谢谢。那么只读内存有大小限制吗?就像我贴上大量的const char* 和大量的const int...等等。在整个项目中,问题严重吗?
    • @MarsonMao 这取决于平台,但通常不会。字符串数据和其他任何东西一样,操作系统应该能够处理数百兆字节的程序文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-20
    • 1970-01-01
    • 2011-07-04
    • 2013-03-21
    • 2011-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多