Программирование >>  Унарные и бинарные операторы 

1 ... 9 10 11 [ 12 ] 13 14 15 ... 37


Указатель на массив 99

Еще любопытнеГг можно записать равенство char ch=*(argv[l]+l) из того же листинга. В данном случае нам нужно получить не нулевой, а первый символ параметра, на который указывает argv[l]. Начнем, как обычно, с argv. Прибавление единицы к argv даст указатель на первый элемент массива argv[ ], то есть па argv[ 1]. Чтобы получить argv[ 1], применим к argv+1 оператор*: *(argv+l). Но argv[l] - тоже указатель, и чтобы получить первый элемент параметра, нужно прибавить к *(argv+l) единицу и снова применить оператор *:

ch = *(*(argv+l)+l):

Из раздела Массивы нам известно, что *(argv+l) можно записать как argv[l]. Поэтому предыдущее равенство можно представить и так:

ch = argv[l][l]:

В такой записи нет ничего удивительного, ведь argv[l] указывает на нулевой элемент массива. Следовательно, ссшиэлементы получаются оператором [], и argv[l][l] - пе что иное, как первый символ первого параметра командной строки.

Наконец, верна и совсем уж дикая запись, поскольку операторы ++ и *, имея одинаковый приоритет, выполняются справа налево:

ch=*++*++argv:

Значит, сначала argv увеличивается па единицу и указывает на argv[l]. Первая звездочка переводит стрелки , преврап1ая указатель на argv[l] в само значение argv[l], и мы продолжаем путешествие пе по массиву argv[], а по массиву argv[l][]. Второй оператор ++ передвигает у казатель к первому элеме! гту массива а rgv[ 1 ] [ ], а звездочка добывает сам этот элемент.

Задача 5.2. После присваивания ch=*++*++argv указатель argv меняется. На сколько позиций назад нужно вернуть указатель argv, чтобы он по-прежнему указы вал на argv[0]?

Указатель на массив

Среди многочисленных способов обращения к символам, из которых состоят параметры ко.мандпой строки (см. предыдуп1ий раздел), нам встретился и такой: argv[l][l]. В квадратных скобках, конечно же, могут стоять и целочисленные переменные: argv[i][j]. Эта последняя запись наво/щт на мысль, что из указателя на указатель может вырасти двухмерная структура данных, в которой есть строки и столбцы. Такие структуры идеально подходят для хранения изображений или матриц, правда, для этого они должны быть прямоугольными, то есть содержать строки одного и того же размера. Один из способов создания такой структуры демонстрирует програ.мма. [гоказапная в листинге 5.8. Листинг 5.8

#inc1ude <iostream>

using namespace std;

fdefine NR 20

#define NC 40

main(){

int **pic;

pic=new int *[NR];

for(int i=0: i<NR:i++) pic[i]=new int[NC];

for(int i=0;i<NR;i++) forCint j=0;j<NC;j++) pic[i][j]=i*j:

cout pic[5][6]; 30

return 0:

Следующая инструкция создает массив указателей на int:

pic = new int *[NR]:

В переменной pic хранится адрес начала .этого массива, то есть указатель на указатель, что и показано при объявлении pic: int**pic. Массив указателей содер-



жит NR переменных и служит заготовкой для NR строк нашей прямоугольной матрицы.

В каждой строке будет NC переменных int, память для которых выделяется в цикле:

fordnt i=0: i<NR;i++) pic[i]=new int[NC]:

Оператор new вьщеляет память для NC идущих подряд переменных и записывает адрес начала этой памяти в pic[i ]. В результате получается прямоугольная структура данных, состоящая как бы из NR строк и NC столбцов.

Отдельный элемент, принадлежащий i-й строке и j-.My столбцу, записывается как pic[i ][j]. Программа, показанная в листинге 5.8, использует двойной цикл, чтобы записать в каждый элемент матрицы число, равное произведению номера столбца на номер строки. Далее ради проверки на экран выводится значение pic[5][6], равное, как и ожидалось, 30.

Кроме матриц, растущих из указателей на указатель, в С++ есть другая, очень похожая структура: двухмерный массив. Объявление int spic[20][40] создает массив, содержащий 20 х 40 = 800 элементов типа int. Каждый элемент принадлежит определенным строке и столбцу, например, spic[0][15] - число, стоящее в нулевой строке и пятнадцатом столбце.

Поскольку доступ к элементу матрицы, получаемой с помощью oi lepaTopoB new (см. листинг 5.8) и элементу двухмерного массива одинаков - в том и другом случаях элемент, стоящий в пятой строке и третьем столбце, записывается как <имя>[5][3], - можно подумать, что оба способа идентичны. Но это совсем не так. Попытаемся присвоить указателю на указатель имя двухмерного массива:

Значения NR и NC задаются директива.ми #define. Строка #dcfine NR 20 означает, что еще до компиляции в тексте программы имя NR всюду будет заменено значением 20. Это очень удобно, так как нудная работа (замена числа строк и столбцов) поручается компьютеру.

int **р1с:

int spic[20][40]:

pic-spic: Нельзя!

Эта попытка вызовет сообщение об ошибке: assignment to int ** from int (*)[40]

Oho говорит о том, что имя относится совсем к другому типу int (*)[40].

Чтобы лучше понять, что это за тин, нужно познакомиться с тем, как двухмерный массив храшст данные. Оказывается, объекгы двухмерного массива занимают сплошную область памяти компьютера, а поскольку память одномерна, массив хранится в пей построчно. Сначала идет нулевая строка из сорока элементов типа - int, затем первая - и так до самой последней, девятнадцатой. Если spic - имя двухмерного массива, то *(spic+l), очевидно, указывает на начало первой строки, ведь пулевой элемент первой строки можно записать так:

**{spic+l)-*(*spic+l)+0)=spic[l][0]

Так что же такое spic? Логично предположить, что это указатель на одномерный массив (в нашем случае - из сорока элементов типа int). Если это так, то spic+1 должен указывать на первую строку двухмерного массива, sp1c+2 - на вторую. Правда, нужно сказать, что указатель на массив - довольно странный объект. Мы ведь привыкли, что если р - указатель на тип char, то *р - само значение типа char. С указателем на массив это не так. Если spic - указатель на массив, то *spic не может быть самим массивом, так как непосредственно массивами С++ не оперирует. Поэтому *spic - это указатель на нулевой элемент массива. Еще один оператор * - и мы получим само значение.

Важно понимать разницу между указателем на указатель и указателем на массив. В первом случае прибавление единицы передвигает указатель вдоль массива указателей, во втором - перебрасывает указатель на одну строку вперед. Различия между указателем на ука-



затель н указателем па массив, быть может, лучп1е всего вндпы на рис. 5.3, где показано размеп1ение в памяти матриц, растущих из того и другого.

агг

№1 [01111 ИИ 101131

arrlOl

arrlll

arrI21

ТТ~]

I I I I I

11I1D1 РВИ [IPI РМЧ

PIIOI 121[11 ИИ I21I3I

(*)[1->-Г ° I I I ИР I Д ! I 11 И1Р11 [4131 I РЮ! I [2Ц111 рр] I PW I

Рис. 5.3. Двухмерный массив и динамически созданная матрица

Матрица, берущая начало из указателя на указатель, разб1Х)сана по ко.мпьютерпой памяти, потому что оператор new вызывается отдельно для каждой строки. Двухмерный же массив занимает память целым куском - строка за строкой.

Теперь мы нопнмаем, что в сообщении компилятора об ошибке как раз и содержалось правильное объявление указателя па массив из сорока значений типа 1nt:

int {*)[40]

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

Зная, что представляет собой двухмерный массив, легко понять, каким должен быть прототип принимаюпю!! его функции. Как и в случае одномерных массивов, (])yiiK-ции нельзя передавать сам массив. Достаточно псчедать указатель на массив, 1>авный по размеру строке двухмерно-

го массива (или, что то же самое, числу столбцов в нем). Программа, показанная в листинге 5.9, использует функцию min ддя вычисления минимальногоэлемешамассива. Листинг 5.9

#include <iostream> using namespace std: #define NR 20 #define NC 40

int minCint nr.int {*m)[NC]){ int min-m[0][0]: for(int i=0:i<nr;i++)

for(int j=0:jNC:j-+)

min=(m[i][j] < min)? m[i][j] ; min; return min:

int mainC){ int spic[NR][NC]: for(int i=0:i<NR:i++) forCint j=0:j<NC;j++) spic[i][j]=i*j; cout minCNR.spic) endl; return 0: }

Функция min имеет два параметра: число строк nr и указатель на массив (строку) из NC элементов intC*ffl)[NC]. В самой функции минимум сначала полагается равным элементу [0][0] массива, а дальше в цикле, перебирающем все его элементы, проверяется, так ли это. Для проверки испо;н>зуется необычный оператор:

min=Cm[i][j] < min) ? m[i][j] : min;

Получаемый с его помо1цью результат зависит от условного выражения в круглых скобках. Если условие m[i][j] <min истинно, переменная min становится равной выражению, стоящему слева от двоеточия, то есть [ilCj], если же условие в скобках ложно, значение min не меняется, потому что выполняется присваивание niin=min. Следующая инструкция выводит на эщуан значение минимума, равное в нашем случае пулю:

cout minCNR.spic) endl:



1 ... 9 10 11 [ 12 ] 13 14 15 ... 37

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика