|
|
Функции и структура программ
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 ).
