Инициализация указателя в C

#c

#c

Вопрос:

В C почему это законно делать

 char * str = "Hello";
  

но незаконно делать

 int * arr = {0,1,2,3};
  

Комментарии:

1. Потому что это не массив. (Стандарт не указывает, что это должно работать.)

2. Потому что мы обычно ожидаем printf("Hello, world!n"); работы без каких-либо переменных, но никто не хочет использовать memcpy(arr, {0,1,2,3}, sizeof arr);

3. @ChrisLutz: Кто сказал, что никто не хочет? Мы не используем его, потому что язык его не поддерживает; (разумный ИМХО) вопрос в том, почему .

4. @KeithThonpson — Когда я писал это, я подумал: «Знаете, это может быть полезно в какой-то момент», но затем ответ cnicutar напомнил мне, что мы действительно можем это сделать (хотя и с немного более длинным синтаксисом).).

Ответ №1:

Я думаю, именно так работают инициализаторы в C. Однако вы можете сделать:

 int *v = (int[]){1, 2, 3}; /* C99. */
  

Комментарии:

1. Вы можете использовать тот же синтаксис для строк, чтобы получить изменяемый строковый «литерал».

Ответ №2:

Что касается C89:

"A string" при использовании вне char инициализации массива является строковым литералом; стандарт гласит, что при использовании строкового литерала создается впечатление, что вы создали глобальный char массив, инициализированный этим значением, и записали его имя вместо литерала (существует также дополнительное ограничение, к которому приводит любая попытка изменить строковый литералнеопределенное поведение). В вашем коде вы инициализируете a char * строковым литералом, который распадается на char указатель, и все работает нормально.

Однако, если вы используете строковый литерал для инициализации char массива, в действие вступает несколько магических правил, так что это уже не «как если бы массив … и т.д.» (что не сработало бы при инициализации массива), но это просто хороший способ сообщить компилятору, как следует инициализировать массив.

{1, 2, 3} Способ инициализации массивов сохраняет только эту семантику: это только для инициализации массива, это не «литерал массива».

Ответ №3:

В случае:

 char * str = "Hello";
  

"Hello" является строковым литералом. Он загружается в память (но часто доступен только для чтения) при запуске программы и имеет адрес памяти, который может быть назначен указателю, например char *str . Однако строковые литералы, подобные этому, являются исключением.

С:

 int * arr = {0,1,2,3};
  

..вы фактически пытаетесь указать на массив, который не был помещен нигде конкретно в памяти. arr является указателем, а не массивом; он содержит адрес памяти, но сам по себе не имеет хранилища для данных массива. Если вы используете int arr[] вместо int *arr , то это работает, потому что подобный массив связан с хранилищем для его содержимого. Хотя массив распадается до указателя на свои данные во многих контекстах, это не одно и то же.

Даже со строковыми литералами char *str = "Hello"; и char str[] = "Hello"; делать разные вещи. Первый устанавливает указатель str так, чтобы он указывал на строковый литерал, а второй инициализирует массив str значениями из "Hello" . Массив имеет хранилище для связанных с ним данных, но указатель просто указывает на данные, которые уже где-то загружены в память.

Ответ №4:

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

 int arr[] = { 0, 1, 2, 3 };
  

вы можете использовать arr как an int * практически во всех контекстах (исключение составляет операнд to sizeof ).

Комментарии:

1. Другое исключение, являющееся операндом оператора унарного amp; адреса; amp;arr выдает адрес массива, а не его первого элемента (та же ячейка памяти, другой тип).

Ответ №5:

… или вы можете злоупотреблять строковыми литералами и сохранять числа в виде строкового литерала, который для маленькой конечной машины будет выглядеть следующим образом:

 int * arr = (int *)"123";
  

Комментарии:

1. Небольшая конечная машина, где sizeof int 4, если быть точным.