Инициализация глобальных и локальных переменных

 

При инициализации необходимо придерживаться следующих правил:

o Объявления, содержащие спецификатор класса памяти extern, не могут содержать инициаторов;

O Глобальные переменные всегда инициализируются, и если это не сделано явно, то они инициализируются нулевым значением;

O Переменная с классом памяти static может быть инициализирована константным выражением. Инициализация для них выполняется один раз при создании переменной. Если явная инициализация отсутствует, то переменная инициализируется нулевым значением;

O Явная инициализация переменных с классом памяти auto или register выполняется всякий раз при входе в блок, в котором они объявлены. Если инициализация переменных в объявлении отсутствует, то их начальное значение не определено;

O Начальными значениями для глобальных переменных и переменных с классом памяти static должны быть константные выражения. Адреса таких переменных являются константами, которые можно использовать для инициализации объявленных глобально указателей. Адреса переменных с классом памяти auto или register не являются константами и их нельзя использовать в инициаторах.

УКАЗАТЕЛИ

Указатель – это переменная, значением которой является адрес некоторого объекта (обычно другой переменной) в памяти компьютера. Подобно тому, как переменная типа char имеет в качестве значения символ, а переменная типа int – целочисленное значение, переменная типа указателя имеет в качестве значения адрес ячейки оперативной памяти. Допустимые значения для переменной-указателя – множество адресов оперативной памяти компьютера.

 

Указатель является одной из наиболее важных концепций языка C.

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

· Указатели являются средством, при помощи которого функции могут изменять значения передаваемых в нее аргументов;

· При помощи указателей выполняется динамическое распределение памяти;

· Указатели позволяют повысить эффективность программирования;

· Указатели обеспечивают поддержку динамических структур данных (двоичные деревья, связные списки).

Однако указатель может вызвать и ряд затруднений, например, если указатель содержит неправильное значение, программа может быть неработоспособной. Можно легко ошибиться при использовании указателей; к тому же ошибки, связанные с неправильными значениями указателей, найти очень трудно.

Итак, указатель – это новый тип данных. Для него определены понятия константы, переменной, массива. Как и любую переменную, указатель необходимо объявить. Объявление указателя состоит из имени базового типа, символа * (звездочка) и имени переменной.

Общая форма объявления указателя:

тип *имя;

Тип указателя определяет тип объекта, на который указатель будет ссылаться, например,

int *p1;

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

Операция получения адреса

 

Понятие указателя тесно связано с понятием адреса объекта. В C есть специальная операция, позволяющая получить адрес любой переменной:

&p – получение адреса, где p – идентификатор переменной. Результатом операции является адрес переменной p.

Пример программы:

# include <stdio.h>

int main()

{

int x=2,*p;

p=&x;

printf("\n x=%d address of x=%u",x,p);

return 0;

}

 

Понятие переменной типа указатель также связано с операцией косвенной адресации *, называемой еще операцией разыменования, которая имеет структуру: *р – разыменование, где р – идентификатор переменной-указателя. Эта запись означает, что в ячейку с адресом, записанным в переменную р, помещено значение некоторой величины.

Операторы ptr=&a; и val=*ptr; равнозначны оператору val=a;

Например,

n=32;

p=&n; /* p–адрес ячейки, куда записано n */

v=*p;

В результате выполнения этих действий в переменную v будет помещено число 32.

 

Операции над указателями

 

Над указателями определено 5 основных операций:

 

§ Определение адреса указателя: &p, где p – указатель (&p – адрес ячейки, в которой находится указатель).

§ Присваивание. Указателю можно присвоить адрес переменной p=&q, где p – указатель, q – идентификатор переменной.

§ Определение значения, на которое ссылается указатель: *p (операция косвенной адресации).

§ Увеличение (уменьшение) указателя. Увеличение выполняется как с помощью операции сложения (+), так и с помощью операции инкремента (++). Уменьшение – с помощью операции вычитания (–) либо декремента (––).

Например, пусть p1 – указатель, тогда р1++ перемещает указатель на:

o 1 байт, если *p1 имеет тип char;

o 4 байта, если *p1 имеет тип int (в 32 разрядной операционной системе) или 2 байта (в 16 разрядной операционной системе);

o 4 байта, если *p1 имеет тип float.

§ Разность двух указателей. Пусть р1 и р2 – указатели одного и того же типа. Можно определить разность р1 и р2, чтобы найти, на каком расстоянии друг от друга находятся элементы массива.

 

Пример программы:

Даны адреса переменных &a=63384,&b=64390,&c=64404. Что напечатает ЭВМ?

# include <stdio.h>

int main()

{

float a,*p1;

int b,*p2;

char c,*p3;

a=2.5; b=3; c='A';

p1=&a; p2=&b; p3=&c;

p1++; p2++; p3++;

printf("\n p1=%u, p2=%u, p3=%u",p1,p2,p3);

return 0;

}

Ответ: р1=63388, р2=64392, р3=64405.

 

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

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

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

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