2010-08-06 5 views
3

Пожалуйста, смотрите следующие инструкции:Разница между массивом инициализациями

char a[5]="jgkl"; // let's call this Statement A 
char *b="jhdfjnfnsfnnkjdf"; // let's call this Statement B , and yes i know this is not an Array 
char c[5]={'j','g','k','l','\0'}; // let's call this Statement C 

Теперь, есть ли разница между операторами А и С? Я имею в виду, что оба должны быть на Stack, не так ли? Только b будет находиться в положении Static.


Не так ли, чтобы «jgkl» существовали в статическом месте на всю жизнь программы? Так как предполагается, что он доступен только для чтения/постоянный? Просьба уточнить.

ответ

2

Если значение [] статично, то и c [] - эти два эквивалентны, и ни один из них не является строковым литералом. Эти два одинаково можно было бы объявить так, чтобы они были в стеке - это зависит от того, где и как они объявлены, а не от синтаксиса, используемого для указания их содержимого.

+0

@strut Нет - они не являются, они представляют собой как массивы, содержимое которых может быть изменено после инициализации, так как содержимое b не может быть изменено. Вы не понимаете, что читаете. – 2010-08-07 12:11:54

+0

@strut В 'a [] =" foo "', a является массивом, "foo" является литералом. – 2010-08-07 14:22:36

0

C-Literals всегда доступны только для чтения.

  • a) выделяет 5-байтовую память и сохраняет СОДЕРЖАНИЕ из литерала вкл. «\ 0» в нем
  • б) выделяет SizeOf (size_t) байт память и хранить буквального-адрес в нем
  • с) выделяет 5 байт памяти и хранить в 5 символьных значений в нем
+0

Только b относится к строковому литералу. и, таким образом, «доступно только для чтения». – 2010-08-06 21:08:04

+0

В (a) нет литерала. В этом случае вы никогда не сможете принять адрес чего-либо, чтобы найти символ 'char *' to "jgkl" в виде строкового литерала. –

+0

@ Хейт, верно, вот что я пытался сказать. – AShelly

2

Значение «jgkl» никогда не может быть загружено в рабочую память. Перед вызовом main выполняется функция (часто называемая cinit). Одна из вещей, которые выполняет эта функция, это инициализировать переменные static и file-scope. В используемом компиляторе DSP исходные значения хранятся в таблице, которая является частью образа программы. Формат таблицы не связан с форматом инициализируемых переменных. Таблица инициализатора остается частью образа программы и никогда не копируется в ОЗУ. Проще говоря, в памяти нет нигде, что я могу надежно получить доступ к «jgkl».

Маленькие струны, такие как a, могут даже не храниться в этой таблице вообще. Оптимизатор может уменьшить это до эквивалента (псевдоопределение) store reg const(152<<24|167<<16|153<<8|154)

Я подозреваю, что большинство компиляторов аналогичны.

5

Нет, потому что символов «jgkl» из Statement A используется для инициализации a, он не создает хранилище в исполняемом файле для символьной строки (кроме хранения, созданного объявляя a). Это объявление создает массив символов в памяти чтения-записи, которые содержат байты {'j','g','k','l','\0'}, но строка, которая была использована для инициализации, в противном случае не присутствует в исполняемом результате.

В заявлении B в качестве инициализатора используется строковый литерал адрес. Переменная char *b является указателем, хранящимся в памяти чтения-записи. Он указывает на строку символов "jhdfjnfnsfnnkjdf". Эта строка присутствует в вашем исполняемом изображении в сегменте, часто называемом «.sdata», что означает «статические данные». Строка обычно хранится в постоянной памяти, как это допускается стандартом C.

Это одно ключевое различие между объявлением массива символов и строковой константой: даже если у вас есть указатель на константу строки, вы не должны изменять содержимое.

Попытка изменить константу строки - это «неопределенное поведение» в соответствии с стандартным разделом ANSI C 6.5.7 при инициализации.

+0

@Heath: строковые литералы в C фактически * не * 'const' квалифицированы (по большей части исторические причины). Тем не менее, программе не разрешается писать им. – Gilles

+0

@Giles: Спасибо за эту важную коррекцию, которая теперь включена в мой ответ. –

+0

@strut: Книга с самой важной информацией - это стандарт ANSI C, но вы должны внимательно прочитать каждое слово. Что касается '.sdata', я рекомендую вам поэкспериментировать с компилятором и компоновщиком. В частности, смешивание языка ассемблера с C приводит к пониманию того, как это работает. –

1

A и C в точности эквивалентны. Синтаксис, используемый в A, является аббревиатурой для синтаксиса в C.

Каждый из объектов с именем a и c представляет собой массив байтов длиной 5, хранящийся в определенном месте в памяти, которое фиксировано во время выполнения. Программа может в любой момент изменить байты элемента. Ответственность за инициализацию объектов лежит на компиляторе. Компилятор может генерировать нечто похожее на a[0] = 'j'; a[1] = 'g'; ... или что-то похожее на memcpy(a, static_read_only_initialization_data[1729], 5), или что бы он ни выбрал. Массив находится в (концептуальном) стеке, если объявление происходит в функции или в глобальной записываемой памяти, если объявление происходит в области файлов.

Объект с именем b является указателем на байт. Его начальное значение является указателем на строковое литерала, которое доступно только для чтения во многих реализациях, имеющих постоянную память, но не все. Значение b может меняться (например, указывать на другую строку или на NULL). Программе не разрешается изменять содержимое jhdfjnfnsfnnkjdf", хотя, как обычно, в C реализация не может обеспечить выполнение этого.

Смежные вопросы