2009-05-13 3 views
401

В чем разница между следующие объявления:C указатель на массив/массив указателей неоднозначности

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

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

+45

Вот отличная статья о чтении сложных объявлений в C: http://unixwiz.net/techtips/reading-cdecl.html – jesper

+0

@jesper К сожалению, квалификаторы 'const' и' volatile', которые являются важными и в этой статье отсутствуют. –

ответ

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

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

Общее правило: operator precedence. Он может стать еще более сложным, поскольку указатели на функцию попадают в изображение.

+4

Итак, для 32-битных систем: int * arr [8];/* Выделено 8х4 байтов для каждого указателя */ int (* arr) [8];/* Выделено 4 байта, только указатель */ – George

+10

Нет. int * arr [8]: выделено 8x4 байт * всего *, 4 байта для каждого указателя. int (* arr) [8] прав, 4 байта. –

+2

Мне следовало перечитать то, что я написал. Я имел в виду 4 для каждого указателя. Спасибо за помощь! – George

114

Я не знаю, имеет ли оно официальное название, но я называю это правым левым Thingy (TM).

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

int* arr1[8]; 

arr1 представляет собой массив из 8 указателей на целые числа.

int (*arr2)[8]; 

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

int *(arr3[8]); 

arr3 представляет собой массив из 8 указателей на целые числа.

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

+16

Я слышал, что это называется именем« The Spiral Rule », которое можно найти здесь [http: //www.c-faq .com/Decl/spiral.anderson.html). – fouric

+5

@InkBlend: Правило спирали отличается от [правого правого правила] (http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html). [Бывший не удается] (http://stackoverflow.com/questions/16260417/the-spiral-rule-about-declarations-when-is-it-in-error) в таких случаях, как 'int * a [] [10] 'в то время как последнее преуспевает. – legends2k

+1

@dogeen Я думал, что термин имеет какое-то отношение к Bjarne Stroustrup :) –

244

Используйте программу cdecl, как это было предложено К & Р.

$ 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) 
14

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

декларации следует использовать.

int (*arr2)[8];

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

int *(arr3[8]);

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

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

float *(*x)(void)

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

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

(пс: не стесняйтесь, чтобы улучшить этот образец)

5
typedef int (*PointerToIntArray)[]; 
typedef int *ArrayOfIntPointers[]; 
23
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 
+0

. Не должен ли быть третий: a - массив указателей на целочисленный массив размером 8? Я имею в виду, что каждый из целых массивов будет иметь размер 8 справа? – Rushil

+2

@ Rushil: нет, последний индекс ('[5]') представляет внутреннее измерение. Это означает, что '(* a [8])' является первым измерением и, таким образом, является внешним представлением массива. Каждый элемент в 'a' ** указывает на ** - это другой целочисленный массив размера 5. – zeboidlund

+0

Спасибо за третий. Я ищу, как написать массив указателей на массив. – Deqing

2

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

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

"ptr является указателем на" go to right ..its ")" now go left its a "(" выйти наружу вправо "()" так "функции, которая не принимает аргументов" go left "и возвращает указатель «go right» to массив «go left» целых чисел »

-7

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

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

+0

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

2

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

2

Вот как я истолковал его

int *something[n]; 

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

Итак, здесь мы будем применять '[]' перед '*', что делает заявление эквивалентно:

int *(something[i]); 

записку о том, как декларация имеет смысл: int num средства (NUM) is (int), int *ptr или int (*ptr) означает, что значение (value at ptr) равно a (int), что делает ptr указателем на int.

Это может быть прочитано как (значение (значение в i-м индексе чего-то)) является целым числом. Таким образом, (значение в i-м индексе чего-то) является (целым указателем), что делает что-то массив целых указателей.

Во втором,

int (*something)[n]; 

Чтобы разобраться в этом заявлении, вы должны быть знакомы с этим фактом:

записку на указатель представления массива: somethingElse [я] является эквивалентно * (somethingElse + i)

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

0

Я предполагаю, что вторая декларация вводит в заблуждение для многих. Вот простой способ понять это.

Позволяет иметь массив целых чисел, то есть int B[8].

Давайте также иметь переменную A, которая указывает на B. Теперь значение в точке A равно B, то есть (*A) == B. Следовательно, A указывает на массив целых чисел. В вашем вопросе arr похож на A.

Аналогичным образом, в int* (*C) [8], C является указателем на массив указателей на целое число.

Смежные вопросы