За исключением случаев, когда это операнд операторов sizeof
или унарных &
или строковый литерал, используемый для инициализации массива символов в объявлении, N-элемент выражение типа "N" массив T
"(T [N]
) преобразуется (" распадается ") в выражение типа" указатель на T
"(T *
), а значением выражения является адрес первого элемента массив.
Массив объекты не являются указателями. Если вы объявите массив как
char foo[] = "hello";
, он будет выглядеть в памяти следующим образом (адреса приведены только для иллюстрации):
+–––+
0x1000: |'h'|
+–––+
0x1001: |'e'|
+–––+
0x1002: |'l'|
+–––+
0x1003: |'l'|
+–––+
0x1004: |'o'|
+–––+
0x1005: | 0 |
+–––+
Объект foo
не указатель; он не выделяет места для указателя. Выражение foo
в большинстве случаев преобразуется в указатель, в том числе при передаче в качестве аргумента функции:
uc( foo );
То, что получает uc
, является адресом первого элемента, следовательно, объявление
void uc( char *s ) { ... }
Что касается оператора индекса []
, это то же самое - выражение массива преобразуется в указатель на первый элемент, и операция с индексом применяется к этому указателю. Подстрочная операция определена как
a[i] == *(a + i)
При заданном начальном адресе a
, вычислить адрес i
-го объекта указанного типа ( а не i
'-й байт ) после этого адреса и разыменования результата.
Таким образом, вы можете использовать оператор индекса 10 * в индексе указателя, а также выражение массива.
Указатели не имеют , которые должны быть представлены как целые числа - на некоторых старых сегментированных архитектурах они были представлены в виде пары значений (номер страницы и смещение). Кроме того, указатели на разные типы могут иметь разные представления - например, char *
может не выглядеть как int *
, который может не выглядеть как double *
, et c. На настольных системах, таких как x86, они есть, но это не гарантировано.
Редактировать
Из комментария:
при инициализации вектора типа int следующим образом: for( int i=0; i < size; ++i); scanf("%d", &vector[i])
калькулятор использует этот указатель " механизм "для цикла корыта?"
Да, точно. scanf
ожидает, что аргумент, соответствующий спецификатору преобразования %d
, будет адресом объекта int
, то есть выражением типа int *
. Унарный оператор &
возвращает адрес объекта, поэтому, если vector
было объявлено
int vector[N]; // for some value of N
, тогда выражение &vector[i]
вычисляется по адресу i
'-ого элемента массив и тип выражения: int *
.
Помните, что C передает все аргументы функции по значению - формальный параметр в определении функции - это другой объект в памяти чем фактический параметр в вызове функции. Например, учитывая
void foo( T x ) // for any type T
{
x = new_value;
}
void bar( void )
{
T var;
foo( var );
}
формальный параметр x
в foo
- это другой объект в памяти, чем var
, поэтому изменение на x
не влияет на var
. Если мы хотим, чтобы foo
мог писать в var
, то мы должны передать на него указатель:
void foo( T *ptr )
{
*ptr = new_value; // write a new value to the thing ptr *points to*
}
void bar( void )
{
T var;
foo( &var ); writes a new value to var
}
Унарный оператор *
в *ptr = new_value
разыменования ptr
, поэтому выражение *ptr
в foo
эквивалентно var
:
*ptr == var // T == T
ptr == &var // T * == T *
В объявлении , *
просто означает, что объект ptr
имеет тип указателя - он не разыменовывается, поэтому вы можете написать что-то вроде
int x;
int *ptr = &x; // ptr is *not* being dereferenced
int y = 5;
*ptr = y; // ptr *is* being dereferenced