#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, если быть точным.