|
Программирование >> Унарные и бинарные операторы
Указатель на массив 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:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |