Смекни!
smekni.com

Основы программирования на языке Си (стр. 24 из 27)

ного положения вещей, но все же оно достаточно близко к практическим целям. От-

ветственность за то, чтобы при выполнении программы не возникали подобные бес-

смысленные значения, возлагается на программиста.

102

В программе 2.1 демонстрируется, как в исходном тексте на Си++ располага-

ются описание типа структуры, объявление переменных и обращение к компонентам

структуры.

struct SimpleStructure {

char c;

int i;

float f;

double d;

};

void main()

{

SimpleStructure s1, s2;

s1.c = 'a';

s1.i = 1;

s1.f = 3.14f; // Буква 'f' в конце вещественной константы

// означает, что это константа типа float,

// а не double

s1.d = 0.00093;

s2.c = 'b';

s2.i = 2;

s2.f = 6.28f;

s2.d = 0.15;

}

Программа 2.1.

В программе 2.1 в функции "main()" создаются две переменные типа

"SimpleStructure" с именами "s1" и "s2". У каждой из этих переменных есть соб-

ственный набор компонент с именами "c", "i", "f", и "d". Т.е. "s1" и "s2" являются

наборами независимых переменных. Для выбора компонент внутри "s1" или "s2"

применяется _______операция "." (точка). Подобная запись применялась в предыдущих лек-

циях для обращения к функциям-членам объектов "cin" и "сout". Обращения к ком-

понентам классов в объектно-ориентированном Си++ очень похожи на обращения к

компонентам структур.

Переменные типа структуры можно присваивать, передавать, как параметры

функции, и возвращать из функции в качестве результата. Например:

Person current;

Person set_current_person( Person& p )

{

Person prev = current;

current = p;

return prev;

}

Остальные операции, такие, как сравнение ("==" и "!="), для структур по умол-

чанию не определены, но программист может их определить при необходимости.

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

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

Например, допустимы следующие прототипы функций:

103

struct S; // S - имя некоторого типа

S f();

void g(S v1);

Но эти функции нельзя вызывать, если тип "S" не определен:

void h()

{

S a; // ошибка: S не объявлено

f(); // ошибка: S не объявлено

g(a); // ошибка: S не объявлено

}

3. Доступ к компонентам структуры через указатель

Во всех предыдущих примерах компоненты структур использовались в выра-

жениях подобно обычным переменным. Аналогично обычным переменным, можно

создавать указатели на переменные-структуры. Для доступа к компонентам структу-

ры через указатель применяется операция "->". Например:

void print_person( Person* p )

{

cout << p->name << '&bsol;n';

cout << p->birthdate.day << '&bsol;n';

cout << p->birthdate.month << '&bsol;n';

cout << p->birthdate.year << '&bsol;n';

cout << p->salary << '&bsol;n&bsol;n';

}

Функцию "print_person()" можно переписать в эквивалентном виде с по-

мощью операции разыменования указателя "*" и операции доступа к компонентам

структуры ".". Обратите внимание на скобки в записи "(*p).", которые необходимы,

поскольку приоритет операции "." выше, чем у "*":

void print_person( Person* p )

{

cout << (*p).name << '&bsol;n';

cout << (*p).birthdate.day << '&bsol;n';

cout << (*p).birthdate.month << '&bsol;n';

cout << (*p).birthdate.year << '&bsol;n';

cout << (*p).salary << '&bsol;n&bsol;n';

}

Использование указателей на структуры показано в программе 3.1. В функции

"main()" этой программы указателю "sp" cначала присваивается адрес "s1", и затем

с помощью операции "->" компонентам "s1" присваиваются начальные значения. За-

тем указателю "sp" присваивается адрес "s2", и компоненты этой структуры также

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

екты является одним из важнейших свойств указателей, которые, в частности, полез-

ны для реализации динамических структур данных (например, связного списка или

стека).

struct ExStruct {

104

char c;

int i;

float f;

double d;

};

void main()

{

ExStruct s1, s2;

ExStruct* sp = &s1;

sp->c = 'a';

sp->i = 1;

sp->f = 3.14f;

sp->d = 0.00093;

sp = &s2;

sp->c = 'b';

sp->i = 2;

sp->f = 6.28f;

sp->d = 2.5;

}

Программа 3.1.

В 7-й лекции (для реализации связного списка) и в 8-й лекции уже рассматри-

валось понятие рекурсивных структур данных. Для создания в структуре ссылки на

структуру такого же типа необходимо пользоваться указателем. В программе 3.2 соз-

даются две структуры, содержащие ссылки друг на друга.

struct SelfReferential {

int i;

SelfReferential* sr;

};

void main()

{

SelfReferential sr1, sr2;

sr1.sr = &sr2;

sr2.sr = &sr1;

sr1.i = 47;

sr2.i = 1024;

}

Программа 3.2.

Описание без указателя является недопустимым, т.к. в нем при описании ком-

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

определен полностью:

struct SelfReferential {

int i;

SelfReferential sr; // Недопустимое описание компоненты

};

4. Массивы и структуры

У массива и структуры есть общее свойство: оба этих типа данных являются

типами с произвольным доступом. Но структура более универсальна, поскольку не

105

требуется, чтобы типы всех ее компонент были одинаковы. С другой стороны, в неко-

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

тов могут вычисляться, а имена компонент структуры это фиксированные иденти-

фикаторы, задаваемые в описании типа.

Массивы и структуры могут комбинироваться различными способами. Напри-

мер, i-я компонента массива "a", который является компонентой структуры "r", обо-

значается как

r.a[i]

Напротив, компонента с именем "s", входящая в i-ю компоненту-структуру

массива структур "a" обозначается как

a[i].s

В качестве примера далее приведено описание переменной "screen", которая

является двумерным массивом структур типа "Cell". Этот массив предназначен для

хранения содержимого текстового экрана размером 80х25 знакомест:

struct Cell {

unsigned char character; // Символ

int foreground; // Цвет символа

int background; // Цвет фона

bool blink; // Мигание включено/выключено

};

void main()

{

Cell screen[25][80];

...

}

5. Перегрузка операторов

В 3-й лекции (п. 4) были описаны средства перегрузки функций. Си++ допус-

кает перегрузку не только функций, но и операторов, таких, как _______"+", "-", "*" (и боль-

шинство других "+=", "->" и даже "()"). Средства перегрузки операторов полезны

тогда, когда желательно, чтобы пользовательские типы данных в исходном тексте

программ выглядели в выражениях подобно встроенным типам Си++. Поясним это на

нескольких примерах.

Для сложения комплексных чисел (описание структуры см. п.2) можно приме-

нить функцию:

Complex C_add( const Complex& x, const Complex& y )

{

Complex t;

t.re = x.re + y.re;

t.im = x.im + y.im;

return t;

}

Параметры функции "C_add(...)" передаются по ссылке, и, кроме того, опи-

саны как константы, чтобы запретить изменения параметров внутри функции. Пере-

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

ров, а константное описание защищает параметры от изменений.

106

Допустим, в программе реализована также функция "C_mult(...)" для умно-

жения двух комплексных чисел. Ниже приведен пример использования этих функ-