На что указывает `int *userMask[3][4]`?
Я изменяю некоторый код и натолкнулся на объявление, которое мне трудно понять:
int *userMask[3][4] = {0};
На что именно это указывает? Это матрица, где каждый элемент является указателем? Или это указывает на матрицу размера [3][4]?
Спасибо
Я думаю, мой вопрос, как userMask[2][maskElement][user]
может работать, когда он объявлен как int
, Не будет ли userMask быть int[]
чтобы это работало нормально? Я не должен понимать это право...
Кстати, спасибо за ваше предложение о cdecl Роберте. Однако кто-нибудь знает, как использовать его в командной строке XP? Все, что я могу получить, это синтаксическая ошибка:(
9 ответов
Короткий ответ
Данный userMask объявлен как
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]
? Первый продвинет парри путем sizeof(int[3])
байты (3 * sizeof(int)
), второй будет продвигаться только sizeof( int* )
байт. Так что на самом деле в то время как первый дает вам правильный массив array[0][1]
Второе даст тебе ( char * )array[0] + sizeof( int* )
что-то где мы не хотим, чтобы это было. Но захват неправильного смещения - это еще не все. Поскольку он не знает, к какому массиву обращаются, он попытается интерпретировать то, что находится в pint[1]
как int*
, Скажем, ваш массив был инициализирован с 0x00
, Затем он будет делать следующий шаг индексации на основе адреса 0x00 (Doing pint[1][0]
например). О, нет - совершенно неопределенное поведение! Поэтому очень важно подчеркнуть разницу.
Заключение
Это было больше, чем вы просили, но я думаю, что очень важно знать эти детали. Особенно, если вы хотите передать 2d массивы в функции, тогда эти знания действительно полезны.
Это двумерный массив, где каждый элемент является указателем на int
и все указатели инициализируются нулями.
В своем продолжении вы показываете, что массив используется следующим образом:
if(userMask[2][maskElement][user] && blah)
result = true;
В этом случае каждый элемент в userMask
на самом деле должен указывать на массив int
s. (An 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.
int *userMask[3][4] = {0};
2-мерный массив, где каждый член является указателем на int. Кроме того, все члены инициализируются нулевыми указателями.
int (*userMask)[3][4];
будет указатель на 2-мерный массив целых. Квадратные скобки в C связывают более жесткие, чем *, поэтому скобки необходимы для создания указателя на массив.
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]
if(userMask[2][maskElement][user] && blah)
result = true;
Вторая часть здесь состоит в том, что в C нет массивов; есть только указатель арифметики. По определению, p[i]
всегда эквивалентно *(p+i)
так
userMask[2][maskElement][user]
эквивалентно
*((userMask[2][maskElement])+user)
Код где-то назначает вектор (я бы поспорил, что деньги от malloc(3c) или аналогичного вызова) указателю в этом массиве; теперь ваш, если говорит
ЕСЛИ пользовательский элемент вектора в userMask[2][maskElement] не равен нулю
ТОГДА ЕСЛИ бла не равен нулю (из-за короткого замыкания &&, второе соединение не оценивается, если первое соединение равно 0)
ТО установить результат = истина.
Примените правило наизнанку.
int *userMask[3][4] = {0};
Начиная с внутренней части декларации,
userMask
это имя
userMask[3]
выделяет пространство для (является вектором) 3 из них
userMask[3][4]
выделяет место для 4 userMask[3]
"s
int *
говорит нам, что userMask
элементы указателя типа int
а потом = {0}
инициализатор, где все элементы 0
, Так
int *userMask[3][4] = {0};
массив 3 * 4 типа int *, инициализированный в 0.
Я думаю, что оператор обращается к третьей строке массива usermask, затем обращается к указателю maskElement в этой строке, и, поскольку это указатель на int, он может указывать на начало массива int (думаю, символьные строки), который я Предположим, что это происходит, и этот массив индексируется пользователем.
Это помогает, если вы читаете это так:
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][пользователь]
выбирает указатель маски пользователя, сохраненный в [2][maskElement], и оценивает его для пользователя пользователя
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}};
where each of the zeros is implicitly converted to (int*)0
,
Это матрица, где каждый элемент является указателем.
Если бы он указывал на матрицу размера [3][4], код был бы
int userMask[3][4]={0};