Ответ за последние два также можно вычесть из золотого правила в C:
Декларация следует за использованием.
int (*arr2)[8];
Что произойдет, если вы разыграете arr2? Вы получаете массив из 8 целых чисел.
int *(arr3[8]);
Что произойдет, если вы возьмете элемент из arr3? Вы получаете указатель на целое число.
Это также помогает при работе с указателями на функции. Чтобы взять пример с сигьюса:
float *(*x)(void )
Что происходит, когда вы разыменовываете x? Вы получаете функцию, которую можете вызывать без аргументов. Что происходит, когда вы звоните? Он вернет указатель на число с плавающей точкой.
Приоритет оператора всегда сложен. Однако использование круглых скобок также может сбивать с толку, потому что объявление следует за использованием. По крайней мере, для меня интуитивно arr2 выглядит как массив из 8 указателей на целые, но на самом деле все наоборот. Просто нужно привыкнуть. Достаточно причины, чтобы всегда добавлять комментарий к этим объявлениям, если вы спросите меня:)
редактировать: пример
Кстати, я только что наткнулся на следующую ситуацию: функция, которая имеет статическую матрицу и использует арифметику указателей, чтобы увидеть, находится ли указатель строки вне границ. Пример:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUM_ELEM(ar) (sizeof(ar) / sizeof((ar)[0]))
int *
put_off(const int newrow[2])
{
static int mymatrix[3][2];
static int (*rowp)[2] = mymatrix;
int (* const border)[] = mymatrix + NUM_ELEM(mymatrix);
memcpy(rowp, newrow, sizeof(*rowp));
rowp += 1;
if (rowp == border) {
rowp = mymatrix;
}
return *rowp;
}
int
main(int argc, char *argv[])
{
int i = 0;
int row[2] = {0, 1};
int *rout;
for (i = 0; i < 6; i++) {
row[0] = i;
row[1] += i;
rout = put_off(row);
printf("%d (%p): [%d, %d]\n", i, (void *) rout, rout[0], rout[1]);
}
return 0;
}
Выход:
0 (0x804a02c): [0, 0]
1 (0x804a034): [0, 0]
2 (0x804a024): [0, 1]
3 (0x804a02c): [1, 2]
4 (0x804a034): [2, 4]
5 (0x804a024): [3, 7]
Обратите внимание, что значение border никогда не меняется, поэтому компилятор может оптимизировать его. Это отличается от того, что вы могли бы изначально использовать: const int (*border)[3]
: он объявляет границу как указатель на массив из 3 целых чисел, который не изменит значение, пока существует переменная Тем не менее, этот указатель может быть направлен на любой другой такой массив в любое время. Вместо этого мы хотим такое поведение для аргумента (потому что эта функция не меняет ни одно из этих целых чисел). Декларация следует за использованием.
(с .: улучшите этот образец!)