Указатели на многомерные массивы




Лабораторная работа № 12

Обработка одномерных массивов с использованием указателей

 

 

Цель работы

1.1 Получение навыков в написании программ с использованием указателей.

1.2 Изучение механизма передачи массива в функцию и передачи параметров по ссылке.

Техническое обеспечение

2.1 Персональная ЭВМ IBM PC/286 и более поздних моделей.

2.2 Клавиатура.

2.3 Дисплей.

2.4 Печатающее устройство.

Программное обеспечение

3.1 Операционная система MS DOS версия 5.0 и более поздние версии.

3.2 Система программирования Borland C++ версия 3.1 и более поздние версии.

Постановка задачи

Дано несколько массивов чисел. Длины массивов заданы в вари­анте. Требуется в каждом массиве найти наибольший и наименьший элементы и напечатать их, затем все компоненты каждого массива возвести в квадрат и снова найти наибольший и наименьший элемен­ты.

Содержание отчета

5.1. Тема и цель работы.

5.2. Схема алгоритма решения задачи.

5.3. Текст программы.

5.4. Результаты выполнения программы.

 

Общие сведения

Указатель содержит адрес памяти, которая распределена для другой переменной. Если одна переменная содержит адрес другой, то говорят, что первая переменная указывает на вторую.

Если переменная должна содержать указатель, она объявляется следующим образом. Объявление указателя включает базовый тип, * и имя переменной. Стандартный вид объявления указателя следующий:

тип * имя;

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

Базовый тип указателя определяет тип переменной, на которую ссылается указатель. С технической точки зрения любой тип указателя может указывать в любой участок памяти, но С предполагает, что указатель указывает на объект своего базового типа.

 

Операторы для работы с указателями

Для работы с указателями используются два оператора - * и &.

Оператор & - это унарный оператор, возвращающий адрес операнда. Например:

p = #

помещает адрес переменной num в p. Данный оператор ничего не делает со значением переменной num. Операцию & можно рассматривать как «взятие адреса».

Оператор * дополняет оператор &. Оператор * - это унарный оператор, возвращающий значение переменной, находящейся по указанному адресу. Например, если p содержит адрес памяти переменной num, то

q = *p;

присвоит значение переменной num переменной q. В данном случае вышеприведенный оператор может быть прочитан как «q получает значение по адресу р ».

Пример.

#include <stdio.h>

int main (void)

{

int num, q;

int *p;

num = 100; /* num присваивается значение 100 */

p = &num; /* p получает адрес num */

q = p; /* q посредством р присваивается значение num */

printf(“%d”, q); /* выводит 100 */

return 0;

}

Данная программа выводит значение 100 на экран.

 

Выражения с указателями

В целом выражения, использующие указатели, подчиняются тем же правилам, что и обычные выражения языка С. Рассмотрим их некоторые особенности.

Присваивание указателей

Как и обычные переменные, указатели могут использоваться с правой стороны оператора присваивания для присвоения значения другому указателю. Например:

#include <stdio.h>

int main(void)

{

int x;

int *p1, *p2;

p1 = &x;

p2 = p1;

printf(“%p %p”, p1, p2); /* выводит адреса, хранящиеся в p1 и р2.

Они будут одинаковы */

return 0;

}

Арифметические действия с указателями

К указателям могут применяться только две арифметические операции: сложение и вычитание. Предположим, что p1 – это указатель на целое длиной 2 байта. После выполнения выражения

p1++;

значение указателя увеличится на 2, т. е. произойдет сдвиг указателя на 2 байта и он будет указывать на следующий элемент базового типа. Это справедливо и для уменьшения. Т. е. выражение

p1- -;

приведет к уменьшению значения указателя на 2 и сдвигу его к предыдущему элементу.

Можно вычитать из указателей любые целые числа. Выражение:

p1 = p1 + 7;

приводит к тому, что указатель указывает на седьмой элемент по сравнению с элементом, на который он указывал до присваивания.

Операция

р2 – р1

дает число элементов между указателями р 1 и р 2.

Сравнение указателей

В общем случае сравнение указателей с помощью одной из операций отношения имеет смысл в том случае, если оба указателя указывают на общий для них объект, например, массив или строку. Например:

#include <stdio.h>

int main(void)

{

int a, b;

int p1, *p2;

p1 = &a;

p2 = &b;

/* указатели р1 и р2 не равны, так как они указывают на разные объекты */

if (p1!= p2) printf(“Указатели р1 и р2 не равны\n”);

else printf(“Эта строка не напечатается”);

return 0;

}

 

Указатели и массивы

Между указателями и массивами существует прямая связь. Когда объявляется массив, например

int arr[25];

то этим определяется несколько больше, чем только выделение памяти для 25 элементов типа int. А именно, идентификатор массива arr определяется как указатель на первый (нулевой) элемент массива (адрес первого элемента массива &arr [0]). Так как идентификатор массива определяется как адрес, то следующее присваивание является допустимым:

int arr[25];

int *ptr;

ptr = arr;

Указатель ptr устанавливается на адрес первого элемента массива arr. Указатель ptr становится указателем на массив и его можно использовать для ссылки на элементы массива. Это присваивание можно записать в эквивалентной форме:

ptr = &arr[0];

Для доступа к элементам массива тоже существует два способа.

Первый способ доступа к элементам массива связан с использованием обычных индексных выражений вида arr [ i ]. Например, индексное выражение arr[5] ссылается на шестой элемент массива arr.

Второй способ доступа к элементам массива связан с использованием адресного выражения (или выражения с указателем на массив)

*(ptr+1).

Например, адресное выражение *(ptr +5) ссылается на шестой элемент массива arr.

Рассмотрим как вычисляется индексное выражение arr [5]. Идентификатор arr является указателем начала массива. В соответствии с правилами выполнения операции сложения указателя с целой величиной целая величина преобразуется к адресному представлению путем умножения ее на размер типа, адресуемого указателем. Целая константа 5 умножается на размер типа int. Преобразованное значение числа 5 складывается со значением указателя arr, что дает адрес, относящийся к шестой позиции типа int после arr. К полученному адресу применяется операция «разадресация». Результатом является значение шестого элемента массива. В терминах языка С это выглядит так:

arr[5] = (arr+5) = (arr + 5*sizeof(int)).

Следовательно, следующие выражения ссылаются на шестой элемент массива arr:

arr[5];

5[arr];

*(arr + 5);

*(5 + arr).

Как следует из определения индексного выражения языка С, любой указатель может быть задан с индексом. Указатель на массив тоже может быть проиндексирован. Выражения

Ptr[5]

5[ptr]

ссылаются на тот же шестой элемент массива arr. Если расписать индексное выражение ptr[5], то получается адресное выражение

*(ptr + 5).

В действительности компилятор языка С преобразует все индексные выражения в адресные выражения. Например:

arr[i] в *(arr + i)

ptr[i] в *(ptr + i).

Таким образом, для доступа к i -му элементу массива arr можно пользоваться любым из данных выражений

arr[i] i[arr] *(arr + i)

*(i + arr) *(ptr + i) *(i + ptr)

ptr[i] i[ptr]

или другими словами, любое индексное выражение можно представить в виде выражения с указателем и смещением и наоборот. Причем обе формы доступа можно сочетать в одном операторе.

Пример.

/* Функция strcpy – копирование строк */

/* Вариант с использованием массивов */

char *strcpy (char s1[ ], char s2[ ])

{

int I;

for (i = 0; s1[i] = s2[i]; i++);

return (s1);

}

 

/* Вариант с использованием указателей */

char *strcpy (char *s1, char *s2)

{

char *str = s1;

while (*s1++ = *s2++);

return (str);

}

 

 

Указатели на многомерные массивы

Многомерный массив в языке С – это массив массивов, т. е. Массив, элементами которого являются массивы. Например, трехмерный массив есть массив, элементы которого двухмерные массивы

int arr[L][M][N];

Массив arr состоит из L элементов. Каждый элемент – двухмерный массив размерностью M ´ N. В памяти массив arr расположен следующим образом:

 

  ... Первый массив M ´ N Второй массив M ´ N   ... L - й массив M ´ N   ...

Младший адрес Старший адрес

Каждый массив M ´ N в памяти располагается по строкам. Например, i -й массив M ´ N трехмерного массива arr представляется следующим образом:

  ... Первый массив из N элем-тов Второй массив из N элем-тов   ... M - й массив из N элем-тов   ...

 

Рассмотрим, как осуществлять доступ к элементам многомерного массива через указатель. Сделаем это на примере массива arr.

Объявим указатель и свяжем его с массивом arr

int *ptr;

ptr = &arr[0][0][0];

Необходимо получить доступ к элементу arr [ i ][ j ][ k ], или к k -му элементу j -й строки i -го массива M ´ N массива arr. Последовательно это вычисляется так:

адрес первого массива M ´ N в массиве arr ptr

адрес i -го массива M ´ N в массиве arr ptr + i*(M*N)

адрес j -й строки i -го массива M ´ N ptr + i*(M*N) + j*N

в массиве arr

адрес k -го элемента j -й строки i -го массива ptr + i*(M*N) + j*N + k

M ´ N в массиве arr

k -й элемент j -й строки i -го массива (ptr + i*(M*N) + j*N + k)

M ´ N в массиве arr

 

Пример.

/* Функция поиска минимального элемента в трехмерном массиве. В функцию

передается адрес начала массива и размеры массива. Возвращается указатель

на структуру, содержащую индекс минимального элемента */

struct INDEX { /* определение структуры INDEX */

int i, j, k;

} index;

struct INDEX *find (int *ptr, int L, int M, int N0

{

int min;

int i, j, k, ind;

min = *ptr;

index.i = index.j = index.k = 0;

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

for (j = 0; j < M; j++)

for (k =0; k < N; k++) {

ind = i*(M*N) + j*N + k;

if (min > *(ptr + ind)) {

min = *(ptr + ind);

index.i = i;

index.j = j;

index.k = k;

}

}

return &index;

}

 

Создание передачи по ссылке

Хотя, как правило, передача параметров в функцию в С происходит по значению, можно передать параметры по ссылке. Поскольку в данном случае происходит передача адреса аргумента, возможно изменение значения аргумента, находящегося вне функции.

Указатели передаются в функцию как обычные значения. Конечно, необходимо объявлять параметры типа указатель. Ниже приведена функция swap (), обменивающая значение двух целочисленных аргументов:

void swap (int *x, int *y)

{

int temp;

temp = *x /* сохранение значения по адресу х */

*x = *y; /* помещение у в х */

*y = temp; / * помещение х в у */

}

Оператор * используется для доступа к переменным, на которые указывают операнды. Следовательно, содержимое переменных, используемых при вызове функции, обменивается.

Важно помнить, что функция swap () (или любая другая функция, использующая указатели на параметры) должна вызываться с адресами аргументов. Следующая программа демонстрирует правильное использование функции swap ():

#include <stdio.h>

void swap (int *x, int *y);

int main(void)

{

int x, y;

x = 10;

y = 20;

swap(&x, &y);

printf(“%d %d”, x, y);

return 0;

}

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-04-27 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: