9

当我在我的 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 是如何为这两个程序生成代码的,以及它们在执行过程中是如何映射到内存的。由于这已经记录了很多次,因此也欢迎任何指向文档的链接。

4

1 回答 1

13

在这两种情况下,编译器都会在程序部分(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,它可以反汇编程序,转储数据部分等等!

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

于 2012-09-12T18:15:37.347 回答