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


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

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

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

12 426

12 ответов:

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

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

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

использовать cdecl программа, как предложено компанией K&Р.

$ 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];

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

int *(arr3[8]);

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

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

float *(*x)(void )

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

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

редактировать: пример

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

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

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

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

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

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

"ptr указатель на " перейти к правильным ..ее") " теперь идите налево ее а "(" выходите идите направо "() " Итак "к функции, которая не принимает аргументов "go left" и возвращает указатель "go right" to массив "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

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

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