ванию каждого из полей):
struct XYS s1, s2; ...
s2 = s1;
в отличие от массивов, которые присваивать целиком нельзя:
int a[5], b[5]; a = b; /* ОШИБОЧНО ! */
Пример обращения к полям структуры:
typedef struct _Point {
short x, y; /* координаты точки */
char *s; /* метка точки */
} Point;
Point p; Point *pptr; short *iptr;
struct _Curve {
Point points[25]; /* вершины ломанной */
int color; /* цвет линии */
} aLine[10], *linePtr = & aLine[0];
...
pptr = &p; /* указатель на структуру p */
p.x = 1; p.y = 2; p.s = "Grue";
linePtr->points[2].x = 54; aLine[5].points[0].y = 17;
В ы р а ж е н и е значение
---------+------------+------------+-----------+-----------
p.x | pptr->x | (*pptr).x | (&p)->x | 1
---------+------------+------------+-----------+-----------
&p->x | ошибка
-----------+----------------+------------------+-----------
iptr= &p.x | iptr= &pptr->x | iptr= &(pptr->x) | адрес поля
-----------+----------------+--------+---------+-----------
*pptr->s | *(pptr->s) | *p.s | p.s[0] | 'G'
-----------+----------------+--------+---------+-----------
pptr->s[1] | (&p)->s[1] | p.s[1] | 'r'
-----------+----------------+------------------+-----------
&p->s[1] | ошибка
-----------+----------------+------------------+-----------
(*pptr).s | pptr->s | p.s | "Grue"
-----------+----------------+------------------+-----------
*pptr.s | ошибка
-----------------------------------------------+-----------
А. Богатырев, 1992-95 - 174 - Си в UNIX
Вообще (&p)->field = p.field
pptr->field = (*pptr).field
Объединения - это агрегаты данных, которые могут хранить в себе значения данных
разных типов на одном и том же месте.
struct a{ int x, y; char *s; } A;
union b{ int i; char *s; struct a aa; } B;
Структура:
________________________
A: | A.x int | Три поля
------------------------ расположены подряд.
| A.y int | Получается как бы
------------------------ "карточка" с графами.
| A.s char * |
------------------------
А у объединений поля расположены "параллельно",
на одном месте в памяти.
_______________________________________________________
B: | B.i int | B.s char * | B.aa : B.aa.x int |
-----------| | struct a : B.aa.y int |
---------------| : B.aa.s char * |
|___________________________|
Это как бы "ящик" в который можно поместить значение любого типа из перечисленных, но
не ВСЕ ВМЕСТЕ ("и то и это", как у структур), а ПО ОЧЕРЕДИ ("или/или"). Размер его
достаточно велик, чтоб вместить самый большой из перечисленных типов данных.
Мы можем занести в union значение и интерпретировать его как другой тип данных -
это иногда используется в машинно-зависимых программах. Вот пример, выясняющий поря-
док байтов в short числах:
union lb {
char s[2]; short i;
} x;
unsigned hi, lo;
x.i = (02 << 8) | 01;
hi = x.s[1]; lo = x.s[0];
printf( "%d %d\n", hi, lo);
или так:
#include
union {
int i;
unsigned char s[sizeof(int)];
} u;
void main(){
unsigned char *p;
int n;
u.i = 0x12345678;
for(n=0, p=u.s; n < sizeof(int); n++, p++){
printf("%02X ", *p);
}
putchar('\n');
}
А. Богатырев, 1992-95 - 175 - Си в UNIX
или порядок слов в long числах:
union xx {
long l;
struct ab {
short a; /* low word */
short b; /* high word */
} ab;
} c;
main(){ /* На IBM PC 80386 печатает 00020001 */
c.ab.a = 1; c.ab.b = 2; printf("%08lx\n", c.l );
}
5.1. Найдите ошибки в описании структурного шаблона:
structure { int arr[12],
char string,
int *sum
}
5.2. Разработайте структурный шаблон, который содержал бы название месяца, трехбук-
венную аббревиатуру месяца, количество дней в месяце и номер месяца. Инициализируйте
его для невисокосного года.
struct month {
char name[10]; /* или char *name; */
char abbrev[4]; /* или char *abbrev; */
int days;
int num;
};
struct month months[12] = { /* индекс */
{"Январь" , "Янв", 31, 1 }, /* 0 */
{"Февраль", "Фев", 28, 2 }, /* 1 */
...
{"Декабрь", "Дек", 31, 12}, /* 11 */
}, *mptr = & months[0]; /* или *mptr = months */
main(){
struct month *mptr;
printf( "%s\n", mptr[1].name );
printf( "%s %d\n", mptr->name, mptr->num );
}
Напишите функцию, сохраняющую массив months в файл; функцию, считывающую его из
файла. Используйте fprintf и fscanf.
В чем будет разница в функции чтения, когда поле name описано как char name[10]
и как char *name?
Ответ: во втором случае для сохранения прочитанной строки надо заказывать память
динамически при помощи malloc() и сохранять в ней строку при помощи strcpy(), т.к.
память для хранения самой строки в структуре не зарезервирована (а только для указа-
теля на нее).
Найдите ошибку в операторах функции main(). Почему печатается не "Февраль", а
какой-то мусор? Указание: куда указывает указатель mptr, описанный в main()? Ответ: в
"неизвестно куда" - это локальная переменная (причем не получившая начального значе-
ния - в ней содержится мусор), а не то же самое, что указатель mptr, описанный выше!
Уберите описание mptr из main.
А. Богатырев, 1992-95 - 176 - Си в UNIX
Заметим, что для распечатки всех или нескольких полей структуры следует ЯВНО
перечислить в printf() все нужные поля и указать форматы, соответствующие типам этих
полей. Не существует формата или стандартной функции, позволяющей распечатать все
поля сразу (однако такая функция может быть написана вами для конкретного типа струк-
тур). Также не существует формата для scanf(), который вводил бы структуру целиком.
Вводить можно только по частям - каждое поле отдельно.
5.3. Напишите программу, которая по номеру месяца возвращает общее число дней года
вплоть до этого месяца.
5.4. Переделайте предыдущую программу таким образом, чтобы она по написанному бук-
вами названию месяца возвращала общее число дней года вплоть до этого месяца. В прог-
рамме используйте функцию strcmp().
5.5. Переделайте предыдущую программу таким образом, чтобы она запрашивала у пользо-
вателя день, месяц, год и выдавала общее количество дней в году вплоть до данного
дня. Месяц может обозначаться номером, названием месяца или его аббревиатурой.
5.6. Составьте структуру для учетной картотеки служащего, которая содержала бы сле-
дующие сведения: фамилию, имя, отчество; год рождения; домашний адрес; место работы,
должность; зарплату; дату поступления на работу.
5.7. Что печатает программа?
struct man {
char name[20];
int salary;
} workers[] = {
{ "Иванов", 200 },
{ "Петров", 180 },
{ "Сидоров", 150 }
}, *wptr, chief = { "начальник", 550 };
main(){
struct man *ptr, *cptr, save;
ptr = wptr = workers + 1;
cptr = &chief;
save = workers[2]; workers[2] = *wptr; *wptr = save;
wptr++; ptr--; ptr->salary = save.salary;
printf( "%c %s %s %s %s\n%d %d %d %d\n%d %d %c\n",
*workers[1].name, workers[2].name, cptr->name,
ptr[1].name, save.name,
wptr->salary, chief.salary,
(*ptr).salary, workers->salary,
wptr - ptr, wptr - workers, *ptr->name );
}
Ответ:
С Петров начальник Сидоров Сидоров
180 550 150 150
2 2 И
5.8. Разберите следующий пример:
#include
struct man{
А. Богатырев, 1992-95 - 177 - Си в UNIX
char *name, town[4]; int salary;
int addr[2];
} men[] = {
{ "Вася", "Msc", 100, { 12, 7 } },
{ "Гриша", "Len", 120, { 6, 51 } },
{ "Петя", "Rig", 140, { 23, 84 } },
{ NULL, "" , -1, { -1, -1 } }
};
main(){
struct man *ptr, **ptrptr;
int i;
ptrptr = &ptr;
*ptrptr = &men[1]; /* men+1 */
printf( "%s %d %s %d %c\n",
ptr->name,
ptr->salary,
ptr->town,
ptr->addr[1],
ptr[1].town[2] );
(*ptrptr)++;
/* копируем *ptr в men[0] */
men[0].name = ptr->name; /* (char *) #1 */
strcpy( men[0].town, ptr->town ); /* char [] #2 */
men[0].salary = ptr->salary; /* int #3 */
for( i=0; i < 2; i++ )
men[0].addr[i] = ptr->addr[i]; /* массив #4 */
/* распечатываем массив структур */
for(ptr=men; ptr->name; ptr++ )
printf( "%s %s %d\n",
ptr->name, ptr->town, ptr->addr[0]);
}
Обратите внимание на такие моменты:
1) Как производится работа с указателем на указатель (ptrptr).
2) При копировании структур отдельными полями, поля скалярных типов (int, char,
long, ..., указатели) копируются операцией присваивания (см. строки с пометками
#1 и #3). Поля векторных типов (массивы) копируются при помощи цикла, поэле-
ментно пересылающего массив (строка #4). Строки (массивы букв) пересылаются
стандартной функцией strcpy (строка #2). Все это относится не только к полям
структур, но и к переменным таких типов. Структуры можно также копировать не по
полям, а целиком: men[0]= *ptr;
3) Запись аргументов функции printf() лесенкой позволяет лучше видеть, какому фор-
мату соответствует каждый аргумент.
4) При распечатке массива структур мы печатаем не определенное их количество (рав-
ное размеру массива), а пользуемся указателем NULL в поле name последней струк-
туры как признаком конца массива.
5) В поле town мы храним строки из 3х букв, однако выделяем для хранения массив из
4х байт. Это необходимо потому, что строка "Msc" состоит не из 3х, а из 4х бай-
тов: 'M','s','c','\0'.
При работе со структурами и указателями большую помощь могут оказать рисунки. Вот как
(например) можно нарисовать данные из этого примера (массив men изображен не весь):
А. Богатырев, 1992-95 - 178 - Си в UNIX
--ptr-- --ptrptr--
ptr | * |<------|---* |
---|--- ----------
|
/ =========men[0]==
/ men:|name | *---|-----> "Вася"
| |---------------|
| |town |M|s|c|\0|
| |---------------|
| |salary| 100 |
| |---------------|
| |addr | 12 | 7 |
\ -----------------