на что указывает `int * userMask [3] [4]`? - PullRequest
22 голосов
/ 08 ноября 2008

Я изменяю некоторый код и натолкнулся на объявление, которое мне трудно понять:

int *userMask[3][4] = {0};

На что именно это указывает? Это матрица, где каждый элемент является указателем? Или это указывает на матрицу размера [3] [4]?

Спасибо


Полагаю, мой вопрос в том, как userMask[2][maskElement][user] может работать, когда он объявлен как int. Разве userMask не должен быть int[] для правильной работы? Я не должен понимать это право ...

Кстати, спасибо за ваше предложение о cdecl Роберте. Однако кто-нибудь знает, как использовать его в командной строке XP? Все, что я могу получить, это синтаксическая ошибка: (

Ответы [ 9 ]

36 голосов
/ 08 ноября 2008

Краткий ответ

Данная пользовательская маска объявлена ​​как

int *userMask[3][4];

тогда userMask имеет тип int*[3][4]. Это двумерный массив указателей на int. Размер внешнего измерения равен 3, размер внутреннего измерения равен 4. Действительно, это не более чем 3-элементный 1d массив, тип элемента которого является другим 4-элементным 1d массивом, тип элемента которого int*.

Шаги объяснили

Так что, если вы делаете

userMask[2][maskElement][user]

затем по существу с первыми двумя индексами вы выбираете конкретный указатель из массива 2d:

int * p = userMask[2][maskElement];

затем вы выбираете int где-то смещение от этого указателя, выполняя

p[user]

теперь этот код весь в userMask[2][maskElement][user].

Действительный код C

Чтобы сделать это шаг за шагом с действующим кодом c (не беспокойтесь, если вы еще не все поняли в следующем):

int * userMask[3][4] = { { 0 } };
int ** pa = userMask[2]; /* int*[4] becomes int** implicitly */
int * pi = pa[maskElement];
int i = pi[user];

assert(i == userMask[2][maskElement][user]);

Разница между массивами и указателями

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

int array[5][4][3];
/* int[4][3] implicitly converts to int(*)[3] (pointer to first element) */
int (*parray)[3] = array[0]; 
int ** pint = (int**) array[0]; /* wrong!! */

Что будет, если мы сделаем parray[1] и pint[1]? Первый будет продвигать parray на sizeof(int[3]) байтов (3 * sizeof(int)), второй будет продвигаться только на sizeof( int* ) байтов. Таким образом, в то время как первый дает вам правильный массив array[0][1], второй даст вам ( char * )array[0] + sizeof( int* ), что мы и не хотим. Но захват неправильного смещения - это еще не все. Поскольку он не знает, к какому массиву обращаются, он попытается интерпретировать то, что в pint[1], как int*. Скажем, ваш массив был инициализирован с 0x00. Затем он выполнит следующий шаг индексации, основываясь на адресе 0x00 (например, pint[1][0]). О нет - совершенно неопределенное поведение! Поэтому очень важно подчеркнуть разницу.

* * Заключение тысячи сорок-девять

Это было больше, чем вы просили, но я думаю, что очень важно знать эти детали. Особенно, если вы хотите передать 2d массивы в функции, тогда эти знания действительно полезны.

17 голосов
/ 08 ноября 2008

Это двумерный массив, каждый элемент которого является указателем на int, а все указатели инициализируются нулями.

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

if(userMask[2][maskElement][user] && blah)
    result = true;

В этом случае каждый элемент в userMask должен фактически указывать на массив int с. (int* может указывать на один int или массив int с). Чтобы определить это, проверьте код, который присваивает значения userMask. Например, можно написать:

int userArray[2] = { 10, 20 };

userMask[0][0] = userArray; // userMask[0][0] points to the
                            // first element of userArray.

Тогда следующий код индексирует в userArray:

int value = userMask[0][0][1]; // sets value = userArray[1], giving 20.
10 голосов
/ 08 ноября 2008
int *userMask[3][4] = {0};

- это двумерный массив, в котором каждый член является указателем на int. Кроме того, все члены инициализируются нулевыми указателями.

int (*userMask)[3][4];

будет указателем на двумерный массив целых чисел. Квадратные скобки в С связывают более жестко, чем *, поэтому для создания указателя на массив необходимы скобки

cdecl - это простая утилита, которую вы можете скачать для объяснения сложных объявлений:

cdecl> explain int *userMask[3][4]
declare userMask as array 3 of array 4 of pointer to int

Он также может делать обратное:

cdecl> declare userMask as pointer to array 3 of array 4 of int
int (*userMask)[3][4]
3 голосов
/ 08 ноября 2008
if(userMask[2][maskElement][user] && blah)
   result = true;

Вторая часть заключается в том, что в C нет массивов; есть только указательная арифметика. По определению p[i] всегда эквивалентно *(p+i), поэтому

userMask[2][maskElement][user]

эквивалентно

*((userMask[2][maskElement])+user)

Код где-то назначает вектор (я бы поспорил, что деньги от malloc (3c) или аналогичного вызова) указателю в этом массиве; теперь ваш if говорит

IF пользовательский элемент вектора в userMask [2] [maskElement] не равен нулю

ТОГДА ЕСЛИ бла не равен нулю (из-за оценки && короткого замыкания, второе соединение не будет оценено, если первое соединение 0)

ТО установить результат = true.

2 голосов
/ 08 ноября 2008

Применить правило наизнанку.

int *userMask[3][4] = {0};

Начиная с внутренней части объявления,

userMask

это имя

userMask[3] 

выделяет место для (является вектором) 3 из них

userMask[3][4] 

выделяет место для 4 userMask[3]

int *

говорит нам, что userMask элементы имеют указатель типа на int

, а затем = {0} - инициализатор, в котором все элементы 0. Итак

int *userMask[3][4] = {0};

- это массив int * 3x4, инициализированный равным 0.

0 голосов
/ 08 ноября 2008

это помогает, если вы читаете это так:

type variablename[array_spec];

в этом случае: int * usermask [3] [4];

так что это матрица из int *.

Теперь, поскольку c не различает указатель и массив, вы можете использовать индексирование массива по указателям.

int* i;
int the_int_behind_i = *(i+1);
int also_the_int_behind_i = i[1];

Это необходимо, чтобы я указывал на область, где несколько целых чисел выстроены друг за другом, конечно, как массив.

Обратите внимание, что оператор индекса [], использованный в последних примерах, выглядит как array_spec в первом, но они совершенно разные.

Итак: userMask [2] [maskElement] [user]

выбирает указатель маски пользователя, сохраненный в [2] [maskElement], и оценивает его для пользователя пользователь

0 голосов
/ 08 ноября 2008

userMask[2] относится к типу int*[],
userMask[2][maskElement] имеет тип int*,
и поэтому userMask[2][maskElement][user] имеет тип int.

Декларация

int *userMask[3][4] = {0};

является сокращением для

int *userMask[3][4] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}};

, где каждый из нулей неявно преобразуется в (int*)0.

0 голосов
/ 08 ноября 2008

Я думаю, что оператор обращается к третьей строке массива usermask, затем обращается к указателю maskElement в этой строке, и, поскольку это указатель на int, он может указывать на начало массива int (думать о символьных строках) , что я предполагаю, что это делает, и этот массив индексируется пользователем.

0 голосов
/ 08 ноября 2008

Это матрица, в которой каждый элемент является указателем.

Если бы он указывал на матрицу размера [3] [4], код был бы

int userMask[3][4]={0};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...