Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЗФ_ОАиП / Лекции ГГУ Скорины - Программирование.doc
Скачиваний:
179
Добавлен:
21.03.2016
Размер:
2.27 Mб
Скачать

15. Массивы и указатели

В языке С массивы и указатели тесно связаны друг с другом. Любую операцию, которую можно выполнить с помощью индексов массива, можно сделать и с помощью указателей. Т.е. доступ к элементам массива можно осуществлять не только с помощью операции [], но и используя механиз указателей. Вариант с указателями обычно оказывается более быстрым, но и несколько более трудным для непосредственного понимания, по крайней мере, для начинающего.

15.1. Указатели и одномерные массивы

В языке С имя массива – это указатель-константа на первый байт нулевого элемента массива. Другими словами, имя массива имеет значение, равное адресу нулевого элемента массива или равное адресу начала самого массива. Этот указатель отличается от обычных указателей тем, что его нельзя изменить (например, установить на другую переменную), поскольку он сам хранится не в переменной, а является просто некоторым постоянным адресом.

Следствием такой интерпретации имен массивов является то, что для того чтобы установить указатель p на начало массива, надо написать:

int *p, mas[10];

p = mas;

p = &mas[0];

но не

p = &mas; // операция & перед одиноким именем массива не нужна

// и недопустима (нельзя получить адрес константы)!

Вывод: когда в программе объявляется массив int mas[10], то этим определяется не только выделение памяти для десяти элементов массива, но еще и определяется указатель-константа с именем mas, значение которого равно адресу первого по счету (нулевого) элемента массива (или адресу начала самого массива).

int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

Если указатель-константа a указывает на нулевой элемент массива, то по определению a+1 указывает на следующий (первый элемент) элемент, a+i указывает на i-й элемент массива. Значит, если a+1 указывает на a[1], то *(a+1) есть содержимое a[1], если a+i – адрес a[i], то *(a+i) есть содержимое a[i]. Эти замечания справедливы независимо от типа элементов в массиве a.

int *pa, i = 5, x;

pa = a; // или так можно написать: pa = &a[0];

x = *a; // x = 0, т.е. x = a[0]

x = *(a+i); // x = 5, т.е. x = a[i] i=5

*(a+9) = 99; // a[9] = 99

x = *pa; // x = 0, т.е. x = a[0]

x = *(pa+3); // x = 3, т.е. x = a[3]

*(pa+7) = *(a+2); // a[7] = a[2];

Обратить внимание! Выражение a[i] в языке C всегда преобразуется к виду *(a+i): a[i]=*(a+i). Т.е. доступ к элементу одномерного массива в языке С физически реализуется через адрес начала массива в памяти и смещения элемента (индекс элемента) от начала массива. Вот поэтому имя массива и должно быть адресом начала массива.

Если указатель p является указателем на элемент массива, то в выражениях его также можно использовать с индексом: p[i] идентично *(p+i).

int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

int *p = a+2; // устанавливаем p на a[2]

int x = p[3]; // x = 5 или x = a[5]

x = *(p+7); // x = 9 или x = a[9]

Вывод: Любое выражение, включающее массивы и индексы, может быть записано через указатели и смещения и наоборот, причем даже в одном и том же утверждении. А элемент массива можно изображать как в виде указателя со смещением, так и в виде имени массива с индексом.

Задача. Найти сумму элементов массива. Для доступа к элементам массива ис-пользовать указатели.

void main() {

int a[100], n, i;

int s = 0;

do {

printf("Введите количество элементов массива n = ");

scanf("%d", &n);

}

while (n < 0 || n > 100);

printf(“Введите элементы массива\n”);

for (i = 0; i < n; i++)

scanf("%d", a + i);

printf(“Вы ввели массив\n”);

for (i = 0; i < n; i++)

printf("%d ", *(a+i));

printf(“\n”);

for (i = 0; i < n; i++)

s += *(a+i);

printf(“Сумма элементов массива = %d\n”, s);

}

==== второй вариант =====================================

void main() {

int *pa;

...

printf(“Введите элементы массива\n”);

for (pa = a, i = 0; i < n; i++, pa++)

scanf("%d", pa);

printf(“Вы ввели массив\n”);

for (pa = a, i = 0; i < n; i++, pa++)

printf("%d ", *pa);

printf(“\n”);

for (pa = a, i = 0; i < n; i++, pa++)

s += *pa;

printf(“Сумма элементов массива = %d\n”, s);

}

==== третий вариант =====================================

void main() {

int *pa, *pa_n;

...

printf(“Введите элементы массива\n”);

for (pa = a, pa_n = a + n; pa < pa_n; pa++)

scanf("%d", pa);

printf(“Вы ввели массив\n”);

for (pa = a, pa_n = a + n; pa < pa_n; pa++)

printf("%d ", *pa);

printf(“\n”);

for (pa = a, pa_n = a + n; pa < pa_n; pa++)

s += *pa;

printf(“Сумма элементов массива = %d\n”, s);

}

==== четвертый вариант =====================================

void main() {

int *pa;

...

pa = a;

printf(“Введите элементы массива\n”);

for (i = 0; i < n; i++)

scanf("%d", &pa[i]);

printf(“Вы ввели массив\n”);

for (i = 0; i < n; i++)

printf("%d ", pa[i]);

printf(“\n”);

for (i = 0; i < n; i++)

s += pa[i];

printf(“Сумма элементов массива = %d\n”, s);

}

Задача. Что выведется в результате работы программы?

void main () {

char arr[] = {'Ш', 'К', 'О', 'Л', 'А'};

char *pt;

int i;

pt = arr + sizeof(arr) - 1;

for(i = 0; i < 5; i++, pt--)

printf("%c %c\n", arr[i], *pt);

}

Может сложиться мнение, что массив и указатель полностью эквивалентны. Однако имеется два существенных отличия массива от указателя:

  • массиву при описании выделяется память для хранения всех его элементов, а указателю только для хранения адреса;

  • адрес массива навсегда закреплен за именем, то есть имя массива является адресной константой. Указатель является переменной, и операции p=a и p++ имеют смысл; имя массива является константой, и конструкции типа a=p или a++, или p=&a будут ошибочными.