C составные литералы, указатель на массивы
Я пытаюсь назначить составной литерал переменной, но, похоже, это не сработает, см.:
int *p[] = (int *[]) {{1,2,3},{4,5,6}};
Я получил ошибку в gcc.
но если я пишу только это:
int p[] = (int []) {1,2,3,4,5,6};
Тогда все в порядке.
Но это не то, что я хочу.
Я не понимаю, почему возникает ошибка, потому что, если я инициализирую ее как массив или использую ее с указателем массивов символов, все в порядке, см.
int *p[] = (int *[]) {{1,2,3},{4,5,6}}; //I got a error
int p[][3] = {{1,2,3},{4,5,6}}; //it okay
char *p[] = (char *[]) {"one", "two"...}; // it okay!
Примечание. Я не понимаю, почему у меня возникла ошибка в первом, и, пожалуйста, я не могу, или я не хочу писать, как вторую форму, потому что она должна быть составной литературой, а я Не хочу сказать, насколько велик массив для компилятора. Я хочу что-то вроде второго, но для значений int.
Спасибо заранее.
Ответы
Ответ 1
Во-первых, листы являются избыточными во всех ваших примерах и могут быть удалены. Во-вторых, вы используете синтаксис для инициализации многомерного массива, и для этого требуется, чтобы второе измерение определялось для выделения последовательного блока памяти. Вместо этого попробуйте один из двух подходов ниже:
-
Многомерный массив:
int p[][3] = {{1,2,3},{4,5,6}};
-
Массив указателей на одномерные массивы:
int p1[] = {1,2,3};
int p2[] = {4,5,6};
int *p[] = {p1,p2};
Последний способ имеет то преимущество, что позволяет использовать подмассивы различной длины. Принимая во внимание, что первый метод гарантирует, что память выставляется соприкосновенно.
Другой подход, который я настоятельно рекомендую вам НЕ использовать, - это кодировать целые числа в строковых литералах. Это не переносной хак. Кроме того, данные в строковых литералах должны быть постоянными. Нужно ли изменять ваши массивы?
int *p[] = (int *[]) {
"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00",
"\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
};
Этот пример может работать на 32-разрядной машине с маленькими концами, но я набираю это с iPad и не могу проверить его на данный момент. Опять же, пожалуйста, не используйте это; Я чувствую себя грязным, даже подниму его.
Вы также обнаружили, что метод кастинга работает с указателем на указатель. Это также можно индексировать как многомерный массив.
int **p = (int *[]) { (int[]) {1,2,3}, (int[]) {4,5,6} };
Ответ 2
Сначала поймите, что "Массивы не являются указателями".
int p[] = (int []) {1,2,3,4,5,6};
В приведенном выше случае p
- массив целых чисел. Копирование элементов {1,2,3,4,5,6}
в p
. Здесь нет необходимости указывать тип и оба типа rvalue
и lvalue
, которые соответствуют целочисленному массиву, поэтому ошибок нет.
int *p[] = (int *[]) {{1,2,3},{4,5,6}};
"Примечание. Я не понимаю, почему я получил ошибку в первом,.."
В приведенном выше случае p
массив целых указателей. Но {{1,2,3},{4,5,6}}
представляет собой двумерный массив (т.е. [] []) И не может быть лидирован в массив указателей. Вам необходимо инициализировать как -
int p[][3] = { {1,2,3},{4,5,6} };
// ^^ First index of array is optional because with each column having 3 elements
// it is obvious that array has two rows which compiler can figure out.
Но почему этот оператор скомпилировался?
char *p[] = {"one", "two"...};
Строковые литералы отличаются от целых литералов. В этом случае также p
представляет собой массив указателей символов. Когда на самом деле сказано "one"
, , его можно либо скопировать в массив, либо указать на его местоположение, считая его только как прочитанным.
char cpy[] = "one" ;
cpy[0] = 't' ; // Not a problem
char *readOnly = "one" ;
readOnly[0] = 't' ; // Error because of copy of it is not made but pointing
// to a read only location.
С строковыми литералами возможен любой из указанных выше случаев. Итак, вот почему скомпилировано выражение. Но -
char *p[] = {"one", "two"...}; // All the string literals are stored in
// read only locations and at each of the array index
// stores the starting index of each string literal.
Я не хочу сказать, насколько велик массив для компилятора.
Динамическое распределение памяти с помощью malloc
является решением.
Надеюсь, что это поможет!
Ответ 3
Так как никто не сказал это: если вы хотите иметь указатель-2D-массив, вы можете (возможно) сделать что-то вроде
int (*p)[][3] = &(int[][3]) {{1,2,3},{4,5,6}};
EDIT: Или вы можете иметь указатель на свой первый элемент через
int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};
Причина, по которой ваш пример не работает, заключается в том, что {{1,2,3},{4,5,6}}
не является допустимым инициализатором для типа int*[]
(потому что {1,2,3}
не является допустимым инициализатором для int*
). Обратите внимание, что это не a int[2][3]
— это просто недопустимое выражение.
Причина, по которой он работает для строк, состоит в том, что "one"
является допустимым инициализатором для char[]
и char[N]
(для некоторого N > 3). В качестве выражения он приблизительно эквивалентен (const char[]){'o','n','e','\0'}
, за исключением того, что компилятор не слишком много жалуется, когда он теряет константу.
И да, есть большая разница между инициализатором и выражением. Я уверен, что char s[] = (char[]){3,2,1,0};
- ошибка компиляции в C99 (и, возможно, С++ pre-0x). Есть и другие вещи, но T foo = ...;
- это переменная инициализация, а не назначение, хотя они выглядят одинаково. (Они особенно отличаются на С++, поскольку оператор присваивания не вызывается.)
И причина путаницы с указателями:
- Тип
T[]
неявно преобразуется в тип T*
(указатель на его первый элемент), когда это необходимо.
-
T arg1[]
в списке аргументов функции на самом деле означает T * arg1
. Вы не можете передать массив функции для разных причин. Это невозможно. Если вы попробуете, вы фактически передаете указатель на массив. (Однако вы можете передать структуру, содержащую массив фиксированного размера, в функцию.)
- Они оба могут быть разыменованы и индексированы с помощью идентичной (я думаю) семантики.
EDIT: Наблюдатель может заметить, что мой первый пример примерно синтаксически эквивалентен int * p = &1;
, что является недопустимым. Это работает на C99, потому что составной литерал внутри функции "имеет автоматическую продолжительность хранения, связанную с охватывающим блоком" (ISO/IEC 9899: TC3).
Ответ 4
Тот, который вы используете, представляет собой массив указателей int. Вы должны использовать указатель на массив:
int (*p)[] = (int *) {{1,2,3}, {4,5,6}}
Посмотрите этот ответ для более подробной информации.
Ответ 5
Кажется, вы путаете указатели и массив. Это не одно и то же! Массив - это сам список, а указатель - это просто адрес. Затем с помощью арифметики указателей вы можете притворяться, что указатели являются массивом, и с тем, что имя массива является указателем на первый элемент, все суммируется в беспорядке.;)
int *p[] = (int *[]) {{1,2,3},{4,5,6}}; //I got a error
Здесь p представляет собой массив указателей, поэтому вы пытаетесь назначить элементы, адреса которых 1, 2, 3 для первого массива и 4, 5, 6 для второго массива. Ошибка seg происходит из-за того, что вы не можете получить доступ к этим ячейкам памяти.
int p[][3] = {{1,2,3},{4,5,6}}; //it okay
Это нормально, потому что это массив массивов, поэтому на этот раз 1, 2, 3, 4, 5 и 6 не являются адресами, а самими элементами.
char *p[] = (char *[]) {"one", "two"...}; // it okay!
Это нормально, потому что строковые литералы ( "один", "два",...) не являются строками, а указателями на эти строки, поэтому вы назначаете p [1] адрес строкового литерала "один".
Кстати, это то же самое, что делать char abc[]; abc = "abc";
. Это не будет компилироваться, потому что вы не можете назначить указатель на массив, а char *def; def = "def";
решает проблему.