выбор класса хранения
Ответом на вопрос о том, какой выбрать класс хранения, чаще всего будет — автоматический. В конце концов, по какой еще причине автоматический класс хранения был выбран по умолчанию? Да, мы знаем, что на первый взгляд внешний класс хранения выглядит более привлекательным. Стоит лишь сделать все свои переменные внешними, и не придется беспокоиться об использовании аргументов и указателей при взаимодействии между функциями.
Однако здесь подстерегает коварная ловушка. Вам придется переживать о том, что функция А() незаметно изменит значения переменных, применяемых в функции В(), хотя ваши намерения были совершенно другими. Несомненное свидетельство несметного количества лет, на протяжении которых формировался коллективный опыт программистов, говорит о том, что одна эта скрытая опасность далеко превосходит сомнительную привлекательность неразборчивого использования переменных с внешним классом хранения.
Распространенным исключением из правила являются данные 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() определена в отдельном файле, хотя оно необязательно.
Читать дальше