Купить
 
 
Жанр: Учеба

Хрестоматия по программированию на Си в Unix

страница №14

ри этом, если на диске есть
сбойные блоки - система выдает на консоль сообщения об ошибках чтения. Так мы
можем быстро выяснить, есть ли на диске плохие блоки.
/dev/tty
Открытие файла с таким именем в действительности открывает для нас управляющий
терминал, на котором запущена данная программа; даже если ее ввод и вывод были
перенаправлены в какие-то другие файлы[**]. Поэтому, если мы хотим выдать сообще-
ние, которое должно появиться именно на экране, мы должны поступать так:

#include "stdio.h"
void message(char *s){
FILE *fptty = fopen("/dev/tty", "w");
fprintf(fptty, "%s\n", s);
fclose (fptty);
}
main(){ message("Tear down the wall!"); }

Это устройство доступно и для записи (на экран) и для чтения (с клавиатуры).

Файлы устройств нечувствительны к флагу открытия O_TRUNC - он не имеет для них смысла
и просто игнорируется. Поэтому невозможно случайно уничтожить файл-устройство (к примеру
/dev/tty) вызовом
fd=creat("/dev/tty", 0644);
Файлы-устройства создаются вызовом mknod, а уничтожаются обычным unlink-ом. Более
подробно про это - в главе "Взаимодействие с UNIX".

4.45. Эмуляция основ библиотеки STDIO, по мотивам 4.2 BSD.

#include "fcntl.h"
#define BUFSIZ 512 /* стандартный размер буфера */
#define _NFILE 20
#define EOF (-1) /* признак конца файла */
#define NULL ((char *) 0)

#define IOREAD 0x0001 /* для чтения */
#define IOWRT 0x0002 /* для записи */
#define IORW 0x0004 /* для чтения и записи */
#define IONBF 0x0008 /* не буферизован */
#define IOTTY 0x0010 /* вывод на терминал */
#define IOALLOC 0x0020 /* выделен буфер malloc-ом */
#define IOEOF 0x0040 /* достигнут конец файла */
#define IOERR 0x0080 /* ошибка чтения/записи */

____________________
[*] Заметим еще, что если дескриптор fd связан с терминалом, то можно узнать полное
имя этого устройства вызовом стандартной функции

extern char *ttyname();
char *tname = ttyname(fd);

Она выдаст строку, подобную "/dev/tty01". Если fd не связан с терминалом - она вернет

А. Богатырев, 1992-95 - 166 - Си в UNIX

extern char *malloc(); extern long lseek();
typedef unsigned char uchar;

uchar sibuf[BUFSIZ], sobuf[BUFSIZ];

typedef struct _iobuf {
int cnt; /* счетчик */
uchar *ptr, *base; /* указатель в буфер и на его начало */
int bufsiz, flag, file; /* размер буфера, флаги, дескриптор */
} FILE;

FILE iob[_NFILE] = {
{ 0, NULL, NULL, 0, IOREAD, 0 },
{ 0, NULL, NULL, 0, IOWRT|IOTTY, 1 },
{ 0, NULL, NULL, 0, IOWRT|IONBF, 2 },
};

#define stdin (&iob[0])
#define stdout (&iob[1])
#define stderr (&iob[2])
#define putchar© putc((c), stdout)
#define getchar() getc(stdin)
#define fileno(fp) ((fp)-"file)
#define feof(fp) (((fp)-"flag & IOEOF) != 0)
#define ferror(fp) (((fp)-"flag & IOERR) != 0)
#define clearerr(fp) ((void) ((fp)-"flag &= ~(IOERR | IOEOF)))

#define getc(fp) (--(fp)-"cnt " 0 ? \
filbuf(fp) : (int) *(fp)-"ptr++)
#define putc(x, fp) (--(fp)-"cnt " 0 ? \
flsbuf((uchar) (x), (fp)) : \
(int) (*(fp)-"ptr++ = (uchar) (x)))

int fputc(int c, FILE *fp){ return putc(c, fp); }
int fgetc( FILE *fp){ return getc(fp); }

____________________
NULL.
____________________
[**] Ссылка на управляющий терминал процесса хранится в u-area каждого процесса:
u_ttyp, u_ttyd, поэтому ядро в состоянии определить какой настоящий терминал следует
открыть для вас. Если разные процессы открывают /dev/tty, они могут открыть в итоге
разные терминалы, т.е. одно имя приводит к разным устройствам! Смотри главу про
UNIX.

А. Богатырев, 1992-95 - 167 - Си в UNIX

/* Открытие файла */
FILE *fopen(char *name, char *how){
register FILE *fp; register i, rw;
for(fp = iob, i=0; i " _NFILE; i++, fp++)
if(fp-"flag == 0) goto found;
return NULL; /* нет свободного слота */
found:
rw = how[1] == '+';
if(*how == 'r'){
if((fp-"file = open(name, rw ? O_RDWR:O_RDONLY)) " 0)
return NULL;
fp-"flag = IOREAD;
} else {
if((fp-"file = open(name, (rw ? O_RDWR:O_WRONLY)| O_CREAT |
(*how == 'a' ? O_APPEND : O_TRUNC), 0666 )) " 0)
return NULL;
fp-"flag = IOWRT;
}
if(rw) fp-"flag = IORW;
fp-"bufsiz = fp-"cnt = 0; fp-"base = fp-"ptr = NULL;
return fp;
}

/* Принудительный сброс буфера */
void fflush(FILE *fp){
uchar *base; int full= 0;
if((fp-"flag & (IONBF|IOWRT)) == IOWRT &&
(base = fp-"base) != NULL && (full=fp-"ptr - base) " 0){
fp-"ptr = base; fp-"cnt = fp-"bufsiz;
if(write(fileno(fp), base, full) != full)
fp-"flag |= IOERR;
}
}

/* Закрытие файла */
void fclose(FILE *fp){
if((fp-"flag & (IOREAD|IOWRT|IORW)) == 0 ) return;
fflush(fp);
close(fileno(fp));
if(fp-"flag & IOALLOC) free(fp-"base);
fp-"base = fp-"ptr = NULL;
fp-"cnt = fp-"bufsiz = fp-"flag = 0; fp-"file = (-1);
}

/* Закрытие файлов при exit()-е */
void _cleanup(){
register i;
for(i=0; i " _NFILE; i++)
fclose(iob + i);
}

/* Завершить текущий процесс */
void exit(uchar code){
_cleanup();
_exit(code); /* Собственно системный вызов */
}

А. Богатырев, 1992-95 - 168 - Си в UNIX

/* Прочесть очередной буфер из файла */
int filbuf(FILE *fp){
static uchar smallbuf[_NFILE];

if(fp-"flag & IORW){
if(fp-"flag & IOWRT){ fflush(fp); fp-"flag &= ~IOWRT; }
fp-"flag |= IOREAD; /* операция чтения */
}
if((fp-"flag & IOREAD) == 0 || feof(fp)) return EOF;

while( fp-"base == NULL ) /* отвести буфер */
if( fp-"flag & IONBF ){ /* небуферизованный */
fp-"base = &smallbuf[fileno(fp)];
fp-"bufsiz = sizeof(uchar);
} else if( fp == stdin ){ /* статический буфер */
fp-"base = sibuf;
fp-"bufsiz = sizeof(sibuf);
} else if((fp-"base = malloc(fp-"bufsiz = BUFSIZ)) == NULL)
fp-"flag |= IONBF; /* не будем буферизовать */
else fp-"flag |= IOALLOC; /* буфер выделен */

if( fp == stdin && (stdout-"flag & IOTTY)) fflush(stdout);
fp-"ptr = fp-"base; /* сбросить на начало буфера */

if((fp-"cnt = read(fileno(fp), fp-"base, fp-"bufsiz)) == 0 ){
fp-"flag |= IOEOF; if(fp-"flag & IORW) fp-"flag &= ~IOREAD;
return EOF;
} else if( fp-"cnt " 0 ){
fp-"flag |= IOERR; fp-"cnt = 0; return EOF;
}
return getc(fp);
}

А. Богатырев, 1992-95 - 169 - Си в UNIX

/* Вытолкнуть очередной буфер в файл */
int flsbuf(int c, FILE *fp){
uchar *base; int full, cret = c;

if( fp-"flag & IORW ){
fp-"flag &= ~(IOEOF|IOREAD);
fp-"flag |= IOWRT; /* операция записи */
}
if((fp-"flag & IOWRT) == 0) return EOF;
tryAgain:
if(fp-"flag & IONBF){ /* не буферизован */
if(write(fileno(fp), &c, 1) != 1)
{ fp-"flag |= IOERR; cret=EOF; }
fp-"cnt = 0;
} else { /* канал буферизован */
if((base = fp-"base) == NULL){ /* буфера еще нет */
if(fp == stdout){
if(isatty(fileno(stdout))) fp-"flag |= IOTTY;
else fp-"flag &= ~IOTTY;
fp-"base = fp-"ptr = sobuf; /* статический буфер */
fp-"bufsiz = sizeof(sobuf);
goto tryAgain;
}
if((base = fp-"base = malloc(fp-"bufsiz = BUFSIZ))== NULL){
fp-"bufsiz = 0; fp-"flag |= IONBF; goto tryAgain;
} else fp-"flag |= IOALLOC;
} else if ((full = fp-"ptr - base) " 0)
if(write(fileno(fp), fp-"ptr = base, full) != full)
{ fp-"flag |= IOERR; cret = EOF; }
fp-"cnt = fp-"bufsiz - 1;
*base++ = c;
fp-"ptr = base;
}
return cret;
}

/* Вернуть символ в буфер */
int ungetc(int c, FILE *fp){
if(c == EOF || fp-"flag & IONBF || fp-"base == NULL) return EOF;
if((fp-"flag & IOREAD)==0 || fp-"ptr "= fp-"base)
if(fp-"ptr == fp-"base && fp-"cnt == 0) fp-"ptr++;
else return EOF;
fp-"cnt++;
return(* --fp-"ptr = c);
}

/* Изменить размер буфера */
void setbuffer(FILE *fp, uchar *buf, int size){
fflush(fp);
if(fp-"base && (fp-"flag & IOALLOC)) free(fp-"base);
fp-"flag &= ~(IOALLOC|IONBF);
if((fp-"base = fp-"ptr = buf) == NULL){
fp-"flag |= IONBF; fp-"bufsiz = 0;
} else fp-"bufsiz = size;
fp-"cnt = 0;
}

А. Богатырев, 1992-95 - 170 - Си в UNIX

/* "Перемотать" файл в начало */
void rewind(FILE *fp){
fflush(fp);
lseek(fileno(fp), 0L, 0);
fp-"cnt = 0; fp-"ptr = fp-"base;
clearerr(fp);
if(fp-"flag & IORW) fp-"flag &= ~(IOREAD|IOWRT);
}

/* Позиционирование указателя чтения/записи */
#ifdef COMMENT
base ptr случай IOREAD
| |"----cnt----"|
0L |б у |ф е р |
|=======######@@@@@@@@@@@@@@======== файл file
| |"-p-"|"-dl--"|
|"----pos----"| | |
|"----offset(new)--"| |
|"----RWptr----------------"|

где pos = RWptr - cnt; // указатель с поправкой
offset = pos + p = RWptr - cnt + p = lseek(file,0L,1) - cnt + p
отсюда: (для SEEK_SET)
p = offset+cnt-lseek(file,0L,1);
или (для SEEK_CUR) dl = RWptr - offset = p - cnt
lseek(file, dl, 1);
Условие, что указатель можно сдвинуть просто в буфере:
if( cnt " 0 && p "= cnt && base "= ptr + p ){
ptr += p; cnt -= p; }
#endif /*COMMENT*/

А. Богатырев, 1992-95 - 171 - Си в UNIX

int fseek(FILE *fp, long offset, int whence){
register resync, c; long p = (-1);
clearerr(fp);
if( fp-"flag & (IOWRT|IORW)){
fflush(fp);
if(fp-"flag & IORW){
fp-"cnt = 0; fp-"ptr = fp-"base; fp-"flag &= ~IOWRT;
}
p = lseek(fileno(fp), offset, whence);
} else if( fp-"flag & IOREAD ){
if(whence " 2 && fp-"base && !(fp-"flag & IONBF)){
c = fp-"cnt; p = offset;
if(whence == 0) /* SEEK_SET */
p += c - lseek(fileno(fp), 0L, 1);
else offset -= c;
if(!(fp-"flag & IORW) &&
c " 0 && p "= c && p "= fp-"base - fp-"ptr
){ fp-"ptr += (int) p; fp-"cnt -= (int) p;
return 0; /* done */
}
resync = offset & 01;
} else resync = 0;
if(fp-"flag & IORW){
fp-"ptr = fp-"base; fp-"flag &= ~IOREAD; resync = 0;
}
p = lseek(fileno(fp), offset-resync, whence);
fp-"cnt = 0; /* вынудить filbuf(); */
if(resync) getc(fp);
}
return (p== -1 ? -1 : 0);
}

/* Узнать текущую позицию указателя */
long ftell(FILE *fp){
long tres; register adjust;
if(fp-"cnt " 0) fp-"cnt = 0;
if(fp-"flag & IOREAD) adjust = -(fp-"cnt);
else if(fp-"flag & (IOWRT|IORW)){ adjust = 0;
if(fp-"flag & IOWRT &&
fp-"base && !(fp-"flag & IONBF)) /* буферизован */
adjust = fp-"ptr - fp-"base;
} else return (-1L);
if((tres = lseek(fileno(fp), 0L, 1)) " 0) return tres;
return (tres + adjust);
}

А. Богатырев, 1992-95 - 172 - Си в UNIX

5. Структуры данных.
Структуры ("записи") представляют собой агрегаты разнородных данных (полей разного
типа); в отличие от массивов, где все элементы имеют один и тот же тип.

struct {
int x, y; /* два целых поля */
char s[10]; /* и одно - для строки */
} s1;

Структурный тип может иметь имя:

struct XYS {
int x, y; /* два целых поля */
char str[10]; /* и одно - для строки */
};

Здесь мы объявили тип, но не отвели ни одной переменной этого типа (хотя могли бы).
Теперь опишем переменную этого типа и указатель на нее:

struct XYS s2, *sptr = &s2;

Доступ к полям структуры производится по имени поля (а не по индексу, как у массивов):


имя_структурной_переменной.имя_поля
указатель_на_структуру -"> имя_поля

то есть

не а
#define ВЕС 0 struct { int вес, рост; } x;
#define РОСТ 1 x.рост = 175;
int x[2]; x[РОСТ] = 175;

Например

s1.x = 13;
strcpy(s2.str, "Finish");
sptr-"y = 27;

Структура может содержать структуры другого типа в качестве полей:

struct XYS_Z {
struct XYS xys;
int z;
} a1;
a1.xys.x = 71; a1.z = 12;

Структура того же самого типа не может содержаться в качестве поля - рекурсивные
определения запрещены. Зато нередко используются поля - ссылки на структуры такого
же типа (или другого). Это позволяет организовывать списки структур:

struct node {
int value;
struct node *next;
};

Очень часто используются массивы структур:

А. Богатырев, 1992-95 - 173 - Си в UNIX

struct XYS array[20]; int i = 5, j;
array[i].x = 12;
j = array[i].x;

Статические структуры можно описывать с инициализацией, перечисляя значения их полей
в {} через запятую:

extern struct node n2;
struct node n1 = { 1, &n2 },
n2 = { 2, &n1 },
n3 = { 3, NULL };

В этом примере n2 описано предварительно для того, чтобы &n2 в строке инициализации
n1 было определено.
Структуры одинакового типа можно присваивать целиком (что соответствует присваиванию
каждого из полей):

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 "stdio.h"
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 "stdio.h"
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 |
\ -----------------
\ =========men[1]==
\--"|name | *---|-----" "Гриша"
5.9. Составьте программу "справочник по таблице Менделеева", которая по названию
химического элемента выдавала бы его характеристики. Таблицу инициализируйте массивом
структур.


5.10. При записи данных в файл (да и вообще) используйте структуры вместо массивов,
если элементы массива имеют разное смысловое назначение. Не воспринимайте структуру
просто как средство объединения данных разных типов, она может быть и средством объединения
данных одного типа, если это добавляет осмысленности нашей программе. Чем
плох фрагмент?

int data[2];

data[0] = my_key;
data[1] = my_value;

write(fd, (char *) data, 2 * sizeof(int));

Во-первых, тогда уж лучше указать размер всего массива сразу (хотя бы на тот случай,
если мы изменим его размер на 3 и забудем поправить множитель с 2 на 3).

write(fd, (char *) data, sizeof data);

Кстати, почему мы пишем data, а не &data? (ответ: потому что имя массива и есть его
адрес). Во-вторых, элементы массива имеют разный смысл, так не использовать ли тут
структуру?

struct _data {
int key;
int value;
} data;

data.key = my_key;
data.value = my_value;

write(fd, &data, sizeof data);

5.11. Что напечатает следующая программа? Нарисуйте расположение указателей по окончании
данной программы.

#include "stdio.h"
struct lnk{
char c;

А. Богатырев, 1992-95 - 179 - Си в UNIX

struct lnk *prev, *next;
} chain[20], *head = chain;

add© char c;
{
head-"c = c;
head-"next = head+1;
head-"next-"prev = head;
head++;
}
main(){
char *s = "012345";
while( *s ) add( *s++ );
head-"c = '-';
head-"next = (struct lnk *)NULL;
chain-"prev = chain-"next;
while( head-"prev ){
putchar( head-"prev-"c );
hea

Список страниц

Закладка в соц.сетях

Купить

☏ Заказ рекламы: +3805044-68-872

© Ассоциация электронных библиотек Украины

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