Просто чтобы немного расширить это, помните, что массивы C++ - это точно массивы C. Итак, все, что у вас есть, — это адрес участка памяти, который претендует (без каких-либо гарантий) на массив чего-то.
Обновлять
Хорошо, мы расширим немного больше.
C (и, следовательно, C++) на самом деле не имеет массивов как таковых. Все, что у него есть, это адреса, указатели. Итак, когда вы делаете что-то массивом, на самом деле происходит следующее: вы сообщаете компилятору, что какая-то переменная представляет собой адрес.
В C полезно различать декларацию и определение. В объявлении вы просто даете чему-то имя и тип; в определении вы фактически выделяете место.
Итак, если мы начнем с определения массива, например
int ar[100];
это означает, что мы сообщаем компилятору, что нам нужно место для 100 int
, мы хотим, чтобы все они были выделены в одном фрагменте, и мы собираемся использовать для него имя ar
. Оператор sizeof
дает количество байтов, используемых типом или объектом, поэтому наш массив ar
будет занимать 100×sizeof(int)
байт. На большинстве машин это будет 400 байт, но это зависит от машины к машине.
Если мы определим переменную
int * ar_p; // using '_p' as a reminder this is a pointer
мы определяем пространство для переменной, которая будет содержать адрес. Его размер будет sizeof(int*)
, что обычно равно 4 или 8, но на некоторых машинах может быть от 2 до 16 на некоторых машинах, с которыми вы вряд ли скоро столкнетесь.
имя массива — ar
. Компилятор преобразует это имя в адрес, поэтому мы можем сохранить этот адрес с помощью
ar_p = ar ; // THIS WORKS
Теперь предположим для удобства, что наш массив ar
начинается с позиции 1000 в памяти.
Для этого имени ar
нет места, выделенного под него; это как константа, число. Итак, вы не можете отменить это назначение
ar = ar_p ; // THIS WON'T WORK
по той же причине вы не могли сказать
1000 = ar_p ; // THIS WON'T WORK EITHER
т. е. вы не можете изменить значение 1000. (В ранних версиях FORTRAN этот прием срабатывал по сложным причинам. Это была ошибка. Вы никогда не жили, пока не попытались отладить программу, в которой значение 2 равно 3.)
Массивы в C всегда отсчитываются от нуля, то есть первый индекс всегда равен нулю. Любые другие индексы — это просто адреса, вычисленные с использованием индекса. Итак, ar[0]
— это просто адрес 1000 плюс 0 байт смещения, или 1000. ar[1]
— это 1000 плюс 1, умноженный на размер int
, поэтому следующий int закончен. И на самом деле, это всегда верно в C.
Это называется ссылка на массив.
Когда мы используем синтаксис *ar_p
, мы сообщаем компилятору, что нужно получить объект ПО адресу, содержащемуся в ar_p
. `.
Это называется разыменованием указателя.
Если мы скажем
ar_p = ar;
тогда *ar_p
и ar[0]
относятся к одному и тому же.
Когда мы говорим ar[0]
, мы сообщаем компилятору, что нам нужна вещь по адресу 0 байт от ar
. ar[1]
— это адрес один int
или 4 байта от ar
. Итак, *(ar_p+3)
относится к тому же, что и ar[3]
. (Нам нужны круглые скобки, потому что мы хотим сначала добавить 3 к адресу, а затем посмотреть содержимое. *ar_p+3
сначала получит содержимое, на которое указывает ap_p
, а затем добавит к нему 3.
Дело в том, что C не знает и не заботится о том, насколько велик массив на самом деле. Если я приду и сделаю ar[365]
, компилятор с радостью сгенерирует код для поиска в ячейке 1000+(365×sizeof(int)
). Если это в вашем массиве, хорошо, но если это просто случайная память, это тоже хорошо. Си не волнует.
(Помните, что С происходит из телефонной компании. Нам все равно, нам это не нужно. Мы телефонная компания.)
Итак, теперь мы знаем некоторые правила, которые я перенес сюда. Прочитайте ≡ as эквивалентно или совпадает с.
На что вы можете положиться:
foo(TYPE t[])
≡ foo(TYPE * t)
Поскольку C не знает разницы между указателями и массивами, вы можете объявить любой из них. Когда вы определяете функцию, вы можете написать
void foo(int[] ar){
or
void foo(int* ar){
и получить точно такой же эффект.
Это было выше. Везде, где вы можете написать ar[i]
, вы можете заменить его на *(ar+i)
. (На самом деле есть странный побочный случай, который нарушает это, но вы не столкнетесь с ним как новичок.)
- где
TYPE *t
, (t+i)
будут равны адресу t
плюс i*sizeof(TYPE)
Объяснил и это выше. Когда вы индексируете массив, например ar[42]
, это означает, что вы хотите, чтобы 42-й номер был от начального адреса. Итак, если вы используете int
, то вам нужно переместиться более 42 раз, независимо от ширины int
, то есть sizeof(int)
.
Итак, это все С, и поскольку С++ определяется как разновидность С, все это верно и для С++. КРОМЕ
- если только
TYPE
не является определяемым пользователем типом, который перегружает operator[]
и operator*
.
в C++ вы можете решить, что хотите определить новый тип, который действует так же, как и любой другой тип, но вы можете изменить то, как язык выполняет определенные действия. Таким образом, программист может решить перегрузить, т. е. заменить, стандартное поведение операторов ссылки на массив и разыменования указателя чем-то собственным. Как новичок, вы не должны сталкиваться с этим в ближайшее время, но вы должны знать об этом.
person
Charlie Martin
schedule
18.04.2009