Во-первых, вы имеете в виду «typedef», а не «typecast» в вашем вопросе.
В C указатель на тип T
может указывать на объект типа T
:
int *pi;
int i;
pi = &i;
Вышесказанное легко понять. Теперь давайте сделаем это немного сложнее. Кажется, вы знаете разницу между массивами и указателями (то есть вы знаете, что массивы не являются указателями, хотя иногда они ведут себя как они). Итак, вы должны понимать:
int a[3];
int *pa = a;
Но ради полноты: в присваивании имя a
эквивалентно &a[0]
, то есть указателю на первый элемент массива a
. Если вы не уверены, как и почему это работает, существует множество ответов, объясняющих, когда именно имя массива «распадается» на указатель, а когда нет:
Я уверен, что на SO есть еще много таких вопросов и ответов, я только что упомянул некоторые, которые я нашел из поиска.
Вернуться к теме: когда у нас есть:
int foo[2][4];
foo
имеет тип "массив [2]
массива [3]
из int
". Это означает, что foo[0]
- это массив 3 int
с, а foo[1]
- это массив 3 int
с.
Теперь предположим, что мы хотим объявить указатель, и мы хотим присвоить его foo[0]
. То есть мы хотим сделать:
/* declare p somehow */
p = foo[0];
Вышеуказанное не отличается по форме от строки int *pa = a;
, поскольку типы a
и foo[0]
одинаковы. Итак, нам нужно int *p;
как наше объявление p
.
Теперь, главное, что нужно помнить о массивах, это то, что «правило» о том, что имя массива уменьшается до указателя на его первый элемент, применяется только один раз. Если у вас есть массив массива, то в контексте значений имя массива не будет уменьшаться до типа «указатель на указатель», а скорее на «указатель на массив». Возвращаясь к foo
:
/* What should be the type of q? */
q = foo;
Имя foo
выше - это указатель на первый элемент foo
, то есть мы можем написать выше как:
q = &foo[0];
Тип foo[0]
- это «массив [3]
из int
». Поэтому нам нужно, чтобы q
был указателем на "массив [3]
из int
":
int (*q)[3];
Скобки вокруг q
необходимы, потому что []
связывается более плотно, чем *
в C, поэтому int *q[3]
объявляет q
как массив указателей, и мы хотим указатель на массив. int *(q[3])
сверху эквивалентно int *q[3]
, то есть массиву из 3 указателей на int
.
Надеюсь, это поможет. Вы также должны прочитать C для умничков: массивы и указатели для действительно хорошего учебника по этой теме.
О чтении объявлений в целом: вы читаете их «наизнанку», начиная с имени «переменной» (если она есть). Вы идете влево как можно дальше, если только справа нет []
, и вы всегда соблюдаете скобки. cdecl
должен быть в состоянии помочь вам до такой степени:
$ cdecl
cdecl> declare p as pointer to array 3 of int
int (*p)[3]
cdecl> explain int (*p)[3]
declare p as pointer to array 3 of int
Читать
int (*a)[3];
a # "a is"
(* ) # parentheses, so precedence changes.
# "a pointer to"
[3] # "an array [3] of"
int ; # "int".
Для
int *a[3];
a # "a is"
[3] # "an array [3] of"
* # can't go right, so go left.
# "pointer to"
int ; # "int".
Для
char *(*(*a[])())()
a # "a is"
[] # "an array of"
* # "pointer to"
( )() # "function taking unspecified number of parameters"
(* ) # "and returning a pointer to"
() # "function"
char * # "returning pointer to char"
(Пример из c-faq вопрос 1.21 . На практике, если вы читаете такое сложное объявление, в коде что-то серьезно не так!)