Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на C / C++ / Нейбауэр А. Моя первая программа на С.doc
Скачиваний:
182
Добавлен:
02.05.2014
Размер:
3.75 Mб
Скачать

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

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

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

В программе, приведенной в Листинге 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)

{

*first = index+1;

flag = 1;

}

}

index++;

}

}

После ввода строки и символа мы передаем функции 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(), где осуществляется вывод информации, включающий вывод строки, символа, количества появлений символа в строке и первого случая появления символа в строке.

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

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

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

______________________________

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

<>

Вопросы

  1. Где может быть использован структурный тип данных?

  2. Как определить структуру?

  3. В чем заключается различие между типом записи структуры и структурной переменной?

  4. Как обратиться к элементу структуры?

  5. Может ли структура содержать элементы одного типа?

  6. Что такое указатель?

  7. Если в программе присутствует определение типа float *num, в чем будут выражаться различия между num и *num?

  8. Для чего в языке Си используют указатели?

  9. Каким образом указатели передаются функции?

<>

Упражнения

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

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

  3. Напишите программу, в которой две переменные типа float определяются в main() как локальные, а затем используются в функции, вычисляющей квадраты обоих чисел.

  4. Объясните, почему следующая программа написана неверно:

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);

}