Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебник Моя первая программа на С++.docx
Скачиваний:
3
Добавлен:
01.07.2025
Размер:
6 Мб
Скачать
☆

Значение переменной tax равно 35

Указатель переменной tax имеет значение 21260

Возможно, вы спросите: «Ну и что из этого? В конце концов, того же самого результата можно было до- биться, присвоив значение адреса переменной tax обычной целочисленной переменной, а не какому-то указателю». Преимущество использования указателя состоит в том, что к нему можно обращаться как к

*taxptr. Звездочка в данном случае будет сообщать компилятору, что мы интересуемся содержимым об- ласти памяти, адрес которой является значением указателя. То есть нас интересует не значение 21260, а содержимое памяти по этому адресу. В то время как значение переменной taxptr равно 21260, значение

*taxptr равно 35.

Значением, которое можно присвоить переменной taxptr, является только адрес. Попытка написать инст- рукцию

taxptr = 21260;

может привести к ошибке компиляции, так как здесь мы пытаемся присвоить целочисленное значение пе- ременной, определенной как указатель. Единственное значение, которое может быть присвоено перемен- ной taxptr, это адрес другой переменной, определенный с помощью оператора получения адреса:

taxptr = &tax;

Однако можно присвоить значение указателю *taxptr, написав инструкцию

*taxptr = 35;

Эта инструкция означает: «Поместить значение 35 в элемент памяти, на который указывает переменная

taxptr». Поскольку указатель содержит значение 21260,

Рис. 11.11. Присваивание значения указателю

число 35 будет размещено в памяти именно по адресу 21260, а так как эту область памяти занимает пе- ременная tax, она приобретет значение 35 (рис.11.11).

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

Указатели и функции

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

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

В программе, приведенной в Листинге 11.5, вводится значение строки и символа. Затем подсчитывается, сколько раз символ встречается в строке, и определяется позиция первого появления символа в строке.

Листинг 11.5. Программа, в которой используется указатель для возврата значений.

/*letcount.c*/ main()

{

char name[20], letter; int number, start; puts("Введите имя\n"); gets(name);

printf("Введите символ"); letter = getchar();

countlet(name, letter, &number, &start); printf("\nСимвол %c встречается в имени

%s %d раз\n", letter, name, number); printf("Первый раз символ встречается

в %d позиции", start);

}

countlet(ndplume, alpha, count, first) char ndplume[], alpha;

int *count, *first;

{

int index, flag;

*count = 0;

index = 0;

flag = 0;

*first = 0;

while (ndplume[index != '\0')

{

if (ndplume[index] == alpha)

{

*count = *count+1; if (flag == 0)

¶a++ae æepcue u+uru æɵuyye+a sneuwpo++ɵu usgawenacwæou "Books-shop".

Pacupocwpa+e+ue, upogaua, uepesauuca ga++on u+uru unu ee ¬acwen 3AMPE[EHª.

O æcex +apyue+uex upoca6a coo6yawa uo agpecy piracy@books-shop.com

}

index++;

}

}

{

*first = index+1; flag = 1;

}

После ввода строки и символа мы передаем функции countlet() четыре переменных: строку, символ и ад- реса переменных count и start, определенных с помощью оператора получения адреса.

Функция присваивает адреса получающим указателям: адрес переменной number хранится в *count, а адрес переменной start содержится в *first (рис.11.12).

Рис. 11.12. Передача адресов функции

Рис. 11.13. Фигурные скобки отмечают блоки инструкций

Переменные инициализируются путем присваивания нулевого значения, а затем начинает выполняться цикл while:

while (ndplum[index] != '\0')

Цикл проверяет каждый символ в строке и продолжает проверку до тех пор, пока не встретит нулевой символ. Как только выясняется, что очередной символ является нулевым, выполнение цикла прекращает- ся.

Положение фигурных скобок внутри цикла while является весьма существенным, так как одна инструкция if целиком вложена в другую (рис.11.13). Первое из условий if проверяет, является ли очередной встре- ченный символ тем, количество повторений которого мы подсчитываем. Если это так, значение счетчика, который в данном случае является указателем, увеличивается на единицу:

*count = *count + 1;

Это приводит к тому, что происходит увеличение на единицу значения, содержащегося в области памяти, адрес которой является значением указателя. Так как указатель содержит адрес переменной number, зна- чение number увеличивается на 1, хотя эта переменная является локальной, определенной внутри функ- ции main(). Обратите внимание, что в данном случае нельзя использовать синтаксис

*count++;

так как это приведет к изменению значения адреса, реально являющегося значением указателя, а не со- держимого соответствующего элемента памяти*.

Второе условие if проверяет значение переменной flag. Если оно равно нулю, значит указанный символ встречен в строке впервые. При этом указателю *first присваивается значение, соответствующее номеру

символа в строке плюс единица. Добавляя единицу к индексу, мы указываем положение символа в строке, начиная отсчет с 1 (такой способ является наиболее привычным для большинства людей). После этого значение переменной flag меняется на 1, так что номер позиции первого встреченного в строке символа уже не будет меняться при обнаружении следующих символов.

После выполнения условия if значение индекса увеличивается, в результате чего при следующем прохож- дении цикла будет проверяться следующий символ строки. Весьма существенно, чтобы инструкция index++; помещалась после закрывающей фигурной скобки внешней инструкции if, но перед двумя за- ключительными закрывающими скобками, одна из которых отмечает конец инструкции while, а вторая за- вершает функцию. Если указанная инструкция будет помещена в каком-нибудь другом месте, функция не сможет успешно работать.

После завершения выполнения функции countlet() управление передается назад в main(), где осуществ- ляется вывод информации, включающий вывод строки.

* В этом случае можно использовать инструкцию (*count)++;.(Прим.перев.)

символа, количества появлений символа в строке и первого случая появления символа в строке.

Обратите внимание на то, что для возврата значения функция не использует инструкцию return. Вместо этого значения переменных number и start присваиваются через указатели. Другими словами, в програм- ме возврат значений осуществляется путем использования указателей в качестве аргументов функции.

Можно написать ту же программу без использования указателей, определив переменные number и start как глобальные. Тогда и функция main(), и функция countlet() смогут обращаться к ним без передачи ар- гумента, однако использование указателей расширяет возможности контроля за работой программы. Пе- ременные остаются локальными, но к ним можно обращаться и менять их значения путем передачи их ад- ресов.

Использование указателей совершенно необходимо при работе с дисковыми файлами и выводе информа- ции на принтер, что и будет являться предметом нашего разговора в главе 12.