книги / Язык Си
..pdfunion 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. Написать функ цию, печатающую на экран:
-координаты самой удаленной точки от начала координат;
-координаты самой близкой к началу координат точки;
-среднее расстояние до начала координат.
На основании структуры создадим динамический массив, что позволит нам каждый раз задавать разное число точек. Перемен