Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЗФ_ОАиП / Лекции ГГУ Скорины - Программирование.doc
Скачиваний:
179
Добавлен:
21.03.2016
Размер:
2.27 Mб
Скачать

29.7. Структуры и функции

Передавать структуры в функции можно по-разному. Допустим нам надо написать функции для точек и прямоугольников.

struct Point {

int x;

int y;

};

struct Rect {

struct Point pnt1;

struct Point pnt2;

int color;

};

Возникает вопрос: а как передавать функциям информацию о точках и прямоугольниках? Существует по крайней мере три подхода: передавать компоненты по отдельности, передавать всю структуру целиком и передавать указатель на структуру. Каждый подход имеет свои плюсы и минусы.

// формирование точки по компонентам x и y

struct Point makepoint(int x, int y) {

struct Point temp;

temp.x = x;

temp.y = y;

return temp;

}

Никакого конфликта между именами аргументов и именами членов структуры не возникает.

Теперь с помощью функции makepoint() можно выполнять инициализацию любой структуры или формировать структурные аргументы для той или иной функции:

struct Rect screen;

struct Point middle;

screen.pnt1 = makepoint(0, 0);

screen.pnt2 = makepoint(25, 50);

middle = makepoint((screen.pnt1.x + screen.pnt2.x) / 2,

(screen.pnt1.y + screen.pnt2.y) / 2);

// сложение двух точек

struct Point addpoint(struct Point p1, struct Point p2) {

p1.x += p2.x;

p1.y += p2.y;

return p1;

}

Здесь оба аргумента и возвращаемое значение – структуры. В функции мы увеличиваем поля прямо в p1 и не используем для этого временной переменной, т.к. структурные параметры функции передаются по значению точно так же, как и любые другие параметры.

Если функции передается большая структура, то эффективнее передавать указатель на структуру, а не создавать в стеке копию структуру целиком. Также, если функция должна изменять структурную переменную, в нее следует передавать указатель на модифицируемую структуру.

// определение местоположения и цвета прямоугольника

struct Rect makerect(struct Rect *p) {

printf("Введите координаты прямоугольника:\n");

scanf("%d%d%d%d", &p->pnt1.x, &p->pnt1.y,

&p->pnt2.x, &p->pnt2.y);

printf("Введите цвет прямоугольника:\n");

scanf("%d", &p->color);

return *p;

}

struct Rect t1, t2;

t2 = makerect(&t1); // t2 == t1

Без каких либо ограничений можно возвращать из функции и указатель на структуру.

29.8. Объединения (union)

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

Определение объединения аналогично определению структуры:

union имя_объединения {

описания_элементов;

};

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

Доступ к полям объединения выполняется или через ‘.’, или через ‘->’, если для доступа используется указатель:

имя_объединения.элемент

или

указатель_на_объединение->элемент

Пример объединения, которое можно интерпретировать как знаковое или беззнаковое целое число:

union number {

int svar;

unsigned int uvar;

};

number x = {100}; // описание переменной типа

number *p; // можно описывать указатели на union

number y[10]; // можно описывать массивы

union { // можно и так описывать переменные,

int svar; // без определения имени union

unsigned int uvar;

} numb;

Инициализировать объединение можно только значением, имеющим тип его первого элемента (например, переменную x можно инициализировать лишь значением типа int).

Значение одного из этих двух заданных типов может быть присвоено переменной x и далее использовано в выражениях, если это правомерно, т.е. если тип взятого из нее значения совпадает с типом последнего присвоенного ей значения. Выполнение этого требования в каждый текущий момент – задача программиста. В том случае, если нечто запомнено как значение одного типа, а извлекается как значение другого типа, результат зависит от реализации.

Объединения могут входить в структуры и массивы, и наоборот. Запись доступа к элементу объединения, находящегося в структуре (как и структуры, находящейся в объединении), такая же, как и для вложенных структур.

Например, в массиве структур

struct {

char *name;

int flags;

int type;

union {

int ival;

float fval;

char *cval;

} u;

} xxx[10];

к ival i-го элемента массива обращаются так: xxx[i].u.ival.

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

Пример работы с объединением:

void main() {

union number {

int svar;

unsigned int uvar;

char cvar[2];

};

// аппаратные особенности IBM PC – размещение числовых кодов в памяти,

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

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

// при хранении в памяти целых чисел для int байты переставляются,

// для long переставляются сначала по 2 байта, а затем уже байты

// int: 1б 2б => в памяти 2б 1б

// long: 1б 2б 3б 4б => в памяти 4б 3б 2б 1б

// 5010 = 3216 = 0x32

number x = {50}; // {50, 50, “0x32 0x0” (“2”)}

char ss[50];

strcpy(ss, x.cvar); // ss = “2”

x.svar = -100; // {-100, 65436, “0x9C 0xFF”}

unsigned int xx = x.uvar; // xx = 65436

x.svar >>= 1; // {-50, 65486, “0xCE 0xFF”}

// 1111 1111 1001 1100 => 1111 1111 1100 1110 (размножение знака)

x.svar = -100; // {-100, 65436, “0x9C 0xFF”}

x.uvar >>= 1; // {32718, 32718, “0xCE 0x7F”}

// 1111 1111 1001 1100 => 0111 1111 1100 1110

}

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