Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Язык Си

..pdf
Скачиваний:
6
Добавлен:
20.11.2023
Размер:
7.64 Mб
Скачать

union my_type {int i;

char ch;

long double a;

} X;

Объявление объединения (так же как и структуры) заканчива­ ется точкой с запятой.

В отличие от структуры для переменной типа union места в памяти выделяется ровно столько, сколько надо элементу объеди­ нения, имеющему наибольший размер в байтах. В приведенном выше примере под переменную X будет выделено 12 байт памяти, так как максимальный размер имеет поле а - 10 байт, а ближайшее кратное 4 - это 12. Таким образом, все поля объединения начина­ ются с одного и того же адреса, т.е. к одним и тем же данным объ­ единения можно получать доступ через разные поля:

union my_type

байта

 

{int

i;

//4

 

char

ch; //1

байт

байт

long

double

a; //10

} X;

 

 

 

 

struct

my_type

 

{int

i;

//4 байта

 

char

ch; //1

байт

байт

long

double

a; //10

} X;

 

 

 

 

 

 

 

а (1 0

б а й т )

 

1

(4

б а й т а )

 

сЫ 1 б а й т )

 

N-

1 1

1

1 II

1 1 1 1

 

 

 

 

 

 

 

X (12

б а й т )

i i i i i i i i i i i

i i i ~ m

X (16

бай т)

Синтаксис использования элементов объединения такой же, как и для структуры. Операции, применимые к структурам, при­ менимы и к объединениям, т.е. законны присваивание объедине­ ния и копирование его как единого целого, взятие адреса от объе­ динения и доступ к отдельным его элементам (операция «точка» или в случае указателей «стрелочка»). Объединения также могут входить в структуры и массивы.

П рим ер. Использование объединения.

#include<conio.h>

#include<stdio.h>

union student {char *gruppa; int kurs;

main()

{student Ivanov; Ivanov.kurs = 5; Ivanov.gruppa = "RKT-IO";

printf("%d\n",Ivanov.kurs); print f ("%s",Ivanov.kurs);

getch(); return 0;

Результат на экране:

4202684

RKT-10

Поскольку поля *gruppa и kurs имеют одинаковые ячейки в памяти, строка

Ivanov.gruppa = "RKT-10";

перезаписьшает ранее заданное значение Ivanov.kurs = 5, и теперь там вместо целого числа хранится строковый массив. Если печа­ тать поле kurs со спецификатором целого числа %d, то получаем непонятное значение 4202684. Но если печатать поле kurs со стро­ ковым спецификатором %s, то получаем правильную строку: RKT-10.

11.3.Поля битов

Вотличие от других языков программирования язык Си обес­ печивает доступ к каждому биту памяти. Это имеет свои преиму­ щества. Например, если в программе имеются переменные, кото­ рые принимают только значение 0 или 1 (их также называют фла­ гами или логическими переменными), то для представления каждой из них достаточно 1 бита, что экономит память. Кроме то­ го, с помощью битовых полей можно формировать объекты внут­ реннего представления, не кратные байту.

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

Основная форма объявления такой структуры следующая:

struct

имя структуры

{тип

имя 1: длина в битах;

тип

имя 2: длина в битах;

тип

имя N: длина в битах;

};

 

Б и т овое поле м ож ет быть т олько элементом струк­

т уры или объединения и вне объект ов эт их типов не встреча­

ет ся.

 

 

 

Б и т овы е поля не им ею т

адресов и не могут

объеди­

нят ься в м ассивы .

 

 

 

В объявлении ст рукт уры

тип бит ового

поля

мож ет

бы т ь т олько int, возм ож но, с модиф икат ором

unsigned или

signed.

 

 

 

Можно пропустить описание /-го поля, тогда соответствую­ щее количество бит не используется (пропускается). Размер выде­ ляемой памяти не может быть менее 1 байта, поэтому если указать

struct onebit

{unsigned one_bit: 1;

}obj;

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

В структуре могут быть смешаны обычные переменные и по­ ля битов.

П рим ер. Перевод целого десятичного числа в двоичную сис­ тему.

#include<conio.h>

#include<stdio.h>

void DecToBin(int);

union BINARYCODE {int chislo; struct

{unsigned bO:l; unsigned bl:l; unsigned b2:l; unsigned b3:l; unsigned b4:l; unsigned b5:l; unsigned b6:l; unsigned b7:l; }byte;

};

main () {int a;

printf("Введите число от 0 до 255: "); scant("%d",&а);

DecToBin(а);

getch(); return 0;

void DecToBin(int x)

{BINARYCODE a; a.chislo = x;

printf ("%u%u%u%u%u%u%u%u",a.byte.b7, a.byte.b6, a.byte.bS, a.byte.b4, a.byte.b3,a.byte.b2, a .byte.bl,a .byte.bO);

}

Благодаря тому, что компьютер хранит данные в памяти в двоичной системе счисления, нам не нужно использовать никаких алгоритмов для перевода из десятичной системы счисления, а дос­ таточно распечатать биты, где хранится число. Поскольку мы име­ ем дело с объединением , поля chislo и byte начинаются с одного и того же адреса, а поле byte еще имеет 8 битовых полей длиной 1 бит каждое. Таким образом, при печати мы выводим каждый бит памяти, в которую поместили исходное число.

11.4. Перечисления

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

епит имя перечисления{список перечисления} список пере­

менных;

Пример создания перечисления:

е т ш seasons {win, spr, sum, aut} sl,s2;

Список переменных при задании шаблона перечисления мо­ жет быть пустым:

е т ш seasons {win, spr, sum, aut};

а переменные созданы позже:

seasons sl,s2;

Каждое из имен в списке перечисления win, spr, sum и aut представляет собой целую величину. По умолчанию они соот­ ветственно равны 0, 1, 2, 3. Значения по умолчанию можно изме­ нить.

enum seasons {win=2, spr=3, sum, aut};

Если после каких-то констант целое значение не задано, то оно считается равным на один больше предыдущего, т.е. sum = 4, aut = 5.

В момент объявления, например,

seasons s;

переменная инициализируется случайным значением. В дальней­ шем переменной можно задавать любые допустимые значения, например

s=aut;

Поскольку перечисляемые типы относятся к целым порядко­ вым типам, к ним применимы все операции сравнения:

<

>

<=

>=

==

! =

Например, можно написать

if(s>spr)

или

if(s !=win)

Также переменные перечисляемого типа можно использовать в операторе switch:

switch(s)

{case win:

printf("Зима");

break;

case spr:

printf("Весна");

break;

case sum:

printf("Лето" );

break;

case aut:

printf("Осень" );

break;

}

П рим ер. Работ а с перечисляемым типом.

#include<conio.h>

#include<stdio.h>

enum seasons {win=l, spr, sum, aut};

main()

{seasons 51,52,33,34;

printf("%d %d",s2,s4); //печатает случайные //значения,поскольку s2, s4 //не инициализированы

s2

=

spr;

 

 

 

 

s4 =

aut;

%d",s2,s4);

//печатает

2

4

printf("\n%d

si

=

s2;

 

 

 

 

s3

=

s4;

%d",sl,s3);

//печатает

2

4

printf("\n%d

si— ; //ОШИБКА! sl++; //ОШИБКА!

sl=s2-l; s3=s2+l;

printf("\n%d %d",sl,s3); //печатает 2 4

getch(); return 0;

Обратите внимание, что операции ++ и — не применимы к переменным перечисляемого типа.

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

11.5. Переименование типов - typedef

Язык Си позволяет давать новое название уже существую­ щим типам данных. Для этого используется ключевое слово type­ def, При этом не создается новый тип данных.

Например,

typedef float real;

typedef long double extend;

Теперь вместо определений

float a,b;

long double x[5],z;

можно пользоваться

real a,b;

extend double x[5],z;

РЕШЕНИЕ ЗАДАЧ

Задача 1. Найти координаты центра масс тела, состоящего из трех частиц.

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

m

X

У

z

 

 

 

20

2

4

6

40

6

-2

8

10

1

3

2

Для определения координат центра масс тела будем исполь­ зовать известные формулы

п

п

I ц щ

n

У с =

;

“c

n

/=1

i=i

 

 

/=1

 

 

I

#include<conio.h>

#include<stdio.h>

const int n=3; //число частиц

struct Particle

{float mass; //масса частицы float X; //координата x частицы float Y; //координата у частицы float Z; //координата z частицы

main() {int i;

Particle points[n]={{20,2,4,6}, {40,6,-2,8},{10,1,3,2}};

float Center[n]={0,0, 0}; float m=0;

// масса тела for(i=0;i<n;i++) m+=points[i].mass;

// координаты центра масс for(i=0;i<n;i++) {Center[0]+=points[i].mass*points[i].X/m; Center[1]+=points[i].mass*points[i].Y/m; Center[2]+=points[i].mass*points[i].Z/m;

}

puts (м X

Y

Z11);

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

printf(M%9.4f",Center[i]);

getch(); return 0;

}

Результат на Э1фане:

X Y Z

4.1429 0.4286 6.5714

Задача 2. Создать структуру «точка» со следующими поля­ ми: координата х, координата у, координата z. Написать функ­ цию, печатающую на экран:

-координаты самой удаленной точки от начала координат;

-координаты самой близкой к началу координат точки;

-среднее расстояние до начала координат.

На основании структуры создадим динамический массив, что позволит нам каждый раз задавать разное число точек. Перемен­