Подтвердить что ты не робот

C оптимизация строковых литералов

только что проверил следующее в gdb:

char *a[] = {"one","two","three","four"};
char *b[] = {"one","two","three","four"};
char *c[] = {"two","three","four","five"};
char *d[] = {"one","three","four","six"};

и я получаю следующее:

(gdb) p a
$17 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"}
(gdb) p b
$18 = {0x80961a4 "one", 0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four"}
(gdb) p c
$19 = {0x80961a8 "two", 0x80961ac "three", 0x80961b2 "four", 0x80961b7 "five"}
(gdb) p d
$20 = {0x80961a4 "one", 0x80961ac "three", 0x80961b2 "four", 0x80961bc "six"}

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

Является ли это примером какой-либо оптимизации компилятора или это стандартное поведение для объявления строки такого типа?

4b9b3361

Ответ 1

Он называется "объединение строк". Это необязательно в Microsoft Compilers, но не в GCC. Если вы отключите пул строк в MSVC, то "одни и те же" строки в разных массивах будут дублироваться и иметь разные адреса памяти, и поэтому потребуются дополнительные (ненужные) 50 байтов ваших статических данных.

EDIT: gcc does имеет опцию -fwritable-strings, которая отключает пул строк. Эффект этой опции двоякий: он позволяет перезаписывать строковые литералы и отключает пул строк. Таким образом, в вашем коде установка этого флага позволит несколько опасный код

/* Overwrite the first string in a, so that it reads 'xne'.  Does not */ 
/* affect the instances of the string "one" in b or d */
*a[0] = 'x';

Ответ 2

(Я предполагаю, что ваши a, b, c и d объявлены как локальные переменные, что является причиной ожиданий, связанным с стеком.)

Строковые литералы в C имеют статическую продолжительность хранения. Они никогда не выделяются "в стеке". Они всегда выделяются в глобальной/статической памяти и живут "навсегда", то есть до тех пор, пока программа работает.

В массиве a, b, c и d были выделены массивы. Указатели, хранящиеся в этих массивах, указывают на статическую память. В этих обстоятельствах нет ничего необычного в том, что указатели одинаковы для идентичных слов.

Будет ли компилятор объединять идентичные литералы в один, зависит от компилятора. У некоторых компиляторов даже есть опция, которая контролирует это поведение. Строковые литералы всегда доступны для чтения (именно поэтому лучше использовать тип const char * для ваших массивов), поэтому не имеет большого значения, слиты ли они или нет, пока вы не начнете полагаться на фактический указатель значения.

P.S. Просто из любопытства: даже если эти строковые литералы были выделены в стеке, почему вы ожидаете, что идентичные литералы будут "создаваться" более одного раза?