Как строки C выделяются в памяти?

Скажем, у меня есть простая функция, которая возвращает строку C следующим образом:

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

и я вызываю getString() из main() следующим образом:

  const char * s = getString();

1) В соответствии с gdb переменная ptr сохраняется в стеке, но строка, на которую указывает ptr, не соответствует:

(gdb) p &ptr
$1 = (const char **) 0x7fffffffe688

(gdb) p ptr
$2 = 0x4009fc "blah blah"

Означает ли это, что "бла-бла" не является локальной переменной внутри getString()?

Я предполагаю, что если бы это была локальная переменная, я бы не смог передать ее моей функции main()... Но если это не так, то где оно хранится? На куче? Является ли это "своего рода" динамическим распределением памяти, реализованным ОС каждый раз, когда он попадает на строку, или что?

2) Если я использую массив вместо указателя, следующим образом:

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}

компилятор предупреждает меня, что:

warning: address of local variable ‘a’ returned

(и, конечно, программа компилируется, но она не работает).

Собственно, если я спрошу gdb, я получаю

(gdb) p &a
$2 = (const char (*)[15]) 0x7fffffffe690

Но я думал, что const char * ptr и const char a [] были в основном одинаковыми. Похоже, что это не так.

Неужели я ошибаюсь? Какая разница между двумя версиями?

Спасибо!

Ответ 1

Когда вы пишете

const char *ptr = "blah blah";

то происходит следующее: компилятор генерирует константную строку (типа char []) с содержимым "blah blah" и сохраняет ее где-то в сегменте данных исполняемого файла (он в основном имеет такую ​​же длительность хранения, что и для переменных объявлен с использованием ключевого слова static).

Затем адрес этой строки, действительный на протяжении всего жизненного цикла программы, сохраняется в указателе ptr, который затем возвращается. Все в порядке.

Означает ли это, что "blah blah" не является локальной переменной внутри getString()?

Позвольте мне ответить сломанным английским предложением: да, это не так.

Однако, когда вы объявляете массив, как в

const char a[] = "blah blah";

тогда компилятор не генерирует статическую строку. (В самом деле, это несколько особый случай при инициализации строк.) Затем он генерирует код, который выделяет достаточно большой кусок памяти стека для массива a (это не указатель!) И будет заполните его байтами строки. Здесь a на самом деле является локальной переменной и возвращает результаты своего адреса в поведении undefined.

Итак...

Но я думал, что const char *ptr и const char a[] были в основном одинаковыми.

Нет, совсем нет, потому что массивы не являются указателями.

Ответ 2

Я предполагаю, что если бы это была локальная переменная, я бы не смог пройти это моя основная функция()... Но если это не так, где она хранится?

Строковые литералы обычно хранятся в разделе данных только для чтения (.rodata). C просто говорят, что они имеют статическую продолжительность хранения. Поэтому вы можете вернуть указатель на такой литерал, но это не относится к массивам.

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

char *f(void)
{
    const char *p1 = "hello, world";
    char p2[] = "hello, world";

    return p1; /* allowed */
    return p2, /* forbidden */
}

Ответ 3

В вашей функции область массива a[] находится внутри функции getString2(). его переменную локального массива.

const char *getString2()
{
  const char a[] = "blah blah blah";
  return a;
}  

В приведенной выше строке строка "blah blah blah" копирует кулак в a[], и вы пытаетесь вернуть этот массив с помощью инструкции return a, но не константной строки.

Где как в первом коде getString(): ptr = "blah blah"; ptr указывает на память с глобальной областью.

const char * getString()
{
  const char * ptr = "blah blah";
  return ptr; 
}

В этом случае вы возвращаете адрес постоянной строки "blah blah", который является законным.

Итак, на самом деле проблема с его областью.

полезно узнать о Макет памяти программ C и Область переменных в C.

Ответ 4

Вы правы в том, что они не одно и то же. char a [] - это массив, сформированный в стеке, а затем заполненный "blah.." - внутри функции вы имеете по существу `const char a [15]; strcpy (a, "blah blah blah" ); "

The const char *ptr = "blah blah blah"; С другой стороны, это просто указатель (сам указатель находится в стеке), а указатель указывает на строку "blah blah blah", которая хранится где-то еще [в "только для чтения" ) вероятно].

Вы заметите большую разницу, если попытаетесь что-то изменить, например: a[2] = 'e'; vs ptr[2] = 'e'; - первый из них будет успешным, потому что вы изменяете значение стека, где второй (возможно) сбой, потому что вы изменяете только часть памяти только для чтения, что, конечно же, не должно работать.

Ответ 5

Они не совпадают.

Первый - это указатель на строковый литерал. Сам указатель находится в автоматическом хранилище. Строка находится в статической памяти только для чтения. Он непреложный.

Второй - это автоматический (стек) char массив (и это возвращение, как предупреждает, не является законным).