char p[5]; // p is a 5-element array of char
char *p[5]; // p is a 5-element array of pointer to char
char (*p)[5]; // p is a pointer to a 5-element array of char
Синтаксис объявления
C построен на типах выражений , а не на объектах. Идея состоит в том, что форма объявления совпадает с формой выражения, как это будет выглядеть в коде.
В приведенных выше случаях мы имеем дело с массивами. В первом случае p
- это массив char
; чтобы получить доступ к конкретному символьному значению, мы просто указали бы в массиве:
val = p[i];
Тип выражения p[i]
равен char
, поэтому объявление p
равно char p[5]
.
В следующем случае p
- это массив указателей на char; чтобы получить доступ к значению, мы индексируем массив и разыменовываем результат:
val = *p[i];
Операторы постфикса, такие как []
, имеют более высокий приоритет, чем унарные операторы, такие как *
, поэтому вышеприведенный анализируется как
val = *(p[i]);
Тип выражения *p[i]
равен char
, поэтому объявление p
равно char *p[5]
.
В последнем случае p
является указателем на массив символов, поэтому для доступа к значению символа мы должны разыменовать указатель массива, а затем добавить в результат индекс:
val = (*p)[i];
Поскольку []
имеет более высокий приоритет, чем унарный *
, мы должны использовать скобки, чтобы явно сгруппировать *
с p
(в отличие от p[i]
). Опять же, тип выражения (*p)[i]
равен char
, поэтому объявление p
равно char (*p)[5]
.
EDIT
Указатели на массивы отображаются в следующих контекстах:
Вы явно берете адрес N-мерного массива:
int x[10];
int (*px)[10] = &x;
Обратите внимание, что хотя выражения x
и &x
дают одно и то же значение (адрес первый элемент массива), они имеют различные типы (int *
против int (*)[10]
).
Вы динамически распределяете объект типа массива:
int (*px)[10] = malloc(sizeof *px);
N-мерное выражение массива «разлагается» на указатель на (N-1) -размерный массив:
int x[10][20];
foo(x);
...
void foo(int (*px)[20]){...}