Мне нужен указатель на статический двумерный массив. Как это сделать?
static uint8_t l_matrix[10][20];
void test(){
uint8_t **matrix_ptr = l_matrix; //wrong idea
}
Я получаю всевозможные ошибки, например:
- предупреждение: присваивание из несовместимого типа указателя
- индексируемое значение не является ни массивом, ни указателем
- ошибка: недопустимое использование гибкого элемента массива
Ответы:
Здесь вы хотите сделать указатель на первый элемент массива
С typedef это выглядит чище
Тогда вы снова сможете наслаждаться жизнью :)
Остерегайтесь мира указателей / массивов в C, вокруг этого много путаницы.
редактировать
Рассмотрение некоторых других ответов здесь, потому что поля комментариев слишком короткие, чтобы их там делать. Было предложено несколько альтернатив, но не было показано, как они себя ведут. Вот как они это делают
Если вы исправите ошибку и добавите оператор адреса,
&
как в следующем фрагментеЗатем он создает указатель на неполный массив элементов типа массив 20 uint8_t. Поскольку указатель указывает на массив массивов, вы должны получить к нему доступ с помощью
И поскольку это указатель на неполный массив, вы не можете использовать ярлык
Поскольку для индексации требуется, чтобы был известен размер типа элемента (индексирование подразумевает добавление целого числа к указателю, поэтому оно не будет работать с неполными типами). Обратите внимание, что это работает только в
C
, потому чтоT[]
иT[N]
являются совместимыми типами. В C ++ нет концепции совместимых типов , поэтому он отклоняет этот код, потому чтоT[]
иT[10]
являются разными типами.Следующая альтернатива вообще не работает, потому что тип элемента массива, когда вы рассматриваете его как одномерный массив, не является
uint8_t
, ноuint8_t[20]
Следующее - хорошая альтернатива
Вы получаете доступ к нему с помощью
Его преимущество состоит в том, что он сохраняет размер внешнего измерения. Так что вы можете нанести на него sizeof
Есть еще один ответ, который использует тот факт, что элементы в массиве хранятся непрерывно.
Теперь это формально позволяет вам получить доступ только к элементам первого элемента двумерного массива. То есть выполняется условие
Вы заметите, что это, вероятно, работает до
10*20-1
, но если вы включите анализ псевдонимов и другие агрессивные оптимизации, какой-нибудь компилятор может сделать предположение, которое может нарушить этот код. Сказав это, я никогда не сталкивался с компилятором, который не работал бы на нем (но опять же, я не использовал эту технику в реальном коде), и даже в C FAQ есть эта техника (с предупреждением о ее UB'ness ), и если вы не можете изменить тип массива, это последний вариант, который вас спасет :)источник
uint8_t *d[20]
, что создает массив из трех указателей на uint8_t, но в данном случае это не сработает.Чтобы полностью понять это, вы должны усвоить следующие концепции:
Массивы - это не указатели!
Во-первых (и об этом достаточно сказано), массивы - это не указатели . Вместо этого в большинстве случаев они «распадаются» на адрес своего первого элемента, который может быть назначен указателю:
Я предполагаю, что это работает таким образом, чтобы можно было получить доступ к содержимому массива, не копируя его все. Это всего лишь поведение типов массивов и не означает, что это одно и то же.
Многомерные массивы
Многомерные массивы - это всего лишь способ «разбить» память таким образом, чтобы компилятор / компьютер мог понять и оперировать.
Например,
int a[4][3][5]
= массив, содержащий 4 * 3 * 5 (60) «блоков» целочисленной памяти.Преимущество перед использованием
int a[4][3][5]
vs plainint b[60]
заключается в том, что они теперь «разделены» (при необходимости легче работать с их «кусками»), и теперь программа может выполнять проверку привязки.Фактически, они
int a[4][3][5]
хранятся точно так же, какint b[60]
в памяти - с той лишь разницей, что программа теперь управляет им, как если бы они были отдельными объектами определенных размеров (в частности, четыре группы по три группы по пять).Имейте в виду: оба
int a[4][3][5]
иint b[60]
одинаковы в памяти, и единственная разница в том, как они обрабатываются приложением / компилятором.Из этого вы можете ясно видеть, что каждый «раздел» - это просто массив, который программа отслеживает.
Синтаксис
Теперь массивы синтаксически отличаются от указателей . В частности, это означает, что компилятор / машина будет относиться к ним по-разному. Это может показаться очевидным, но взгляните на это:
В приведенном выше примере один и тот же адрес памяти печатается дважды, например:
Однако только один может быть назначен указателю напрямую :
Почему нельзя
a
назначить указателю, аa[0]
можно?Это просто следствие многомерных массивов, и я объясню почему:
На уровне «
a
» мы все еще видим, что у нас есть еще одно «измерение», которого стоит ожидать. На уровне 'a[0]
' мы уже находимся в верхнем измерении, поэтому, что касается программы, мы просто смотрим на обычный массив.Вы можете спросить:
Почему имеет значение, если массив является многомерным, с точки зрения создания на него указателя?
Лучше всего думать так:
«Распад» из многомерного массива - это не просто адрес, а адрес с данными раздела (он также понимает, что его базовые данные состоят из других массивов), который состоит из границ, установленных массивом за пределами первого измерения.
Эта логика «разделения» не может существовать внутри указателя, если мы ее не укажем:
В противном случае значение свойств сортировки массива теряется.
Также обратите внимание на использование круглых скобок вокруг
*p
:int (*p)[5][95][8]
- Это означает, что мы создаем указатель с этими границами, а не массив указателей с этими границами:int *p[5][95][8]
Вывод
Давайте рассмотрим:
Вкратце: многомерные массивы распадаются до адресов, которые несут в себе способность понимать их содержимое.
источник
int *p1 = &(a[0]); // RIGHT !
самом деле это идентичноint *p1 = a;
В
вы можете получить доступ как
ведь двумерные массивы также хранятся как одномерные.
источник
G'day,
Декларация
выделил хранилище для 10 строк по 20 ячеек unit8_t, то есть 200 ячеек размером uint8_t, при этом каждый элемент находится путем вычисления 20 x строка + столбец.
Так не
дать вам то, что вам нужно, и указать на нулевой элемент столбца первой строки массива?
Изменить: подумав об этом немного дальше, разве имя массива по определению не является указателем? То есть имя массива является синонимом расположения первого элемента, т.е. l_matrix [0] [0]?
Edit2: Как уже упоминалось другими, пространство для комментариев слишком мало для дальнейшего обсуждения. Тем не мение:
не обеспечивает выделение памяти для рассматриваемого массива.
Как упоминалось выше и как определено стандартом, утверждение:
выделил 200 последовательных ячеек типа uint8_t.
Ссылаясь на l_matrix с использованием операторов формы:
даст вам содержимое colno'th элемента, найденного в строке rowno.
То же самое и в том случае, если при хранении объекта под рукой задействовано какое-либо заполнение или сдвиг выравнивания байтов. Компилятор автоматически подстраивается под них. По определению стандарта C ANSI.
НТН
веселит,
источник
В C99 (поддерживаемом clang и gcc) есть неясный синтаксис для передачи многомерных массивов функциям по ссылке:
В отличие от простого указателя, это намекает на размер массива, теоретически позволяя компилятору предупреждать о передаче слишком маленького массива и обнаруживать очевидный доступ за пределы.
К сожалению, это не исправляет,
sizeof()
и компиляторы, похоже, еще не используют эту информацию, так что это остается любопытством.источник
static 10
это своего рода гарантия присутствия как минимум 10 элементов, что опять же означает, что размер не фиксирован.static
массив не будет передан по ссылке, что неверно. В любом случае массивы передаются по ссылке. В исходном вопросе был задан другой вариант использования - доступ к элементам 2D-массива с использованием дополнительного указателя в той же функции / пространстве имен.Вы всегда можете избежать возиться с компилятором, объявив массив как линейный и выполнив (row, col) вычисление индекса массива самостоятельно.
это то, что компилятор сделал бы в любом случае.
источник
Базовый синтаксис инициализации указателя, указывающего на многомерный массив:
type (*pointer)[1st dimension size][2nd dimension size][..] = &array_name
Базовый синтаксис для его вызова:
Вот пример:
Выход:
Это сработало...
источник
Сделать это можно так:
источник
Вам нужен указатель на первый элемент, поэтому;
источник
Вы также можете добавить смещение, если хотите использовать отрицательные индексы:
Если ваш компилятор выдает ошибку или предупреждение, вы можете использовать:
источник
c
, поэтому ответ должен быть на том же языке. Обратите внимание на теги.