Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Вот результаты пробного запуска:
Введите положительное целое число (0 для завершения): 5 итерация цикла: 1 subtotal: 15; total: 15
Введите положительное целое число (0 для завершения): 10 итерация цикла: 2 subtotal: 55; total: 70
Введите положительное целое число (0 для завершения) : 2 итерация цикла: 3 subtotal: 3; total: 73
Введите положительное целое число (0 для завершения): 0 Цикл выполнен 3 раз(а)
Классы хранения, связывание и управление памятью 501
Классы хранения и функции
Функции также имеют классы хранения. Функция может быть либо внешней (по умолчанию), либо статической. (В стандарте С99 добавлена третья возможность — встраиваемая функция, которая обсуждается в главе 16.) Доступ к внешней функции могуг получать функции в других файлах, но статическая функция может применяться только внутри файла, где она определена. Рассмотрим, например, файл со следующими прототипами функций:
double gamma(double); /* по умолчанию внешняя */
static double beta(int, int);
extern double delta(double, int);
Функции gamma() и delta() могут использоваться функциями в других файлах, которые являются частью программы, но beta() — нет. Из-за такого ограничения функции beta() одним файлом в остальных файлах можно применять другие функции с этим же именем. Причина использования класса хранения static связана с созданием функций, закрытых в отношении конкретного модуля, благодаря чему устраняется возможность конфликта имен.
Обычная практика предусматривает применение ключевого слова extern при объявлении функции, определенной в другом файле. Главным образом это касается ясности, т.к. объявление функции предполагается как extern, если только не указано ключевое слово static.
выбор класса хранения
Ответом на вопрос о том, какой выбрать класс хранения, чаще всего будет — автоматический. В конце концов, по какой еще причине автоматический класс хранения был выбран по умолчанию? Да, мы знаем, что на первый взгляд внешний класс хранения выглядит более привлекательным. Стоит лишь сделать все свои переменные внешними, и не придется беспокоиться об использовании аргументов и указателей при взаимодействии между функциями.
Однако здесь подстерегает коварная ловушка. Вам придется переживать о том, что функция А() незаметно изменит значения переменных, применяемых в функции В(), хотя ваши намерения были совершенно другими. Несомненное свидетельство несметного количества лет, на протяжении которых формировался коллективный опыт программистов, говорит о том, что одна эта скрытая опасность далеко превосходит сомнительную привлекательность неразборчивого использования переменных с внешним классом хранения.
Распространенным исключением из правила являются данные const. Поскольку они не могут быть изменены, нет нужды переживать по поводу их непреднамеренной модификации:
const int DAYS = 7;
const char * MSGS[3] = {"Да", "Нет", "Возможно"};
Одним из золотых правил защитного программирования является принцип “необходимого знания”. Держите всю внутреннюю работу каждой функции максимально закрытой в рамках этой функции, совместно используя только те переменные, которые должны совместно использоваться. Друтие классы хранения удобны, и они доступны. Однако прежде чем выбирать какой-то из них, подумайте, есть ли в этом необходимость.
502 Глава 12
Функция генерации случайных чисел и статическая переменная
Теперь, когда вы получили необходимый минимум знаний о классах хранения, давайте рассмотрим пару программ, в которых они применяются. Первым делом мы взглянем на функцию, которая использует статическую переменную с внутренним связыванием: функцию генерации случайных чисел. Для генерации случайных чисел библиотека ANSI С предлагает функцию rand(). Существуют разнообразные алгоритмы генерации случайных чисел, и ANSI С позволяет реализациям выбирать наилучший алгоритм для конкретной машины. Однако ANSI С также предлагает стандартный переносимый алгоритм, который выдает те же самые случайные числа в разных системах. В действительности функция rand() является “генератором псевдослучайных чисел”, т.е. фактическая последовательность чисел предсказуема, но числа достаточно равномерно распределены по диапазону возможных значений.
Вместо применения встроенной функции rand() компилятора, мы будем использовать переносимую версию ANSI, чтобы вы могли видеть, что происходят внутри. Схема начинается с числа, которое называется “начальным”. Функция применяет начальное число для получения нового числа, которое становится новым начальным числом. Затем новое начальное число может использоваться для получения следующего нового начального числа и т.д. Чтобы эта схема работала, функция генерации случайных чисел должна запоминать начальное число, которое применялось при ее последнем вызове. Здесь и возникает потребность в статической переменной. В листинге 12.7 представлена версия 0 (вскоре появится и версия 1).
В листинге 12.7 статическая переменная next начинает со значения 1 и изменяет ся магической формулой при каждом вызове функции. Результатом будет возвращаемое значение, находящееся где-то в диапазоне от 0 до 32767. Обратите внимание, что next является статической переменной с внугренним связыванием, а не просто статической переменной без связывания. Дело в том, что позже пример будет расширен, чтобы переменная next совместно использовалась двумя функциями в том же самом файле.
Давайте протестируем функцию rand0() с помощью простого драйвера, показанного в листинге 12.8.
Листинг 12.8. Драйвер r_drive0. с
/* r_drive0.c -- тестирование функции rand0() */
/* компилировать вместе с rand0.c */
#include extern int rand0(void);
Классы хранения, связывание и управление памятью 503
Здесь вы имеете еще один шанс попрактиковаться с применением нескольких файлов. Поместите код из листинга 12.7 в один файл, а код из листинга 12.8 — в другой. Ключевое слово extern напоминает, что функция rand0() определена в отдельном файле, хотя оно необязательно.
Вывод имеет следующий вид:
16838
5758
10113
17515
31051
Выходные данные производят впечатление случайных, но давайте запустим программу снова. И вот какие результаты получены на этот раз:
16838
5758
10113
17515
31051
Числа выглядят знакомыми; в этом и заключается аспект “псевдо”. Каждый раз, когда главная программа запускается, старт происходит с одного и того же начального числа 1. Проблему можно обойти путем ввода второй функции по имени srandl(), которая позволит переустанавливать начальное число. Трюк заключается в том, чтобы сделать next статической переменной с внутренним связыванием, которая известна только функциям randl() и srandl(). (Эквивалент srandl() в библиотеке С называется srand().) Добавьте функцию srandl() в файл, содержащий randl().
Читать дальшеИнтервал:
Закладка: