|
Функции и структура программ
4.8. Блочная структура
Язык "C" не является языком с блочной структурой в смысле PL/1 или алгола; в нем нельзя описывать одни функции внутри других.
переменные же, с другой стороны, могут определяться по методу блочного структурирования. описания переменных (включая инициализацию) могут следовать за левой фигурной скобкой,открывающей любой оператор, а не только за той, с которой начинается тело функции. переменные, описанные таким образом, вытесняют любые переменные из внешних блоков, имеющие такие же имена, и остаются определенными до соответствующей правой фигурной скобки. Например в
if (n > 0) { int i; /* declare a new i */ for (i = 0; i < n; i++) ... }
Областью действия переменной i является "истинная" ветвь if ; это i никак не связано ни с какими другими i в программе.
Блочная структура влияет и на область действия внешних переменных. Если даны описания
int x; f() { double x; ... }
То появление x внутри функции f относится к внутренней переменной типа double, а вне f - к внешней целой переменной. Это же справедливо в отношении имен формальных параметров:
int x; f(x) double x; { ... }
Внутри функции f имя x относится к формальному параметру, а не к внешней переменной.
4.9. Инициализация
Мы до сих пор уже много раз упоминали инициализацию, но всегда мимоходом , среди других вопросов. Теперь, после того как мы обсудили различные классы памяти, мы в этом разделе просуммируем некоторые правила, относящиеся к инициализации.
Если явная инициализация отсутствует, то внешним и статическим переменным присваивается значение нуль; автоматические и регистровые переменные имеют в этом случае неопределенные значения (мусор).
Простые переменные (не массивы или структуры ) можно инициализировать при их описании, добавляя вслед за именем знак равенства и константное выражение:
int x = 1; char squote = '\''; long day = 60 * 24; /* minutes in a day */
Для внешних и статических переменных инициализация выполняется только один раз, на этапе компиляции. Автоматические и регистровые переменные инициализируются каждый раз при входе в функцию или блок. В случае автоматических и регистровых переменных инициализатор не обязан быть константой: на самом деле он может быть любым значимым выражением, которое может включать определеные ранее величины и даже обращения к функциям. Например, инициализация в программе бинарного поиска из "лекции №3" могла бы быть записана в виде
binary(x, v, n) int x, v[], n; { int low = 0; int high = n - 1; int mid; ... }
вместо
binary(x, v, n) int x, v[], n; { int low, high, mid; low = 0; high = n - 1; ... }
По своему результату, инициализации автоматических переменных являются сокращенной записью операторов присваивания. Какую форму предпочесть - в основном дело вкуса. мы обычно используем явные присваивания, потому что инициализация в описаниях менее заметна. Автоматические массивы не могут быть инициализированы. Внешние и статические массивы можно инициализировать, помещая вслед за описанием заключенный в фигурные скобки список начальных значений, разделенных запятыми. Например программа подсчета символов из "лекции №1" , которая начиналась с
main() /* count digits, white space, others */ ( int c, i, nwhite, nother; int ndigit[10]; nwhite = nother = 0; for (i = 0; i < 10; i++) ndigit[i] = 0; ... )
может быть переписана в виде
int nwhite = 0; int nother = 0; int ndigit[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; main() /* count digits, white space, others */ ( int c, i; ... )
Эти инициализации фактически не нужны, так как все присваиваемые значения равны нулю, но хороший стиль - сделать их явными. Если количество начальных значений меньше, чем указанный размер массива, то остальные элементы заполняются нулями. Перечисление слишком большого числа начальных значений является ошибкой. К сожалению, не предусмотрена возможность указания, что некоторое начальное значение повторяется, и нельзя инициализировать элемент в середине массив без перечисления всех предыдущих.
Для символьных массивов существует специальный способ инициализации; вместо фигурных скобок и запятых можно использовать строку:
char pattern[] = "the";
Это сокращение более длинной, но эквивалентной записи:
char pattern[] = { 't', 'h', 'e', '\0' };
Если размер массив любого типа опущен, то компилятор определяет его длину, подсчитывая число начальных значений. В этом конкретном случае размер равен четырем (три символа плюс конечное \0 ).