C указатель на массив / массив указателей неоднозначности - PullRequest
440 голосов
/ 13 мая 2009

В чем разница между следующими декларациями:

int* arr1[8];
int (*arr2)[8];
int *(arr3[8]);

Каково общее правило для понимания более сложных объявлений?

Ответы [ 12 ]

419 голосов
/ 13 мая 2009
int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers

Третий такой же, как первый.

Общее правило: приоритет оператора . Он может стать еще более сложным, когда на экране появятся указатели функций.

257 голосов
/ 13 мая 2009

Используйте программу 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 )
124 голосов
/ 13 мая 2009

Я не знаю, есть ли у него официальное название, но я называю его «Правостороннее левитирование».

Начните с переменной, затем идите направо, налево и направо ... и так далее.

int* arr1[8];

arr1 - массив из 8 указателей на целые числа.

int (*arr2)[8];

arr2 - указатель (скобка справа налево) на массив из 8 целых чисел. int *(arr3[8]); arr3 - это массив из 8 указателей на целые числа.

Это должно помочь вам со сложными объявлениями.

25 голосов
/ 02 марта 2011
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 
15 голосов
/ 30 марта 2010

Ответ за последние два также можно вычесть из золотого правила в 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 целых чисел, который не изменит значение, пока существует переменная Тем не менее, этот указатель может быть направлен на любой другой такой массив в любое время. Вместо этого мы хотим такое поведение для аргумента (потому что эта функция не меняет ни одно из этих целых чисел). Декларация следует за использованием.

(с .: улучшите этот образец!)

5 голосов
/ 14 августа 2010
typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];
3 голосов
/ 19 сентября 2014

Как правило, правые унарные операторы (например, [], () и т. Д.) Имеют преимущество перед левыми. Таким образом, int *(*ptr)()[]; будет указателем, который указывает на функцию, которая возвращает массив указателей на int (получите правильные операторы, как только сможете, как только выйдете из скобок)

2 голосов
/ 22 августа 2018

Вот интересный сайт, который объясняет, как читать сложные типы в C: http://www.unixwiz.net/techtips/reading-cdecl.html

2 голосов
/ 11 октября 2015

Вот как я это интерпретирую:

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)

Итак, заменив что-тоElse на (* что-то), мы получим * (* что-то + i), которое является целым числом согласно объявлению. Итак, (* что-то) дает нам массив, который делает что-то эквивалентное (указатель на массив).

2 голосов
/ 09 июля 2012

Я думаю, что мы можем использовать простое правило ..

example int * (*ptr)()[];
start from ptr 

"ptr - это указатель на" идите направо ..its ")" теперь идите налево, его "(" выйди иди направо "()" так "функции, которая не принимает аргументов" идти налево "и возвращает указатель" идти направо " массив "иди влево" целых чисел "

...