Где хранятся строковые константы GCC и откуда отображаются эти указатели?

когда я компилирую и запускаю следующую программу C на моей машине Linux x86_64, скомпилированной GCC:

#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 %pn", p1, p2);  // equal 0x40064c 0x40064c
                                      // This is always the output on my machine
    }
    else {
    printf("NotEqual %p %pn", 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 %pn", p1, p2);
    }
    else {                              // Never equal
    printf("NotEqual %p %pn", p1, p2); // NotEqual 0x7fff4b25f720 0x7fff4b25f710
                                        // Different pointers every time
                                        // Pointer values too large
    }
}

два указателя не равны, потому что это два массива которым можно манипулировать самостоятельно.

Я хочу знать, как GCC генерирует код для этих двух программ и как они сопоставляются с памятью во время выполнения. Поскольку это будет уже задокументировано не так много раз, любые ссылки на документацию приветствуются.

1 ответов


в обоих случаях компилятор выдает фактические байты строки "hello" раз, в программы (rodata расшифровывается как только для чтения данных).

они фактически отображаются непосредственно из исполняемого файла в память, что несколько похоже на раздел кода. Вот почему они далеки от динамически распределенных.

затем:

char *p = "hello";

просто инициализирует p адрес из этих (только для чтения) данных. И очевидно:

char *q = "hello";

получает тот же адрес. Это называется группировка строк и является необязательной популярной оптимизацией компилятора.

но когда вы пишете:

char p[] = "hello";

он, вероятно, будет генерировать что-то вроде этого:

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

являясь "hello" фактически адрес строки пула только для чтения.

вызов memcpy только для иллюстрации. Он может очень хорошо копировать inline, а не с вызовом функции.

если позже вы сделаете:

char q[] = "hello";

он определит другой массив и другой memcpy(). Так же данные, но разные адреса.

но где будут находиться эти переменные массива? Ну, это зависит от.

  • если они являются локальными, нестатическими, переменными: в стеке.
  • если они являются глобальными переменными: тогда они будут в из исполняемый файл, и они будут сохранены там с правильными символами уже там, так что нет memcpy необходимо во время выполнения. Что приятно, потому что memcpy должен быть выполнен до main.
  • если они являются локальными статическими переменными: точно так же, как и с глобальными переменными. Они оба вместе называются variables of static duration или что-то подобное.

о ссылках на документацию, извините, я ничего не знаю.

но кому это нужно документация если вы можете сделать эксперименты самостоятельно? Для этого лучший инструмент вокруг objdump, он может разобрать программу, сбросить разделы данных и многое другое!

я надеюсь, что это ответ на ваши вопросы...