Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции (Ведищев) + шпоры к экзамену / лекции по программированию за 1 курс.doc
Скачиваний:
174
Добавлен:
20.06.2014
Размер:
805.38 Кб
Скачать

Int а[10][7];массив из 10 элементов типа массив, из элементов типа int

Таким образом, матрица – одномерный массив одномерных массивов (вектор векторов)

char C [2][3][4]// трехмерный массив (вектор вектора векторов и символов)

Размещение в памяти.

Элементарный массив размещается в памяти в порядке возрастания индекса, для многомерных – построчно.

Имя массива представляет собой ссылку на начальный элемент массива.

Для обращения к элементу массива используется операция индексации, в качестве индекса целое число от 0 до n-1, где n – количество элементов по данному измерению.

A[2]=2

При использовании операции индексации происходит вычисление значения смещения в зависимости от объявленных размерностей и значения индексов.

Например,

int А[10][7];

а [3][5]; 5-ый элемент 3-ей строки

* (а+3∙7+5) пропускаем 3 строки

Задача . char C [2][3][4]

Посчитать значение смещения в массиве от начала, элемента C [1][2][3]

Всего 24 элемента. Смещение 23.

Трехмерные массивы представляются по слоям, каждый слой построчно. Для смещения : № строки*число элементов строки, индекс в строке добавляем для определения смещения.

4-мерный массив:

смещение: 1-ая размерность не используется. При передаче в качестве параметра массива 1 из его размерностей может отсутствовать.

Задача.

Дан вектор int х[10]. Найти максимальный элемент в векторе.

int х[10],i,max;

for (i=0;i<10;i++)

{

printf (“vvedite tseloe chislo”,x[i]);

scanf (“%d”,&x[i]);

}

max=x[0];

for (i=0;i<10;i++)

{

if (x[i]>max);

max=x[i];

}

Задача.

Дано 2 вектора A[n],B[n]. Вывести да, если они поэлементно равны, нет, если не равны.

float *A,*B;

int i,n;

printf (“vvedite tseloe chislo”,n]);

scanf (“%d”,&n]);

A=new float[n];

B=new float[n];

for (i=0;i<n;i++)

{

A[i]=random(32767);

B[i]=random(32767);

if (A[i]!= B[i])

{

printf (“no”);

break;

}

printf (“yes”);

}

Задача.

Массив М[I,где k измерений, а -количество элементов

М=&M[0][0]…[0]

Найти смещение для элемента по измерению к.

к=1,2,3,…к

м – адрес начала строки

двумерный массив: М[I][J], M[i][j] & M[i][j]=M+смещение=M+i*J+j

трехмерный массив: М[I][J][K], M[i][j][k]

& M[i][j][k]= M+i*J*K+j*K+k

off= смещение

Записать математическую формулу в виде алгоритма.

  1. Присваиваем начальное значение sum=0

k=1;k≤k

j=k+1;j≤k

Структура данных.

int j,k,K,*I,*I; //k=const

{

int j,k,i[k],I[K],sum=0,pr=1;

for (j=0;j<k;j++)

{

scanf (“%d”,&i[j]);

scanf (“%d”,&I[j]);

}

for (k=1;k<=K;k++)

{

for (j=k+1;j<=K;j++)

{

pr*=I[j];

}

sum+=pr;

}

printf(“%d”,sum);

getch();

}

Структура.

Тип данных, позволяющий объединить в одном объекте совокупность значений разного типа.

Операции:

  • Передача в функцию в качестве параметра

  • Возврат значения функции

  • Получения адреса

  • Присваивание в случае совпадения типов левого и правого операндов

struct [тег]

{

Список полей

Тип имя переменной

………….

}

Пример.

struct С

{

float Re;

float Im;

struct C*p;

}x;

При использовании объявления структуры с использованием тега допустимо в дальнейшем объявлять переменные следующим образом

struct С y;

Для доступа к полям используется знак “.”:

x.Re=0.1;

x.Im=0.2;

y=x;

Структуры обычно используются для хранения списков. Списки хранят помимо области данных указатель на саму структуру.

struct (*z);

Тогда доступ к полям z возможен 2 способами

(*z).Re=0.4;

z→Im=-5;

Тег – идентификатор, именующий структуру, которая объявляется списком объявления элементов. Описание специфицирует либо переменную типа struct, либо массив, либо указатель на структуру данного типа, либо объект, являющийся комбинацией вышеперечисленного.

struct С y,*f //объявление функции, возвращающей указатель на структуру с именем С.

Список объявлений – последовательность из одного и более объявлений, либо битовых полей.

Переменная, объявленная в списке объявлений, называется элементом структуры. Объявление переменной в этом списке не может содержать классов памяти инициализатора.

Элемент может иметь 1 из базовых типов или являться структурой, или объединением, или указателем.

Элементом структуры не может быть структура определяемого типа, но может быть указателем на структуру определяемого типа.

Битовые поля.

Используется как тип элементов структуры для осуществления прямого доступа к отдельным битам, а также для экономии памяти.

Синтаксис.

Спецификация типа идентификатор, символ :, константные выражения.

Битовое поле состоит из разрядов, количество которых задается константным выражением, которое должно иметь неотрицательное целое значение. Значение не должно превышать количества разрядов, необходимых для хранения значения специфицируемого типа.

Битовые поля нельзя использовать в массивах, функциях, указателях. Не приминима операция взятия адреса.

Пример.

Определить массив, хранящий информацию о содержимом экрана в текстовом видеорежиме с использованием 25 строк по 80 символов, каждый символ описывается 2 байтами. 1-ый хранит код символа, 2-ой его атрибут.

8 бит атрибутов включают в себя: 4 бита – цвет символа, 3 бита – цвет фона, 1 бит – признак мерцания.

struct screen

{

int sym 8;

int col 4;

int fcol 3;

int blink 1;

}P[25][80];

P[1][1].sym=’-’;

P[1][1].col=15;

P[1][1].fcol=4;

P[1][1].blink=1;

Объединение.

Предназначено для хранения данных разнородной информации в одной и той же области памяти. Например требуется организовать доступ к отдельным байтам числа типа float. С помощью объединения размещаем в 1 области памяти переменную float и массив из 4 символов.

Способ интерпретации содержимого памяти будем определять именем поля.

union

{

int s;

unsigned u;

}a;

a.s=-1 a.s=0

a.u=, n=16 a.u=||a.s||, n – количество разрядов для

a.u=65535 хранения целого числа

Объем памяти для хранения union определяется наибольшим по размеру из элементов. Например, union с полями int, float, double будет занимать sizeof(double)=8 байт. Int занимает младшие 2 байта.

Указатели.

Для выполнения операций с адресами. Указатель –объект для хранения адреса объекта определенного типа. Указатель на функцию содержит указатель на точку входа в функцию (адрес 1 исполняемого оператора тела функции)

Тип *описатель

Объявление указателя специфицирует тип объекта и имя переменной. Базовый, перечислимый, пустой типы, struct, union. Если спецификатор типа опущен, то подразумевается int. Если описатель представлен идентификатором, то он является указателем данного типа. Если описатель представлен более сложной конструкцией, то тип объекта на который ссылается описатель является

char **a адрес переменной типа символ в памяти

Может ссылаться на те же типы struct, union, массив, указатель.

Указатель на пустой тип предназначен для хранения на произв. тип void *p

Для выполнения операций над указателем на void, необходимо привести тип указателя к типу отличному от void.

Занимаемая память.

По умолчанию тип адресации ближний, адрес 2 байта, адресация внутри 64-ого сегмента far. Представлен адресом сегмента и смещением внутри сегмента. В переменной типа указатель хранится адрес, формат которого зависит от используемой вычислительной системы, а также от модели памяти установленной в опциях компилятора.

В компиляторе Borland C возможны 3 вида модификатора адреса позволяющие переопределить формат адреса, устанавливаемый для определенной модели памяти по умолчанию.

Модификатор near (ближний) – занимает 2 байта, позволяет адресовать данные в пределах 64 кило байтного сегмента данных.

Модификатор far(дальний) – занимает 4 байта, адресация внутри сегмента, размер 64 кило байта в пределах Мега байтного адресного пространства, при этом 2-х байтный адрес сегмента и 2-х байтное смещение сегмента преобразуются в 20-ти битный адрес в пределах 1 Мега байта следующим образом: адрес сегмента сдвигается влево на 4 разряда (х16) и к нему добавляется смещение.

Huge (огромный) – способ адресации, при котором возможно адресовать объекты размером больше , чем сегмент(far адресация из-за взаимного пересечения этого делать не позволяет).

32- разрядная адресная шина позволяет адресовать 4Г байта памяти. OC WINDOWS, а также так называемые экстендеры памяти в MS-DOS позволяют использовать так называемую плоскую модель памяти, где 32-битный адрес позволяет обращаться к 4Г-байтному пространству.

Указатель на struct, union или перечислимый тип может быть объявлен до того, как этот тип определен, но не может использоваться до определения.

Константа NULL предназначена для нулевой инициализации указателя. Гарантируется, что никакой элемент никогда не будет иметь адрес NULL(отсутствие адреса).

Huge (огромный) адрес хранится в нормализованном виде, поэтому доступ к huge адресу объекта замедляется за счет дополнительных операций нормализации и денормализации.

Описатели. Интерпретация составных описателей.

Описатель в С позволяет объявить следующие объекты: простые переменные, массивы, указатели, функции.

Описатель представляет собой идентификатор, если объявляется простая переменная базового типа, либо struct, либо union.

Объекту присваивается тип заданный спецификацией типа. Для объявления массива специфицирующего типа функция возвращает значение специфицирующего типа либо указателя на значение специфицирующего типа. Идентификатор добавляется [ ]справа, ( ) справа, или * слева.

Составной описатель – идентификатор, дополненный более чем 1 признаком типа массив, указатель или функция. С 1 идентификатором можно образовать множество различных комбинаций признаков, отдельные комбинации запрещены. Не может содержать функцию, функция не может возвращать массив или функцию.

При интерпретации составных частей описателя сначала рассматривается [ ] ( ), стоящие справа от идентификатора. [ ] и ( )имеют равный приоритет и интерпретируются слева направо. После них справа налево рассматривается *, расположенные слева от идентификатора.

Спецификация типа рассматривается на последнем шаге, после того как описатель полностью интерпретирован. Для изменения действующего по умолчанию порядка интерпретации можно использовать внутри описателя ( ).

Правила интерпретаций составных описателей: «изнутри-наружу».

Начинают с идентификатора, затем проверяют наличие открывающихся [ или (, если они есть, интерпретируется правая часть описателя, затем проверяется наличие * слева, если есть, левая часть.

Если на каком-либо этапе справа встречается ), используемая для изменения порядка интерпретации, то вначале проводится интерпретация описателя внутри данной пары скобок, а затем продолжает рассматриваться справа и слева от скобок.

7 6 4 2 1 3 5

char *(*(*X)( ))[10];

  1. Объявить массив из 5 указателей на int

int *Х[5]

  1. Объявить указатель на массив из 5 значений int

int (*Х)[5];

  1. Функция, возвращающая указатель на значение длинное целое

long int*X()

  1. Указатель на функцию

long int (*X)()

  1. Массив из 5 указателей на функции, возвращающие struct.

struct (*X[5])();

  1. Объявить функцию, возвращающую указатель на массив из 3 значений double. double (*f ()) [3];

Описатели с модификаторами.

В качестве модификаторов следующие ключевые слова:

  • Const

  • Volatile (модифицируемый) interrupt

  • Cdecl

  • Pascal

  • Near

  • Far

  • Huge

Действия определяются обработчиком прерывания.

Интерпретация описателей с модификатором.

Cdecl, pascal , interrupt воздействуют на идентификатор и записываются непосредственно перед ним.

Const, volatile, near, far, huge воздействуют либо на идентификатор, либо на указатель непосредственно слева от идентификатора.

Если справа идентификатор, то модифицируется тип объекта, именуемого идентификатором. Если справа указатель, то есть указатель не модифицируемый тип.

Например.

int const*p; p – указатель на постоянное целое

int *const p1; p1 – неизменный указатель на целое

Скобки могут предопределять порядок модификаторов

char far*(far * get int )( int far*);

get int – указатель на far- функцию, требующую 1 аргумент, который является указателем на far – значение типа int и возвращающую указатель на far – значение типа char.

Для доступа к нему требуется 4-байтовый адрес.

Const – не допускает явного присваивания значения переменной либо других косвенных действий по изменению ее значения (инкремент, декремент). Значение указателя с модификатором const изменено быть не может, но может изменяться объект на который указывает. Применение const позволяет выявить нежелательные присваивания значений переменных.

Volatile - показывает, что значение переменной может быть изменено, но не только программой, а также и внешним воздействием (программа обработчиком прерыванием), либо, если переменная соответствует порту ввода/вывода: обменом с ВУ.

Объявление объекта с volatile предупреждает компилятор языка С, что не следует делать предположений относительно стабильности значения объекта в момент вычисления содержащего его выражения, т.к. значение может изменяться в любой момент. Такие volatile-объекты не подвергаются оптимизации, их значения не запускаются в машинные регистры.

Возможно одновременное использование const, volatile. Это означает, что внутри программы объект изменяться не может, но может подвергаться внешним воздействиям.

volatile int ticks;

void interrupt timer() //таймер-функция ничего не возвращающая; обработчик прерывания

{

ticks++;

}

void wait (int interval)

{

ticks =0

while (ticks< interval)

}

Будет ждать в течение времени заданного параметром интервал, в т.0 если функция таймер будет корректно связана с аппаратным прерыванием от таймера.

Если ticks объявить без volatile , то компилятор с высоким уровнем оптимизирующих преобразований, сравнений ticks и interval вынес бы за пределы цикла, т.к. в теле цикла их значения не изменяются, что привело бы к зацикливанию программы.

Запрос на прерывания: от аппаратуры, программный.

Прерывание генерируется, если на линии запроса устанавливается соответствующий сигнал (операция ввода/вывода, запрос на следующую порция данных для принтера)

Для вызова обработчика прерывания программист может установить программный запрос на прерывание. Все функции MS-DOS через механизмы прерывания.

Cdecl, pascal.

Из программы на языке С возможно обращение к программам написанным на других языках, и обратно. При смешивании языков программирования имеют место2 проблемы:

  • Написание внешних имен

  • Передача параметров

Результатом работы компилятора языка С является файл, содержащий объектный код программы. Файлы с таким кодом, полученных при компиляции всех исходных данных программы, компоновщик объединяет в 1 исполняемый файл, при этом происходит разрешение ссылок на глобальные объекты из разных исходных кодов программы. При компиляции все глобальные идентификаторы (имена функций и глобальных переменных) сохраняются в объектном коде и используются компоновщиком во время работы. По умолчанию сохраняется регистр строчные -прописные написания букв и в качестве 1 символа каждого идентификатора, компилятор добавляет символ подчеркивания. Компоновщик различает прописные и строчные буквы.

Pascal

Идентификатор преобразуется к верхнему регистру и к нему не добавляется символ подчеркивания. Такой идентификатор может использоваться в программе на языке Pascal, т.е. в объектном коде сгенерированным с С и с Pascal идентификатор будет представлен идентично. Если идентификатор Pascal применяется к идентификации функции, то он еще оказывает влияние на передачу аргументов. Засылка аргументов в стек производится не в обратном (в С), а в прямом (в Pascal) порядке. Первым засылается первый аргумент. Функции типа Pascal не могут иметь именные переменное число аргументов. … использовать нельзя в списке параметров.

Cdecl.

Существует опция компиляции, присваивающая всем функциям и указателю на функции тип Pascal, при этом можно с помощью cdecl объявить объекты с традиционной для С нотацией.

Замечание.

Все функции стандартных включаемых файлов объявлены с cdecl (stdio.h). позволяет использовать их, даже если используется Pascal. Main всегда должна объявляться с cdecl, т.к. модуль поддержки исполнения передает управление, использую вызывающуюся последовательность языка С.

Near, Far, Huge.

Операции отношения корректно выполняются над huge-указателем, некорректно над far-указателем. Использование hugeтребует дополнительного времени на операцию нормализации и денормализации. В файле сегменты пересекаются, huge-указатель нормализует.

Interrupt

Для объявления функций, работающих с векторами прерывания процесса. Вектор прерывания – адрес 4 – байтной функции обработки прерывания. Номер прерывания определяет адрес обработчика прерывания, хранящийся в векторе, смещение = N вектора*4 (деление на 0). Ctrl-alt-delete.

Функции этого типа при компиляции генерируется дополнительный код в точках входа и выхода из функции для сохранения и восстановления регистров макропроцессора.

Функция обработки прерываний должна иметь void. Interrupt не может одновременно использоваться с near, far, huge.

Классы памяти.

Спецификация классов памяти определяет время ее жизни (глобальное или локальное) и влияет на область действия.

Объект с глобальным временем существует и имеет значение на протяжении программы, все функции имеют глобальное.

Переменные с локальным: выделяется новая ячейка, когда управление передается блоку, в котором она определена. Когда возвращается из блока, переменная теряет свое значение.

Спецификация классов памяти:

  • Auto

  • Registr

  • Static

  • Extern

Extern

Область действия на все исходные формы программы.

Auto, Registr – имеют локальное время жизни.

Static, Extern – определяют объекты с глобальным временем жизни. В совокупности с местоположением объекта спецификации класса памяти определяет область действия переменной или функции. Область действия объявляет часть программы, куда возможен доступ.

Время жизни. Область действия.

  • Глобальное

  • Локальное

уровень

объект

спецификация

класса памяти

время жизни

область действия

1.

Внешний

Определение переменной

Static

Глобальное

Остаток исходного файла

Объявление переменной

Extern

Глобальное

Остаток исходного файла

Объявление или определение функции

Static или Extern

Глобальное

Остаток исходного файла

2.

Внутренний

Объявление переменной

Extern

Глобальное

Блок

Определение переменной

Static

Блок

Определение переменной

Auto

Registr

Глобальное

Локальное

Блок

Объявление функции

Extern

Static

Локальное

Блок

Блок представляет собой составной оператор. Переменная объявленная внутри блока, имеющая тоже имя, что и объявленная на внешнем уровне переменная, заменяет в пределах блока объявление (локальное переобъявление переменной). Область действия переменной внешнего уровня восстанавливается по завершению блока. Блок внутри другого блока может содержать локальные переобъявления переменных, объявленных в охватывающем блоке.

Пространство имен.

Имена (идентификаторы)используются для ссылок на различного рода объекты (функции, переменные и т.п.)

При соблюдении определенных правил допускается использование 1 идентификатора для >1 программного объекта.

Пространства имен в С устанавливаются для идентификации объектов различного рода. Внутри одного пространства имена должны быть уникальны, в разных могут быть идентичны.

Для определения принадлежности имени к объекту используется контекст объекта. В С 4 пространства:

  • Объекты (переменные, функции, формальные параметры, элементы списка перечисления и что определяется с помощью typedef). Уникальность имен в представляемом пространстве связана с исходной областью действия. Имена могут совпадать, если не пересекаются области действия именуемых объектов.

  • Объекты – теги всех переменных перечислимого типа: struct,union. В пределах области действия каждый тег должен отличаться от другого. Ни с какими другими именами теги не конфликтуют.

  • Элементы struct,union. Образуют свое пространство имен. Имя уникально в struct,union, но не обязано отличаться от других имен.

  • Метки операторов. Метка должна отличаться от других в пределах одной функции.

Область действия Extern распространяется на все исходные файлы. Объявления, расположенные вне тел функций относятся к внешнему уровню, внутри тел — к внутреннему. Функции могут быть объявлены как Static или Extern либо без спецификации класса памяти.

Функции всегда имеют глобальное время жизни. Объявление функции без спецификации аналогично объявлению с Extern. Если в объявлении функции указан класс Static, то в определении тоже должен указываться Static.

Объявление функции на внутреннем уровне по смыслу эквивалентно, т.е. оно действует не до конца блока объявления, а до конца файла.

Объявление переменной на внешнем уровне.

Используется класс памяти Static и Extern; класс памяти Auto и Registr на внешнем уровне недопустим; класс памяти может отсутствовать.

Объявление на внешнем уровне.

  • Либо определение

  • Либо объявление (ссылки на определение, сделанные в другом месте).

Определение внешней переменной - объявление, вызывающее выделение памяти для этой переменной и ее инициализацию (явную или неявную).

Определение на внешнем уровне задается одной из следующих форм:

        1. переменная определяется путем ее объявления с класса памяти Static, такая переменная может быть явно инициализирована константным выражением static int.

Если инициализатор отсутствует, то переменная автоматически инициализируется нулевым значением во время компиляции.

k=16, n=0

        1. Переменная может быть определена без класса памяти и при этом явно проинициализирована (int j=3).

Область действия переменной, определенной на внешнем уровне распространяется от точки, где она определена, до конца исходного файла, определенная переменная недоступна.

На другие исходные файлы область действия распространяется в том случае, если переменная объявлена без указания класса памяти Static и в других исходных файлах имеется ее определение. Если в объявлении присутствует класс памяти Static, то в других исходных файлах могут определяться другие переменные с исходным именем и любым классом памяти. Эти переменные не связаны между собой, так как каждое определение Static – переменной доступно только в пределах своего исходного файла.

Спецификация класса памяти Extern.

Для объявления переменной, определенной где-то в другом месте программы. Такие объявления используют в случае, когда надо распространить на данный исходный файл область действия переменной, определенной в другом исходном файле, либо сделать переменную доступную в том же исходном файле, выше её определения.

Область действия переменной распространяется от места объявления до конца исходного файла. Инициализация Extern – переменных не допускается, так как они ссылаются на переменные, значения которых определены в другом месте.

Каждая переменная внешнего уровня должна быть определена только 1 раз в каком-либо из составных файлов программы.

Замечание. Существует исключение из вышеописанных правил, позволяющее опускать в объявлении переменной на внешнем уровне и спецификацию класса памяти, и инициализатор.

int х; имеет различный смысл от контекста

        1. если в каком-то другом исходном файле есть определение на внешнем уровне, переменная с таким же именем, то данное объявление является ссылкой на это определение, то есть подразумевается наличие ключевого слова Extern.

        2. если такого определения переменной в программе нет, то данное определение считается определением переменной. На этапе компоновки программы переменной выделяется память, которая инициализируется нулевым значением.

Если в программе больше 1 объявления переменной с одним и тем же именем, размер выделяемой памяти будет равен размеру наибольшего типа среди всех объявлений. Таким образом, глобальную переменную можно рассматривать как единую область памяти, разделенную несколькими исходными файлами, причем в каждом из них переменная может иметь различный тип. Этим пользоваться не рекомендуется (рекомендуется всегда инициализировать переменные на внешнем уровне). В результате при случае совпадении имен обнаружит ошибку повторной инициализации глобальной переменной и сообщит об ошибке.

В одном исходном файле возможно наличие на внешнем уровне нескольких объявлений с одним и тем же именем. Реакция компилятора зависит от реализации. Возможны следующие варианты:

  • присутствие объявления класса Extern и Static.тор проинтерпретирует как Static;

  • Static и отсутствие класса памяти приведет к объявлению Static, либо сигнал об ошибке;

  • Static и Extern. Будет использован класс Static;

  • Отсутствует Static. Либо Static, либо ошибка.

Присутствует 2 исходных файла программы.

  1. исходный файл

extern int i

объявляем i, которая ссылается на другое определение i

main()

{

i+=1; //увеличим i на 1

printf (“%d”,i); //напечатаем i

next ();// вызовем функцию next

}//конец главного модуля

Определим значение i

int i=3;

в соответствие с определением будет напечатано 4.

next (){

определим функцию next:

i+=1;

увеличивает i на 1

printf (“%d”,i);

напечатаем значение i

будет напечатано 5

вызовем другую функцию other()

  1. исходный файл

extern int i

other()

{

i+=1;

printf (“%d”,i); //напечатаем i: будет выведено 6

}

Без инициализации переменная инициализируется 0; получаем 1,2,3 соответственно.

Использовать не рекомендуется (трудно обнаружить ошибки). Глобальные переменные и переходы в структурное программирование не используются.

Объявление переменной на внутреннем уровне.

На внутреннем уровне можно использовать любой из классов памяти. При отсутствии auto. Понятия определения и объявления совпадают, если не задана в объявлении Extern.

Спецификация класса памяти auto объявляет переменную с локальным временем жизни. Область действия блок и вложенные блоки. Переменная класса памяти auto не инициализируется по умолчанию. Без инициализатора ее значение не определено. Память под auto- переменную отводится в стеке.

Регистровые переменные. При наличие свободных регистров процессора размещается в них (иначе класс памяти auto). При вызове функции из блока, где регистровые переменное содержимое регистров сохраняется в памяти, а при возврате восстанавливается. Для каждого рекурсивного входа в блок порождается новый набор регистровых переменных, при этом каждый раз проводится инициализация переменных, в объявлении которых заданы инициалы.

Static – переменная внутреннего уровня с глобальным временем жизни. Область действия блок и вложенные блоки. При выходе из блока значение Static – переменной сохраняется в отличие от auto-переменной. Static – переменные могут инициализироваться константным выражением. Если инициализатор отсутствует, то 0. Инициализируется во время компиляции и не повторяется при входе в блок. Все рекурсивные вызовы разделяют единственный экземпляр Static – переменной.