С составные литералы, указатель на массивы - PullRequest
31 голосов
/ 31 марта 2011

Я пытаюсь присвоить составной литерал переменной, но, похоже, он не работает, см .:

  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's okay
  char *p[] = (char *[]) {"one", "two"...}; // it's okay!

Заметьте, я не понимаю, почему я получил ошибку в первой, и, пожалуйста, я не могу, или я не хочу писать как вторая форма, потому что это должны быть составные литералы, и я не не хочу сказать, насколько большой массив для компилятора. Я хочу что-то вроде второго, но для значений int.

Заранее спасибо.

Ответы [ 5 ]

31 голосов
/ 31 марта 2011

Во-первых, приведение является избыточным во всех ваших примерах и может быть удалено. Во-вторых, вы используете синтаксис для инициализации многомерного массива, и для этого требуется определить второе измерение, чтобы выделить последовательный блок памяти. Вместо этого попробуйте один из двух подходов ниже:

  • Многомерный массив:

    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} };
13 голосов
/ 31 марта 2011

Сначала поймите, что «Массивы не являются указателями».

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 является решением.

Надеюсь, это поможет!

5 голосов
/ 12 апреля 2011

Поскольку никто не сказал этого: если вы хотите иметь указатель на 2D-массив, вы можете (вероятно) сделать что-то вроде

int (*p)[][3] = &(int[][3]) {{1,2,3},{4,5,6}};

РЕДАКТИРОВАТЬ: Или вы можетеуказатель на первый элемент через

int (*p)[3] = (int[][3]) {{1,2,3},{4,5,6}};

Причина, по которой ваш пример не работает, заключается в том, что {{1,2,3},{4,5,6}} не является допустимым инициализатором для типа int*[] (потому что {1,2,3} не является допустимыминициализатор для int*).Обратите внимание, что это , а не и int[2][3] - это просто недопустимое выражение.

Причина, по которой он работает для строк, заключается в том, что "one" является допустимым инициализатором для char[] и char[N] (для некоторых N> 3).Как выражение , оно приблизительно эквивалентно (const char[]){'o','n','e','\0'}, за исключением того, что компилятор не слишком жалуется, когда теряет константность.

И да, есть большая разницамежду инициализатором и выражением.Я почти уверен, что char s[] = (char[]){3,2,1,0}; - это ошибка компиляции в C99 (и, возможно, C ++ pre-0x).Также есть множество других вещей, но T foo = ...; - это инициализация переменной, а не присваивание, даже если они похожи.(Они особенно отличаются в C ++, поскольку оператор присваивания не вызывается.)

И причина путаницы с указателями:

  • Тип T[] неявно преобразуется в типT* (указатель на его первый элемент) при необходимости.
  • T arg1[] в списке аргументов функции фактически означает T * arg1.Вы не можете передать массив в функцию по различным причинам.Это невозможно.Если вы попытаетесь, вы на самом деле передаете указатель на массив.(Однако вы можете передать структуру, содержащую массив фиксированного размера, в функцию.)
  • Они могут быть разыменованы и подписаны с идентичной (я думаю) семантикой.

РЕДАКТИРОВАТЬ: Наблюдатель может заметить, что мой первый пример примерно синтаксически эквивалентен int * p = &1;, что является недействительным.Это работает в C99, потому что составной литерал внутри функции «имеет автоматическую продолжительность хранения, связанную с включающим блоком» ( ISO / IEC 9899: TC3 ).

1 голос
/ 08 апреля 2011

Кажется, вы путаете указатели и массивы.Они не одно и то же!Массив - это сам список, а указатель - просто адрес.Затем с помощью арифметики с указателями вы можете делать вид, что указатели являются массивом, а с учетом того, что имя массива является указателем на первый элемент, все суммируется в беспорядке.;)

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's okay

Это нормально, потому что это массив массивов, поэтому на этот раз 1, 2, 3, 4, 5 и 6 aren 'адреса, но сами элементы .

char *p[] = (char *[]) {"one", "two"...};    // it's okay!

Это нормально, потому что строковые литералы ("one", "two", ...) на самом деле не строки, а указатели наэти строки, поэтому вы присваиваете p [1] адрес строкового литерала "one".
Кстати, это то же самое, что и char abc[]; abc = "abc";.Это не скомпилируется, потому что вы не можете назначить указатель на массив, в то время как char *def; def = "def"; решает проблему.

1 голос
/ 08 апреля 2011

То, что вы используете, это массив указателей int.Вы должны использовать указатель на массив:

int (*p)[] = (int *) {{1,2,3}, {4,5,6}}

Посмотрите на этот ответ для более подробной информации.

...