2014-12-05 3 views
7

Когда я объявляю новый массив, как это:C++ указатель против нотации массива

int foo[5] 

ли foo действительно указатель на первый элемент массива? Смогу ли я это сделать:

*(foo+2) 

для доступа к третьему элементу массива? Скажем, я делаю 2D массива:

int foo[3][4] 

ли foo теперь int**?

+11

* Является ли foo действительно точкой для первого элемента массива? * - Нет, это массив. * Является ли foo теперь int \ * \ *? * - Нет, это 2D-массив. См. [Массивы] (http://stackoverflow.com/questions/4810664/how-do-i-use-arrays-in-c) – chris

+0

@chris, поэтому, если бы я захотел перебрать его с помощью указателя, я бы нужно сделать 'int * ptr = & foo [0]'? – n0pe

+0

@maxmackie 'int * ptr = foo' будет достаточно. –

ответ

5

Нет, «Foo» является типом массива в обоих случаях, но когда указатель ожидается в выражении с «Foo», он неявно преобразован в один (который указывает на массив первого элемента). Все массивы имеют такое поведение. В случае, когда добавление может быть сделано с помощью типов указателей, но не с массивами, 'foo' преобразуется в 'int *'.

*(foo+2) // 'foo' is implicitly converted into 'int *', pointing to 'foo' first element 

foo + 1 //same as above 

Но теперь вы можете спросить, каковы свойства типа «массив» и почему мы должны когда-либо использовать его, а не неявный указатель на первый элемент гипс. Дело в том, что их не много.Вы можете сказать, размер объекта с типом массива, как это:

sizeof(foo) //returns the size which array 'foo' occupies 

И получить его адрес с помощью «&» оператора:

&foo // '&foo' has type of 'int (*)[5]' 

Вы также можете создавать функции с параметрами «массива '(или указатель), чтобы принимать только те, у которых заданный размер (что невозможно, если они являются просто указателями и ожидают, что массивы, переданные для распада, будут такими). Пример:

void func(int (&)[5]); 

void func1(int (*arg)[5]); // should be accessed by '*arg', allow the use of null-pointers 

void func2(int *); //same as the misleading 'void func2(int [5])' or 'void func2(int [6])' etc. 

int foo[5]; 

int foo1[6]; 

func(foo); // 'foo' type is ('int [5]') - ok 

func1(&foo); // '&foo' type is ('int (*)[5]') - ok 

func(foo1); // 'foo1' type is ('int [6]') - not allowed, param type is 'int (&)[5]' ! 

func1(&foo1); // '&foo1' type is ('int (*)[6]') - not allowed, param type is 'int (*)[5]' ! 

func2(foo); // 'foo' is implicitly converted to 'int *' - ok 

func2(foo1); // 'foo1' is implicitly converted to 'int *' - ok 

Во втором случае, когда массив является 2D - применяются те же свойства. Это объявление означает следующее: «массив из 3 элементов с массивом типов из 4 элементов с типом int». Таким образом, это просто массив массивов и ничего больше. Это неявный указатель на преобразование первого элемента не относится к типу 'int **', а вместо 'int (*) [4]', поскольку каждый его элемент является другим массивом.

Заявление может быть написано так же:

int (foo[3])[4]; 

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

int funcReturningArray()[2]; //not allowed 

int funcAcceptingArray(int [2]); //just converted into pointer 

int funcAcceptingArray(int *); //same as above 

Хотя параметры массива синтаксически принято из-за унаследованных причинам (? Или потому, что что-то еще), их реальное значение никогда не переносится, и они просто «подгонялись» к указателям.

Примечание: Неявное преобразование типа массива в указатель его первого элемента иногда называют «Array to decander decay».

+0

Обычно правильный ответ, но его можно улучшить: 1. Вероятно, вы должны сказать, что массив * распадается * на указатель вместо «он неявно отбрасывается в один». Слово cast обычно подразумевает, что программист заставляет компилятор делать что-то против его лучшего знания, а не что-то неявно и без усилий меняет тип под капотом. Вы также можете улучшить читабельность большого блока кода, значительно сократив количество комментариев. Что-то вроде: '// error: wrong type' или' // ok: foo распадается на int * '. – cmaster

+0

'decay' - это ключевое слово. Должно положить это в начале – texasbruce

-4

Никакие массивы не являются указателями, но в выражениях они преобразуются в указатель rvalue к их первым элементам. Таким образом, в этом выражении

*(foo+2) 

на первый Foo преобразуется в RValue указатель, а затем используется арифметика указателей.

Для этого объявления массива

int foo[3][4]; 

имени Foo используется в выражениях преобразуются в RValue указателя типа int (*)[4]

Слово Rvalue означает, что, например, вы не можете писать ++foo

То есть компилятор для имени массива, используемого в выражениях, создает временный объект, который является poimnter для первого элемента массива.

Учтите, что если вы используете, например, оператор sizeof с именем массива, то последний не будет преобразован в указатель. Таким образом, для последнего определения массива

sizeof(foo) 

будет эквивалентно

3 * 4 * sizeof(int) 

в то время как

sizeof(int (*)[4]) 

возвращает размер самого указателя.

И наконец, если вы примените оператор & к имени массива, тогда вы получите указатель на массив. Например

int foo[3][4]; 

int (*ptr_to_foo)[3][4] = &foo; 
+0

Я действительно не понимаю, кто проигнорировал это ответ, это совершенно правильно! – cmaster

+0

_ "Это компилятор для имени массива, используемого в выражениях, создает временный объект, который является poimnter для первого элемента массива." _ Ошибка анализа ... –

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