В чем разница между следующими декларациями:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Каково общее правило для понимания более сложных объявлений?
constи volatileклассификаторы, которые являются важным и сложным, не хватает в этой статье.
В чем разница между следующими декларациями:
int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);
Каково общее правило для понимания более сложных объявлений?
constи volatileклассификаторы, которые являются важным и сложным, не хватает в этой статье.
Ответы:
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers
Третий такой же, как первый.
Общее правило - приоритет оператора . Это может стать еще более сложным, поскольку указатели функций входят в картину.
( ) [ ] связываются слева направо и имеют более высокий приоритет, чем *считанные int* arr[8]как массив размера 8, где каждый элемент указывает на int, и int (*arr)[8]как указатель на массив размера 8, который содержит целые числа
Используйте программу cdecl , как предложено K & R.
$ cdecl
Type `help' or `?' for help
cdecl> explain int* arr1[8];
declare arr1 as array 8 of pointer to int
cdecl> explain int (*arr2)[8]
declare arr2 as pointer to array 8 of int
cdecl> explain int *(arr3[8])
declare arr3 as array 8 of pointer to int
cdecl>
Это работает и в другую сторону.
cdecl> declare x as pointer to function(void) returning pointer to float
float *(*x)(void )
Я не знаю, есть ли у него официальное название, но я называю это Right-Left Thingy (TM).
Начните с переменной, затем идите направо, налево и направо ... и так далее.
int* arr1[8];
arr1 это массив из 8 указателей на целые числа.
int (*arr2)[8];
arr2 является указателем (скобка справа-слева) на массив из 8 целых чисел.
int *(arr3[8]);
arr3 это массив из 8 указателей на целые числа.
Это должно помочь вам со сложными декларациями.
int *a[][10]то время как второе преуспевает.
( ) [ ]слева направо и справа налево* &
int *a[4]; // Array of 4 pointers to int
int (*a)[4]; //a is a pointer to an integer array of size 4
int (*a[8])[5]; //a is an array of pointers to integer array of size 5
[5]) представляет внутреннее измерение. Это означает, что (*a[8])это первое измерение и, следовательно, внешнее представление массива. То, на что a указывает каждый элемент в пределах, - это другой целочисленный массив размером 5.
Ответ на последние два также можно вычесть из золотого правила в C:
Декларация следует за использованием.
int (*arr2)[8];
Что произойдет, если вы разыграете arr2? Вы получаете массив из 8 целых чисел.
int *(arr3[8]);
Что произойдет, если вы берете элемент из arr3 ? Вы получаете указатель на целое число.
Это также помогает при работе с указателями на функции. Возьмите пример с сигьюса:
float *(*x)(void )
Что происходит при разыменовании x? Вы получаете функцию, которую можете вызывать без аргументов. Что происходит, когда вы звоните? Он вернет указатель наfloat .
Однако приоритет оператора всегда сложен. Однако использование круглых скобок также может сбивать с толку, потому что объявление следует за использованием. По крайней мере, для меня, интуитивно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 целых чисел, который не изменит значение, пока существует переменная. Тем не менее, этот указатель может указывать на любой другой такой массив в любое время. Вместо этого мы хотим такое поведение для аргумента (потому что эта функция не меняет ни одно из этих целых чисел). Декларация следует за использованием.
(ps: не стесняйтесь улучшать этот образец!)
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
Как правило, правые унарные операторы (например [], ()и т. Д.) Имеют преимущество перед левыми. Таким образом, int *(*ptr)()[];будет указатель, который указывает на функцию, которая возвращает массив указателей на int (получите правильные операторы, как только сможете, как только выйдете из скобок)
error: ‘foo’ declared as function returning an array int foo(int arr_2[5][5])[5];под GCC 8 с$ gcc -std=c11 -pedantic-errors test.c
int *(*ptr)();позволяет использовать выражение, подобное p()[3](или (*p)()[3]), позже.
int *foo(int arr_2[5][5]) { return &(arr_2[2][0]); }и назовите это так: foo(arr)[4];что должно содержать arr[2][4], верно?
Я думаю, что мы можем использовать простое правило ..
example int * (*ptr)()[];
start from ptr
« ptrэто указатель на« идти вправо .. его »)« теперь идите влево его a »(« выйди иди вправо »()« так »для функции, которая не принимает аргументов« уходи влево »и возвращает указатель« ходи вправо "к массиву" иди влево "целых чисел"
), теперь иди налево ... это *«указатель на» иди вправо ... это ), теперь иди налево ... это a (выходите, идите направо ()так "к функции, которая не принимает аргументов" идите направо ... []"и возвращает массив указателей " идите направо ;, так идите налево ... *"на" идти налево ... int"целые числа"
Вот интересный сайт, который объясняет, как читать сложные типы в C: http://www.unixwiz.net/techtips/reading-cdecl.html
Вот как я это интерпретирую:
int *something[n];
Примечание о приоритете: оператор индекса массива (
[]) имеет более высокий приоритет, чем оператор разыменования (*).
Итак, здесь мы применим []предыдущее *, делая утверждение эквивалентным:
int *(something[i]);
Обратите внимание на то, как декларация имеет смысл:
int numозначает, чтоnumэтоintозначает ,int *ptrилиint (*ptr)означает, что (значение вptr) являетсяint, что делаетptrуказатель наint.
Это может быть прочитано как, (значение (значение в i-м индексе чего-то)) является целым числом. Итак, (значение в i-м индексе чего-либо) является (целочисленным указателем), что делает массив массивом целочисленных указателей.
Во втором
int (*something)[n];
Чтобы разобраться в этом утверждении, вы должны быть знакомы с этим фактом:
Примечание о представлении указателя массива:
somethingElse[i]эквивалентно*(somethingElse + i)
Итак, заменив somethingElseна (*something), мы получаем *(*something + i), что является целым числом согласно объявлению. Итак, (*something)нам дан массив, который делает что-то эквивалентное (указатель на массив) .
Я думаю, что второе заявление многих сбивает с толку. Вот простой способ понять это.
Позволяет иметь массив целых чисел, то есть int B[8].
Давайте также иметь переменную A , который указывает на B. Теперь, значение в точке А есть В, то есть (*A) == B. Следовательно, A указывает на массив целых чисел. В вашем вопросе arr похож на A.
Аналогично, в int* (*C) [8]C указатель на массив указателей на целое число.
int *arr1[5]
В этом объявлении arr1представлен массив из 5 указателей на целые числа. Причина: квадратные скобки имеют более высокий приоритет над * (оператор разыменования). И в этом типе число строк фиксировано (5 здесь), но количество столбцов является переменным.
int (*arr2)[5]
В этом объявлении arr2указатель на целочисленный массив из 5 элементов. Причина: здесь скобки () имеют более высокий приоритет, чем []. И в этом типе число строк является переменным, но количество столбцов является фиксированным (5 здесь).
В указателе на целое число, если указатель увеличивается, то идет следующее целое число.
в массиве указателей, если указатель увеличивается, он переходит к следующему массиву