Чтобы это немного расширить, помните, что массивы C ++ - это в точности массивы C. Таким образом, все, что у вас есть, - это адрес фрагмента памяти, который подразумевается (без каких-либо гарантий) как массив чего-то.
Обновление
Хорошо, мы расширим немного больше.
C (и, следовательно, C ++) на самом деле не имеет «массивов» как таковых. Все это есть адреса, указатели. Поэтому, когда вы делаете что-то «массив», то, что действительно происходит, вы говорите компилятору, что некоторая переменная представляет адрес.
Полезно проводить различие в C между объявлением и определением . В объявлении вы просто даете что-то имя и тип; в определении вы фактически выделяете пространство.
Итак, если мы начнем с определения массива типа
int ar[100];
это означает, что мы говорим компилятору, что нам нужно пространство для 100 int
, мы хотим, чтобы он был распределен в одном фрагменте, и мы будем использовать для него имя ar
. Оператор sizeof
дает количество байтов, используемых типом или объектом, поэтому наш массив ar
будет занимать 100 & times; 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
, так что next 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)
). Если это в вашем массиве, хорошо, но если это просто случайная память, это тоже хорошо. C не волнует.
(Помните, C приходит от телефонной компании. «Нам все равно; нам это не нужно. Мы телефонная компания.»)
Итак, теперь мы знаем некоторые правила, которые я здесь перенес. Прочитайте "& эквивалент;" как «эквивалентно» или «такое же, как».
От чего вы можете зависеть:
foo(TYPE t[])
& экв .; foo(TYPE * t)
Поскольку C не знает различий между указателями и массивами, вы можете объявить любой из них. Когда вы определяете функцию, вы можете написать
void foo(int[] ar){
или
void foo(int* ar){
и получите точно такой же эффект.
Это было выше. Везде, где вы можете написать ar[i]
, вы можете заменить его на *(ar+i)
. (На самом деле есть странный случай, который ломает это, но вы не столкнетесь с этим как новичок.)
- где
TYPE *t
, (t+i)
будет равен адресу в t
плюс i*sizeof(TYPE)
Объяснил это также выше. Когда вы индексируете в массив, например, ar[42]
, это означает, что вы хотите получить 42-е, начиная с начального адреса. Таким образом, если вы используете int
, то вам нужно подняться в 42 раза шире, чем int
, то есть sizeof(int)
.
Теперь, это все C, и, поскольку C ++ определяется как "вид" C, все это также относится и к C ++. КРОМЕ
- , если
TYPE
не определенный пользователем тип, который перегружает operator[]
и operator*
.
в C ++ вы можете решить, что хотите определить новый тип, который действует точно так же, как любой другой тип, но вы можете изменить способ, которым язык делает определенные вещи. Таким образом, программист может решить "перегрузить" - т.е. заменить - поведение по умолчанию операторов массива и разыменования указателя чем-то своим собственным разработкой. Как новичок, вы не должны столкнуться с этим в ближайшее время, но вы должны знать об этом.