[uquote="Zhuk72",url="/forum/viewtopic.php?p=3126084#p3126084"]ов массив представлялся как {{5 байт},{5 байт},{5 байт}, и т.д.}, ... внутренние {} убрал. Получился одномерный массив, но адресуемый как двухмерный. Я, кстати, такое и раньше делал, ошибок в работе это не вызывало.[/uquote]
В принципе, размерность массива явно указывается при его объявлении количеством скобок []. Внутри него вы можете получать доступ к элементам как угодно, но его размерность от этого не изменится. В регистрах памяти массив, независимо от его размерности, будет располагаться линейно, элемент за элементом, строка за строкой.
В двухмерном массиве array[][] при его инициализации строки можно разделять внутренними скобками {{ , , },{ , , },{ , , },{ , , }}. В одномерном массиве такая запись приведет (может привести) к предупреждению "скобки вокруг скалярной инициализации".
[uquote="Zhuk72",url="/forum/viewtopic.php?p=3125816#p3125816"]Наверное люди делятся на две категории: одни понимают указатели, другие нет

[/uquote]
Эт веееерно

Вообще-то, тут по большей части теоретический вопрос. В умных книшшках много раз описывались указатели, но мало прочесть, надо опробовать самому на деле. И автору (Zhuk72) желательно было бы потренироваться "на кошках", т.е. на простом абстрактном примере, а не влезать сразу к шрифтам и дисплею.
Указатель подобен курсору в текстовом редакторе, или прямоугольной рамке размером в один символ (
равносильно char *ptr), и эта рамка может перемещаться по строке текста. Позиция указателя будет отсчитываться от начала строки (
т.е. значение переменной ptr, хранящей некоторый адрес из адресного пространства). Рамку можем перемещать в любую позицию. Например, перемещаем на первую букву в выбранном слове (
char *ptr = word1 ) и в рамке видим эту букву. Рамку можем двигать на какое-то число позиций вправо или влево по слову (
ptr += 5; ptr--) и наблюдать в рамке буквы (
a = *ptr ). Номер позиции рамки от начала всей строки мы записываем в свободное место той же строки (
переменная ptr сама находится в том же адресном пространтве), а так же в свободном месте строки можем записывать буквы, которые видим в рамке (
переменная, в которую читается по указателю, тоже находится в том же адресном пространстве). Причем, мы можем перескочить за границы интересующего нас слова или вообще попасть туда, где нет текста, но есть место под буквы (
перемещаясь по массиву, мы можем запросто выйти за его границы и ошибок компиляции не будет). Причем, буквы могут быть разной ширины, и чтобы увидеть в рамке именно то, что интересует, нужно выбирать рамку строго по ширине конкретной буквы (
т.е. указатель и переменная должны быть одного типа (кол-ва байт)). К тому же, буквы могут не быть в составе осмысленных слов (
указатели могут указывать на отдельные переменные).
Просто так взяв рамку и бросив ее на текст, мы можем попасть на любую букву. Это к слову об использовании неинициализованных указателей, которые могут указывать на любое случайное место. Поэтому, взяв рамку в руки, обязательно определим, какое место в строке нас интересует. То есть, объявив указатель, скажите ему, на что он указывает. В крайнем случае, напишите
char *pa = (void*)0, сделав его "нулевым указателем". (чисто технически он указывает на адрес 0 в адресном пространстве). Вместо 0 вы можете записать любой другой адрес, либо получить адрес какой-то переменно с помощью символа &.
Встречается ситуация, когда указатель может указывать на переменную не своего типа. Тогда выдается предупреждение о несовместимости типов переменной и указателя на нее. Образно выражаясь, ширина рамки не совпадает с шириной символа. И если это сделано сознательно, а не в результате ошибки, то можно выполнить приведение типа (подогнать ширину рамки вручную по месту). Вот в таком примере мы сможем четырехбайтную переменную
var разложить на отдельные байты в отдельных переменных
byte3 - byte0. (обратная последовательность связана с тем, что младший байт имеет больший адрес, чем старший)
Код: Выделить всё
int var = 0x12345678;
char *ptr = (char*)&var; // указатель будет указывать только на 1 байт переменной var
byte3 = *ptr++; // чтение переменной по указателю и последующий инкремент указателя, передвигая его на следующий байт
byte2 = *ptr++;
byte1 = *ptr++;
byte0 = *ptr;
Используя возможности приведения типа, можно создать указатель на void, то есть указатель на неопределенный тип переменной, и использовать этот указатель как универсальный, для указания на любой тип. Объясняется это просто - тип void имеет размер в 1 байт (по соглашению в новых редакциях языка)
Код: Выделить всё
void *pv = (void*)0; // "нулевой" указатель на void
int a = 0x12345, x;
char b = 'A', y;
pv = &a; // получает адрес переменной любого типа
x = *((int*)pv); // чтение по указателю с приведением указателя к типу читаемой переменной. x = 0x12345
pv = &b;
y = *((char*)pv); // y = 'A'
/* можно пойти еще дальше и объявить указатель непосредственно на char, который будет выражаться через указатель на void с приведением типа */
char *pc;
pc = (char*)pv;
/* и теперь использовать уже его */
char symb;
symb = *pc; // фактически, symb = b непосредственно
*pc = 'B'; // фактически, b = 'B' непосредственно
без приведения типа указателя компилятор будет выдавать предупреждение о несовместимости типов. А
void* - это в большей мере условность, наглядно показывающая универсальный указатель. Этот механизм используется для передачи через параметры функции данных произвольного размера, в том числе и структуры. Параметр функции используется как "универсальный передатчик". Метод используется во FreeRTOS при передаче произвольных параметров в создаваемую задачу.
Вот вкратце и есть суть указателей. Как видим, они используются не только для работы в массивах, но и для создания универсальных взаимосвязей между переменными в разных частях кода. Поэтому освоить указатели - очень даже важно для продвинутого написания кода. "уровень - эдвансд юзер", так сказать
------
Что касается доступа к элементам двухмерного массива через индексацию и через указатели, тут вот какая штука.
Допустим, у нас есть двухмерный массив
array[3][5] = {{0,1,2,3,4},{5,6,7,8,9},{10,11,12,13,14}}; с одинаковой длиной строк:
Доступ к произвольному элементу при помощи индексации осуществляется по типу X-Y (не забываем, что индексация происходит с 0, как по горизонтали, так и по вертикали!)
Код: Выделить всё
char a;
int x, y;
x = 2;
y = 1;
a = array[y][x];
a = array[y+1][x-2];
таким способом удобно получать доступ к произвольному элементу по его координатам.
А вот доступ по порядковому номеру (по смещению от начала) удобнее выполняется через указатель.
Код: Выделить всё
char *ptr = &array[0][0]; // создается указатель на элементы массива и ему сразу присваивается адрес начала массива
ptr += 8; // смещение указателя на 8 позиций от начала массива
a = *ptr; // получение элемента 8-го массива
ptr++; // указатель перемещается на следующий элемент массива
a = *ptr; // получение следующего элемента массива
ptr -= 3; // указатель перемещается на 3 позиции назад от текущей
a = *ptr;
a = *ptr++; // получение текущего элемента с последующим смещением указателя на след.элемент
ptr += (1 * 5); // перемещение указателя на следующую строку ниже. 1 - число строк вниз, 5 - ширина массива (X)
a = *ptr; // получение элемента строкой ниже (12-й)
однако, как видим, произвольный доступ по координатам X-Y через указатель записывается гораздо хуже, потому что нужно знать именно ширину строки, т.е. количество элементов в строке.
Таким образом, указатели хорошо подходят для последовательного считывания в одномерном или двухмерном массиве. Но указатели усложняют жизнь, когда нужно прочитать некоторую ограниченную область в двухмерном массиве, либо при произвольном доступе в координатах X-Y.
С таким простым шрифтом, как у Zhuk72, можно работать хоть как - по указателю или по индексации, разницы большой нет.