Является ли аргумент массива, переданный функции, не постоянным указателем? - PullRequest
7 голосов
/ 01 апреля 2012

Рассмотрим код:

void foo(char a[]){
   a++;            //  works fine, gets compiled
   //... 
}

Теперь рассмотрим это:

void foo(){
   char a[50];
   a++;            //  Compiler error
   //... 
}

Я слышал, что массив эквивалентен константному указателю и не может быть увеличен, поскольку не является lvalue ...

Тогда почему первый код компилируется, потому что аргументы массива для функций передаются как указатель, т.е. T [] преобразуется в T * для передачи Таким образом, foo (a) передает a в качестве указателя.

Но не будет ли он снова преобразован в T [], потому что объявлен как:

void foo(char a[]);

Ответы [ 6 ]

13 голосов
/ 01 апреля 2012

Когда вы передаете массив в качестве аргумента функции, он распадается на указатель.
Поэтому в теле функции вы увеличиваете указатель, а не массив.

10 голосов
/ 01 апреля 2012

Это довольно прискорбная особенность, унаследованная от языка Си, с довольно отвратительным названием «распад».Поскольку C когда-то не разрешал передавать составные типы по значению, они решили позволить программистам указывать массивы как типы параметров функции, но только косметически.Тип массива распадается на тип указателя, реализуя своего рода семантику передачи по ссылке, отличающуюся от остальной части языка.Ужасно.

Подводя итог (и другие уже говорили об этом), подпись

void foo(char a[]); // asking for trouble

бесцеремонно искажается в

void foo(char *a);

… все ради совместимостис древним кодом CПоскольку вы не пишете древний C-код, вам не следует использовать эту «функцию».

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

void foo( char (&a)[ 50 ] );

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

4 голосов
/ 01 апреля 2012

Я слышал, что массив эквивалентен постоянному указателю

Вы можете думать об этом таким образом, но они не эквивалентны.

Массив распадается на указатель при передаче в функцию, поэтому внутри функции он действителен.

Только потому, что подпись void foo(char a[]) не делает a массивом.

Вне функции это просто массив, и вы не можете выполнять арифметику с указателями на нем.

3 голосов
/ 01 апреля 2012

В C ++ любой параметр функции типа «массив из T» настраивается на «указатель на T».Попробуйте этот код:

void foo(char a[]) {}  
void foo(char* a) {}  //error: redefinition  

Это действительно одна и та же функция.

Итак, почему мы можем передать аргумент массива функции в качестве параметра указателя?Не потому, что массив эквивалентен постоянному указателю, а потому, что тип массива может быть неявно преобразован в r-значение типа указателя.Также обратите внимание, что результатом преобразования является значение rvalue, поэтому вы не можете применить оператор ++ к массиву, вы можете применить этот оператор только к lvalue.

1 голос
/ 01 апреля 2012

Я слышал, что массив эквивалентен константному указателю и не может быть увеличен, поскольку он не является lvalue ...

Почти.

Массив выражение является немодифицируемым lvalue ;он не может быть операндом для таких операторов, как ++ или --, и он не может быть целью выражения присваивания.Это не то же самое, что постоянный указатель (то есть указатель, объявленный как T * const).

Выражение массива будет заменено выражением-указателем, значением которого является адрес первого элемента массива , за исключением , когда выражение массива является операндом sizeof или унарным & операторов, или когда выражение массива является строковым литералом, используемым для инициализации другого массива в объявлении.

Когда вы вызываете функцию с аргументом массива, таким как

int a[N];
...
foo(a);

, выражение a преобразуется из типа "N-элемент массива * 1028указатель * to на int и значение этого указателя передается в foo;таким образом, соответствующий прототип функции должен быть

void foo (int *arr) {...}

Обратите внимание, что в контексте объявления параметров функции T a[] и T a[N] идентичны T *a;во всех трех случаях a объявляется как указатель на T.В функции foo параметр arr является выражением-указателем, которое является изменяемым значением l, и поэтому оно может быть назначено и может быть операндом ++ и * 1044.* операторы.

Помните, что все эти преобразования находятся в массиве expression ;то есть идентификатор массива или другое выражение, которое ссылается на массив объект в памяти.Объект массива (кусок памяти, содержащий значения массива) не конвертируется.

1 голос
/ 01 апреля 2012

Когда вы передаете массив a [] в функцию, он передает значение 'a', адрес, в функцию. так что вы можете использовать его как указатель в функции. Если вы объявляете массив в функции, «a» является константой, потому что вы не можете изменить его адрес в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...