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

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.

Спасибо заранее.

4b9b3361

Ответ 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"; решает проблему.