Листинг 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) { *first = index+1; flag = 1; } } index++; } }

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

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

Листинг 5. Программа, в которой используется указатель для возврата значений. - student2.ru
Рис. 12. Передача адресов функции

Листинг 5. Программа, в которой используется указатель для возврата значений. - student2.ru
Рис. 13. Фигурные скобки отмечают блоки инструкций

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

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

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

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

*count = *count + 1;

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

*count++;

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

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

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

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

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

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

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

  Вопросы
1. Где может быть использован структурный тип данных? 2. Как определить структуру? 3. В чем заключается различие между типом записи структуры и структурной переменной? 4. Как обратиться к элементу структуры? 5. Может ли структура содержать элементы одного типа? 6. Что такое указатель? 7. Если в программе присутствует определение типа float *num, в чем будут выражаться различия между num и *num? 8. Для чего в языке Си используют указатели? 9. Каким образом указатели передаются функции?
  Упражнения
1. Напишите программу, в которой две переменные типа float определяются в main() как локальные, а затем используются в функции, вычисляющей квадраты обоих чисел. 2. Объясните, почему следующая программа написана неверно: main() { struct CD { char description[40]; char category[12]; char name[20]; float cost; int number; } disc; puts("Введите сведения о диске"); printf("Введите название: "); gets(name); printf("Введите описание: "); gets(description); printf("Введите категорию: "); gets(category); printf("Введите цену: "); scanf("%f", &cost); printf("Введите номер ячейки: "); scanf("%d", &number); puts("Введена следующая информация о диске: "); printf("Название: %s\n", name); printf("Описание: %s\n", description); printf("Категория: %s\n", category); printf("Цена: %6.2f\n", cost); printf("Номер п/п: %d\n", number); }

Наши рекомендации