【问题标题】:Where are string constants stored by GCC and from where these pointers are mapped?GCC 存储的字符串常量在哪里以及这些指针从哪里映射?
【发布时间】:2012-09-12 18:03:41
【问题描述】:

当我在我的 Linux x86_64 机器上编译并运行以下由 GCC 编译的 C 程序时:

#include <stdio.h>

int main(void)
{
    char *p1 = "hello";               // Pointers to strings
    char *p2 = "hello";               // Pointers to strings
    if (p1 == p2) {                   // They are equal
    printf("equal %p %p\n", p1, p2);  // equal 0x40064c 0x40064c
                                      // This is always the output on my machine
    }
    else {
    printf("NotEqual %p %p\n", p1, p2);
    }
}

我总是得到如下输出:

等于 0x40064c 0x40064c

我知道字符串存储在一个常量表中,但与动态分配的内存相比,地址太低了。

与以下程序比较:

#include <stdio.h>

int main(void)
{
    char p1[] = "hello";                // char arrar
    char p2[] = "hello";                // char array
    if (p1 == p2) {
    printf("equal %p %p\n", p1, p2);
    }
    else {                              // Never equal
    printf("NotEqual %p %p\n", p1, p2); // NotEqual 0x7fff4b25f720 0x7fff4b25f710
                                        // Different pointers every time
                                        // Pointer values too large
    }
}

这两个指针不相等,因为它们是两个可以独立操作的数组。

我想知道 GCC 是如何为这两个程序生成代码的,以及它们在执行过程中是如何映射到内存的。由于这已经被记录了很多次,因此也欢迎任何指向文档的链接。

【问题讨论】:

  • 你总是可以在拆卸时达到顶峰。这是一个很好的技能,你需要在未来再做一次。
  • 您的示例代码中只有两个字符串:equal %p %p\nNotEqual %p %p\np1p2 只是初始化为某个值的字符数组变量,然后将其用作字符串。特别是,您仍然可以使用p1[0] = 'H'; p2[0] = 'J';,例如,没有任何问题。如果您希望p1p2 成为字符串常量,请使用static const char p1[] = "Hello";。至少 GCC-4.6.3 将本地 const 数组视为变量,而不是真正的只读常量,因此需要 static。而且它不会合并字符串,因此两者具有不同的指针。你用的是哪个编译器?

标签: c linux string gcc compiler-construction


【解决方案1】:

在这两种情况下,编译器都会在程序的.rodata 部分中仅发出一次字符串"hello" 的实际字节(rodata 代表只读数据)。

它们实际上是直接从可执行文件映射到内存中,有点类似于代码部分。这就是它们与动态分配的相差甚远的原因。

然后:

char *p = "hello";

只需将p 初始化为此(只读)数据的地址。 很明显:

char *q = "hello";

获取完全相同的地址。这称为字符串池,是编译器的可选流行优化。

但是当你写的时候:

char p[] = "hello";

它可能会生成这样的东西:

char p[6];
memcpy(p, "hello", 6);

"hello"实际上是只读池化字符串的地址。

memcpy 的调用仅用于说明目的。它可能很好地复制到内联,而不是用函数调用。

如果以后你这样做:

char q[] = "hello";

它将定义另一个数组和另一个memcpy()。所以相同的数据,但不同的地址。

但是这些数组变量将驻留在哪里?好吧,这取决于。

  • 如果它们是本地的、非静态的变量:在堆栈中。
  • 如果它们是全局变量:那么它们将在可执行文件的.data 部分中,并且它们将被保存在那里并且已经存在正确的字符,因此在运行时不需要memcpy。这很好,因为 memcpy 必须在 main 之前执行。
  • 如果它们是局部静态变量:与全局变量完全相同。它们都被称为variables of static duration 或类似的名称。

关于文档链接,抱歉,我不知道。

但是,如果您可以自己进行实验,谁还需要文档呢?最好的工具是objdump,它可以反汇编程序、转储数据段等等!

我希望这能回答你的问题...

【讨论】:

  • objdump简单得多,只需使用-S而不是-c来生成汇编程序。
  • @JensGustedt:同意。我只是习惯了objdump的输出。
  • 它也可能不需要memcpy,而是将"hello"直接存储为堆栈词。
猜你喜欢
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 2020-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-25
  • 2021-05-04
相关资源
最近更新 更多