Опубликован: 05.07.2006 | Уровень: для всех | Доступ: платный
Лекция 5:

Функции и структура программ

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

Ярослав Воробей
Ярослав Воробей
Россия
Дмитрий Левин
Дмитрий Левин
Россия