Когда-то давным-давно, то ли Кернигана, то ли Ричи, то ли обоих (что вряд ли), посетила гениальная мысль: а что если определение сущности будет иметь тот же синтаксис, что и её использование? Это позволит не вводить отдельного синтаксиса для объявлений и определений, ключевых слов и разделов в программе для этого, итп.
Т.е. всё просто: вот сущность, синтаксис использования которой такой-то; чтобы её определить, достаточно её "использовать" точно так же, но предварить именем типа. Гениально.
Указатель
Например, есть определение простого указателя:
int* ptr;
Эту конструкцию следует читать как "операция 'разыменования указателя', применённая к ptr, имеет результатом некий int" или более кратко "разыменовый ptr есть int".
Массив
Определение массива выглядит так:
int m[10];
Эту конструкцию можно читать так: "применение операции 'индексирование' к такой-то переменной даёт такой-то тип". Тут только одно специальное дополнение - вторым аргументом операции индексирования должен быть размер массива.
Важное напоминание. Постольку поскольку использование сущности обычно выполняется в неком выражении, то и определение этой сущности обычно напоминает простое выражение. А выражения состоят из отдельных операций, и, естественно, рассматриваются с учётом приоритетов этих операций. Для дополнительного управления приоритетом операций используются скобки.
Массив указателей на int
Имеется следующая запись:
int *m[10]
Что она обозначает? Ввиду большей приоритетности [] перед *, это выражение рассматривается как "элемент индексированного m даёт нечто, что будучи разыменованным, даёт int",
Указатель на массив
Предыдущий пример можно изменить вот так, то есть добавить круглые скобки:
int (*m)[10]
Здесь в приоритете операция *, так как она заключена в круглые скобки. Таким образом, в коде, глядя на переменную m, можно говорить: "разыменованная переменная m даёт нечто, что будучи индексированным, даёт int".
Есть более тупое объяснение, завязанное на вывод типа "по спирали"
Обычно работает такое правило: ищешь в определении название переменной, от него идёшь сначала направо, а потом налево. Порядок обхода меняется круглыми скобками:
int *a[12]; // массив из указателей на int
int (*a)[12]; // указатель на массив int'ов
int *a[12][12]; // массив массивов указателей на int
int (*a[12])[12]; // массив указателей на массив int'ов