Указатели и массивы
В Си существует самая тесная связь между указателями и массивами, поэтому лучше эти средства рассматривать вместе.
Как мы знаем, структура массива полностью соответствует структуре оперативной памяти – элементы массива занимают в ней подряд идущие ячейки. Значит, если описан массив:
int mass[5];
то в оперативной памяти для его элементов выделяется пять подряд идущих ячеек:
К i-му элементу этого массива можно обратиться, назвав его индекс: mass[i] .
Доступ к любому элементу массива, осуществляемый по его индексу (номеру), может быть выполнен при помощи указателя, причем это будет сделано быстрее. Опишем переменную ptr как указатель на данные целого типа:
int *ptr;
В результате присваивания
ptr = &mass[0];
эта переменная будет содержать адрес начального (нулевого) элемента этого массива, то есть указатель ptr будет указывать на элемент mass[0]:
Адрес начального элемента любого массива называется базовым адресом этого массива. Таким образом, сейчас указатель ptr содержит базовый адрес массива mass . Если увеличить значение указателя на единицу, то ptr + 1 будет указывать на следующий элемент массива, то есть на mass[1] , ptr + 2 - на элемент mass[2] и так далее. В общем случае, если значение указателя увеличить на k , то можно получить адрес k-го элемента массива mass.
Значит, адрес любого элемента массива равен сумме его базового адреса, который является адресом его начального элемента, и смещения этого элемента от начала массива. Для начального (нулевого) элемента массива это смещение равно нулю, для первого элемента – единице, для второго – двум, для k-го оно равно k. Это верно для массива любого типа. Смысл выражения «увеличить указатель ptr на единицу » , как и смысл любой арифметики с указателями заключается в том, что ptr + 1 указывает на следующий за ptr элемент, а ptr + k на k-й после ptr элемент массива.
Между индексированием элементов массива и арифметикой с указателями существует очень тесная связь, потому что имя любого массива в Си есть адрес его начального элемента, то есть имя массива является его базовым адресом. Значит, в нашем примере присваивание:
ptr = &mass[0];
можно записать в другом виде:
ptr = mass;
Это будет одно и то же: записи &mass[0] и mass эквивалентны.
Из всего этого следует, что в общем случае запись &mass[k] будет эквивалентна записи (mass + k) , а сам k-й элемент массива можно определить как mass[k] или как *(mass + k). С другой стороны, если ptr – указатель, то в выражениях его можно использовать с индексом, то есть запись ptr[k] эквивалентна записи *(ptr + k).
Таким образом, элемент массива в Си разрешается изображать и в виде указателя со смещением, и в виде имени массива с индексом.
Между именем массива и указателем, выступающим в роли имени массива, однако существует различие. Указатель – это переменная, поэтому можно записать ptr = mass или ptr++. Но имя массива не является переменной, и записи типа mass = ptr или mass++ не допускаются.
Помимо рассмотренной операции сложения, над указателями можно выполнять следующие операции:
- складывать и вычитать указатели и целые данные,
- вычитать и сравнивать два указателя, ссылающиеся на элементы одного и того же массива,
- присваивать значение указателя другому указателю того же типа,
- присваивать указателю нуль и сравнивать его с нулем.
Над указателями нельзя выполнять следующие операции:
- складывать два указателя, перемножать их, делить, сдвигать, выделять разряды,
- складывать указатели со значениями типа float и double,
- присваивать указателю одного типа значение указателя другого типа (исключение составляют указатели типа void).
Указатели можно использовать и при работе с многомерными массивами:
int trio[5][2][3];
int *i_ptr;
Описан трехмерный массив trio целого типа и указатель ptr на данные целого типа. Присвоим этому указателю значение базового адреса массива:
i_ptr=&trio[0][0][0];
Необходимо учесть, что для многомерных массивов нельзя использовать операцию присваивания базового адреса указателю в таком виде:
i_ptr=trio;
как это имеет место для векторов (одномерных массивов).
Доступ к j-му элементу i-ой строки k-го слоя массива trio может быть осуществлен или с помощью индексов:
trio[k][i][j]=1;
либо с помощью указателей:
*(i_ptr + k*(2*3) + i*3 + j)=1;
Как и в Паскале, в языке Си запрещается присваивать значения элементов одного массива другому массиву целиком:
float r[2][2], s[2][2];
r = s; // ошибка!
Эти ограничения можно обойти с помощью указателя:
float *f_ptr;
f_ptr = &s[0][0];
r = *f_ptr;
При этом элементам массива r будут присвоены значения соответствующих элементов массива s.
Дата добавления: 2015-10-19; просмотров: 1519;