Выбрать главу

Переменная может быть инициализирована выражением любой сложности, включая вызовы функций. Например:

#include cmath

#include string

double price = 109.99, discount = 0.16;

double sale_price( price * discount );

string pet( "wrinkles" );

extern int get_value();

int val = get_value();

unsigned abs_val = abs( val );

abs() – стандартная функция, возвращающая абсолютное значение параметра.

get_value()– некоторая пользовательская функция, возвращающая целое значение.

Упражнение 3.3

Какие из приведенных ниже определений переменных содержат синтаксические ошибки?

(a) int car = 1024, auto = 2048;

(b) int ival = ival;

(c) int ival( int() );

(d) double salary = wage = 9999.99;

(e) cin int input_value;

Упражнение 3.4

Объясните разницу между l-значением и r-значением. Приведите примеры.

Упражнение 3.5

Найдите отличия в использовании переменных name и student в первой и второй строчках каждого примера:

(a) extern string name;

string name( "exercise 3.5a" );

(b) extern vectorstring students;

vectorstring students;

Упражнение 3.6

Какие имена объектов недопустимы в С++? Измените их так, чтобы они стали синтаксически правильными:

(a) int double = 3.14159; (b) vector int _;

(c) string namespase; (d) string catch-22;

(e) char 1_or_2 = '1'; (f) float Float = 3.14f;

Упражнение 3.7

В чем разница между следующими глобальными и локальными определениями переменных?

string global_class;

int global_int;

int main() {

int local_int;

string local_class;

// ...

}

3.3. Указатели

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

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

* указатель на int, содержащий значение адреса 1000, направлен на область памяти 1000-1003 (в 32-битной системе);

* указатель на double, содержащий значение адреса 1000, направлен на область памяти 1000-1007 (в 32-битной системе).

Вот несколько примеров:

int *ip1, *ip2;

complexdouble *cp;

string *pstring;

vectorint *pvec;

double *dp;

Указатель обозначается звездочкой перед именем. В определении переменных списком звездочка должна стоять перед каждым указателем (см. выше: ip1 и ip2). В примере ниже lp – указатель на объект типа long, а lp2 – объект типа long:

long *lp, lp2;

В следующем случае fp интерпретируется как объект типа float, а fp2 – указатель на него:

float fp, *fp2;

Оператор разыменования (*) может отделяться пробелами от имени и даже непосредственно примыкать к ключевому слову типа. Поэтому приведенные определения синтаксически правильны и совершенно эквивалентны:

string *ps;

string* ps;

Однако рекомендуется использовать первый вариант написания: второй способен ввести в заблуждение, если добавить к нему определение еще одной переменной через запятую:

//внимание: ps2 не указатель на строку!

string* ps, ps2;

Можно предположить, что и ps, и ps2 являются указателями, хотя указатель – только первый из них.

Если значение указателя равно 0, значит, он не содержит никакого адреса объекта.

Пусть задана переменная типа int:

int ival = 1024;

Ниже приводятся примеры определения и использования указателей на int pi и pi2:

//pi инициализирован нулевым адресом

int *pi = 0;

// pi2 инициализирован адресом ival

int *pi2 = ival;

// правильно: pi и pi2 содержат адрес ival

pi = pi2;

// pi2 содержит нулевой адрес