C указатель на массив / массив указателей

в чем разница между следующие объявления:

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

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

12 ответов


int* arr[8]; // An array of int pointers.
int (*arr)[8]; // A pointer to an array of integers

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

общие правила приоритет операторов. Это может стать еще более сложным, как указатели функции приходят в картину.


использовать ключевое слово 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 )

Я не знаю, есть ли у него официальное название, но я называю его правой-левой штукой(TM).

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

int* arr1[8];

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

int (*arr2)[8];

arr2-указатель (скобка блок справа-слева) на массив из 8 целых чисел.

int *(arr3[8]);

arr3-это массив из 8 указателей на целое число.

Это должно помочь вам со сложными декларациями.


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 

ответ для последних двух также можно вычесть из золотого правила в C:

объявление следует за использованием.

int (*arr2)[8];

что произойдет, если вы dereference arr2? Вы получаете массив из 8 целых чисел.

int *(arr3[8]);

что произойдет, если вы возьмете элемент из arr3? Вы получаете указатель на целое число.

это также помогает при работе с указателями на функции. Взять sigjuice по пример:

float *(*x)(void )

что происходит, когда вы разыменование x? Вы получаете функцию, которую вы можете вызвать без аргументов. Что происходит, когда вы называете это? Он вернет указатель на float.

приоритет оператора всегда сложно, хотя. Однако использование круглых скобок также может привести к путанице, поскольку объявление следует за использованием. По крайней мере, для меня, интуитивно arr2 выглядит как массив из 8 указателей на целые числа, но на самом деле все наоборот. Просто взять некоторые привыкают. Достаточная причина, чтобы всегда добавлять комментарий к этим объявлениям, если вы спросите меня:)

edit: example

кстати, я просто наткнулся на следующую ситуацию: функция, которая имеет статическую матрицу и которая использует арифметику указателя, чтобы увидеть, если указатель строки выходит за рамки. Пример:

#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]

обратите внимание, что значение границы никогда не изменяется, поэтому компилятор может оптимизировать это. Это отличается от того, что вы могли бы изначально использовать:const int (*border)[3]: это объявляет границу как указатель на массив из 3 целых чисел, который не будет изменять значение, пока существует переменная. Однако этот указатель может быть указан на любой другой такой массив в любое время. Вместо этого мы хотим такого поведения для аргумента (потому что эта функция не изменяет ни одно из этих целых чисел). Декларации следует использовать.

(стр. С.: не стесняйтесь улучшать этот образец!)


typedef int (*PointerToIntArray)[];
typedef int *ArrayOfIntPointers[];

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

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

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


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


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

int *something[n];

примечание о приоритете: оператор индекса массива ('[ ]') имеет более высокий приоритет, чем оператор разыменования ('*').

Итак, здесь мы применим "[ ] "перед"*", сделав утверждение эквивалентным:

int *(something[i]);

обратите внимание на то, как в декларации имеет смысл: int num означает (num) является (int),int *ptr или int (*ptr) означает, (значение в ptr) an( 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) == B. Таким образом, указывает на массив целых чисел. В вашем вопросе arr похож на A.

аналогично, в int* (*C) [8], C-указатель на массив указателей на целое число.


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


в указателе на целое число если указатель увеличивается, то он переходит к следующему целому числу.

в массиве указателя, если указатель увеличивается, он переходит к следующему массиву