Читайте также: |
|
ГЛАВА 8. УКАЗАТЕЛИ И МАССИВЫ
Указатели и одномерные массивы
· доступ к элементам массива
· передача одномерных массивов в функцию
Указатели и двумерные массивы
· массивы указателей
· указатели на указатели
· массивы указателей и двумерные массивы
Использование указателей при работе с обычными переменными не имеет особого смысла, а при использовании арифметических операций с указателями даже опасно, так как нет гарантии, что переменные будут расположены в памяти в том порядке, в котором они были объявлены.
Одно из основных применений указателей – работа с массивами. В языке С++ указатели и массивы тесно связаны между собой. В С++ указатель, который ссылается на массив, можно индексировать так, как если бы это было имя массива.
Указатели и одномерные массивы
Поскольку каждый элемент одномерного массива занимает в памяти компьютера одинаковое количество байтов, при этом, все элементы массива расположены подряд, это позволяет эффективно использовать указатели и арифметические операции при работе с ним.
При объявлении массива ему выделяется память, однако, после этого имя массива воспринимается как константный указатель того типа, к которому относятся элементы массива, и содержит адрес первого элемента (элемент с нулевым индексом) массива.
Имя массива является константным указателем на начало массива.
Применение операции адреса (&) к первому элементу массива даст адрес массива. Например, если объявлен массив
int mas[6];
то записи mas и &mas[0] эквивалентны и определяют адрес 1-го элемента массива, т.е. адрес самого массива. Оба значения являются константами типа указатель, их нельзя изменять на протяжении всей работы программы. Однако эти значения можно присваивать переменным типа указатель:
int mas[6];
int * p;
p = &mas[0]; // или p = mas; – указателю p
// присваивается адрес массива
Индекс элемента массива означает смещение элемента от начала массива на величину, равную
индекс * sizeof (тип)
байтов, где тип – тип элементов массива.
Используя операцию разыменования (*), действие операции индексирования ([]) можно объяснить так:
имя_массива[индекс] = *(имя_массива + индекс)
Поэтому, например, для массива mas записи
mas[i] и *(mas + i) обозначают одну величину – значение соответствующего элемента массива, а записи
&mas[i] и (mas + i) обозначают один адрес элемента.
Индекс элемента массива определяет не его номер, а смещение относительно начала массива. Именно этим объясняется то, что индексы массивов начинаются с нуля.
Доступ к элементам массива
Если указателю присвоен адрес массива, то доступ к элементам массива можно осуществлять как с помощью индексирования имени массива, так и с помощью введенного указателя на начало массива. Если указатель p указывает на некоторый элемент массива, то p + 1 указывает на следующий элемент, p + i – на i -ый элемент после p, а p – i – на i -ый элемент перед p. Другими словами, если p указывает на mas[0], то:
(p+i) – адрес элемента mas[i]
*(p+i) – содержимое элемента mas[i]
Отсюда следует, что доступ к i - ому элементу одномерного массива mas, адрес начала которого хранится в указателе p, возможен следующими способами:
mas[i] *(mas + i) p[i] *(p + i)
При работе с массивами возможен любой из предлагаемых вариантов. Однако следует отметить, что компилятор преобразует все индексные выражения в адресные, т.е. встречая запись mas[i], компилятор преобразует её в *(mas+i), а запись p[i] – в *(p + i).
Указатель, который ссылается на массив, можно индексировать так, как если бы это было имя массива. Доступ к элементам массива возможен как с помощью индексов, так и с помощью указателей.
Несмотря на то, что объявления
int mas[6]; и int * p;
во многом схожи, они не являются полными аналогами.
Между именем массива и указателем, имеющим значение адрес массива, имеется существенное различие. Указатель – это переменная, содержащая некоторый адрес, поэтому можно использовать операторы:
int mas[] = {1, 2, 3, 4, 5};
int *p; p = mas;
int y = *p; // содержимое mas[0] присваивается y
*p++; p++;
(*p) ++;
Выражение *p++ разыменовывает указатель *p и передвигает его к следующему элементу массива. Это возможно, поскольку известен тип адресуемых данных, а значит, и их размер. Выражение (*p)++ – разыменовывает указатель *p и к значению добавляет единицу.
А имя массива – это указатель-константа, поэтому следующие операторы не допускаются:
mas = p; mas++; *mas++;
Язык С++ позволяет программам разыменовывать имена массивов с помощью такого выражения, как *имя_массива:
int mas[5];
int x = *mas; // x = mas[0]
*mas = 77; // mas[0] = 77;
cout<<&mas<<" "<<&mas[0]; // вывод адреса массива mas
// Пример 8.1 Адресация элементов одномерного массива mas.
#include <iostream>
using namespace std;
const int n = 5;
int main() {
int mas[n]; // массив из n целых чисел
int * p = mas; // p – указатель на массив типа int
for(int i = 0; i < n; i++) // инициализация элементов массива,
mas[i] = i; // используя индекс
for(int i = 0; i < n; i++) // вывод массива, используя
cout<<*p++<<' '; // указатель p – 0 1 2 3 4
cout<<endl;
for(int i = 0; i < n; i++){
p = &mas[i];
cout<<*p<<' '; // 0 1 2 3 4
}
cout<<endl;
for(int i = 0; i < n; i++)
cout<<*(mas+i)<<' '; // 0 1 2 3 4
cout<<endl;
p = mas; *p = 5;
for(int i = 0; i < n; i++)
cout<<(*p)++<<' '; // 5 6 7 8 9
cout<<endl;
for(int * p = mas; p < mas + n; p++) // обнуление элементов
*p = 0; // массива mas – 0 0 0 0 0
system("pause");
}
Следует отметить, что нельзя выполнить оператор
int *q = &mas; // нельзя, т.к. типы не совпадают
но, выполнив операцию приведения типа, можно выполнить следующие операторы:
int *q = (int*)&mas; // можно, выполнили приведение типа
cout<<q<<endl; // можно, вывод адреса массива mas
Передача одномерных массивов в функцию
При передаче массива в функцию её параметр должен быть объявлен как указатель. Как раз это вариант чаще всего используется профессиональными программистами. В этом случае функция имеет следующий вид:
void vivod (int * num, int size){
for(int i = 0; i < size; i++)
cout<<num[i]<<' ';
cout<<endl;
}
Вызов функции в данном случае для массива mas[6] будет иметь следующий вид:
vivod (mas, 6);
В общем случае вызов функции имеет вид:
имя_функции (имя массива, размер массива);
Однако отдельный элемент массива, используемый в качестве аргумента функции, обрабатывается подобно обычной переменной.
Когда программа передаёт массив в функцию, C++ передаёт адрес памяти первого элемента этого массива. Используя переменную-указатель, функция может перемещаться по содержимому массива, просто увеличивая значение указателя.
Если массив используется в качестве аргумента функции, функции передаётсяадрес массива (адрес элемента массива с нулевым индексом).
Следует понимать, что, так как функция получает адрес массива, то она может изменить реальное содержимое массива, используемого при вызове функции. Покажем это на следующем примере.
// Пример 8.2. Функции fun() изменяет массив, используемый
// при вызове функции.
#include <iostream>
using namespace std;
const int DIM = 20;
void fun (int * num, int size);
int main() {
int x[DIM], n;
cout<<"Vvedi n: "; cin>>n;
for(int i = 0; i < n; i++)
x[i] = i + 1;
for(int i = 0; i < n; i++)
cout<<x[i]<<' '; // 1 2 3 4 5
cout<<endl;
fun (x, n); // вызов функции fun()
for(int i = 0; i < n; i++)
cout<<x[i]<<' '; // 2 4 6 8 10
cout<<endl;
system("pause");
}
void fun (int * num, int size){
for(int i = 0; i < size; i++)
num[i] = num[i] * 2; // или num[i]*= 2;
cout<<endl;
}
// Пример 8.3. Дан одномерный целочисленный массив. Поменять
// местами минимальный и максимальный элементы массива.
// Основные этапы решения задачи (ввод массива, вывод массива,
// обработка массива) оформить в виде функций.
#include <iostream>
using namespace std;
void init (int*, int);
void display (int*, int);
void obmen (int*, int);
const int DIM = 20;
int main() {
int a[DIM], n;
cout<<"Vvedi n: "; cin>>n;
init (a, n); // вызов init()
cout<<"\n\tIsxodni massiv\n"; display (a, n); // вызов display()
obmen (a, n); // вызов obmen()
cout<<"\n\tRezult massiv\n"; display (a, n); // вызов display()
system("pause");
}
void init (int * a, int n){
srand(n);
int * p = a;
for(int i = 0; i < n; i++)
p[i] = rand() % 20 - rand() % 10;
}
void display (int * a, int n){
for (int * p = a; p < a + n; p++)
cout<<*p<<' ';
cout<<endl;
}
void obmen (int * a, int n){
int imin = 0, imax = 0;
for(int i = 0; i < n; i++){
if(*(a + i) < *(a + imin)) imin = i;
if(*(a + i) > *(a + imax)) imax = i;
}
int temp = a[imin]; a[imin] = a[imax];
a[imax] = temp;
}
// Пример 8.4. Дан одномерный массив целых чисел. Отсортировать
// массив по неубыванию. Массив и его размер ввести с клавиатуры.
// Основные этапы решения задачи оформить в виде функций.
#include <iostream>
using namespace std;
void init (int*, int);
void display (int*, int);
void sort (int*, int);
const int DIM = 50;
int main() {
int a[DIM], n;
cout<<"Vvedi n: "; cin>>n;
init (a, n);
cout<<"\n\tIsxodni massiv \n"; display (a, n);
sort (a, n);
cout<<"\n\tOtsortirrov. massiv\n"; display (a, n);
system("pause");
}
void init (int * p, int n){
srand(n);
for(int i = 0; i < n; i++)
p[i] = rand() % 20 - 10;
}
void display( int * p, int n){
for(int i = 0; i < n; i++)
cout<<p[i]<<' ';
cout<<endl;
}
void sort (int * p, int n){
for(int i =1; i < n; i++)
for(int j = 0; j <n-i; j++)
if(p[j] > p[j+1]){
int x = p[j]; p[j] = p[j + 1]; p[j + 1] = x;
}
}
Дата добавления: 2015-01-29; просмотров: 180 | Поможем написать вашу работу | Нарушение авторских прав |