"окошком" через которое мы обозреваем таблицу большего размера, возможно перемещая
окно над ней). Предусмотрите также случай, когда таблица оказывается заполненной не
полностью (как на рисунке).
8.26. Используя библиотеку curses, напишите программу, реализующую клеточный автомат
Конвея "Жизнь". Правила: есть прямоугольное поле (вообще говоря бесконечное, но при-
нято в конечной модели замыкать края в кольцо), в котором живут "клетки" некоторого
организма. Каждая имеет 8 соседних полей. Следующее поколение "клеток" образуется по
таким правилам:
- если "клетка" имеет 2 или 3 соседей - она выживает.
- если "клетка" имеет меньше 2 или больше 3 соседей - она погибает.
- в пустом поле, имеющем ровно 3х живых соседей, рождается новая "клетка".
Предусмотрите: редактирование поля, случайное заполнение поля, останов при смерти
всех "клеток", останов при стабилизации колонии.
8.27. При помощи curses-а напишите экранный редактор кодов доступа к файлу (в форме
rwxrwxrwx). Расширьте программу, позволяя редактировать коды доступа у группы фай-
лов, изображая имена файлов и коды доступа в виде таблицы:
НАЗВАНИЕ КОДЫ ДОСТУПА
файл1 rwxrw-r--
файл2 rw-r-xr-x
файл3 rwxrwxr--
Имена файлов задавайте как аргументы для main(). Указание: используйте для получения
текущих кодов доступа системный вызов stat(), а для их изменения - системный вызов
chmod().
А. Богатырев, 1992-95 - 391 - Си в UNIX
9. Приложения.
9.1. Таблица приоритетов операций языка C++
Операции, расположенные выше, имеют больший приоритет.
Операторы Ассоциативность
--------------------------------------------------
1. () [] -> :: . Left to right
2. ! ~ + - ++ -- & *
(typecast) sizeof new delete Right to left
3. .* ->* Left to right
4. * / % Left to right
5. + - Left to right
6. << >> Left to right
7. < <= > >= Left to right
8. == != Left to right
9. & Left to right
10. ^ Left to right
11. | Left to right
12. && Left to right
13. || Left to right
14. ?: (условное выражение) Right to left
15. = *= /= %= += -= &=
^= |= <<= >>= Right to left
16. , Left to right
Здесь "*" и "&" в строке 2 - это адресные операции; в строке 2 "+" и "-" - унарные;
"&" в строке 9 - это побитное "и"; "(typecast)" - приведение типа; "new" и "delete" -
операторы управления памятью в C++.
Ассоциативность Left to right (слева направо) означает группировку операторов
таким образом:
A1 @ A2 @ A3 это
((A1 @ A2) @ A3)
Ассоциативность Rigth to left (справа налево) это
A1 @ A2 @ A3 это
(A1 @ (A2 @ A3))
9.2. Правила преобразований типов.
9.2.1. В выражениях.
1. Если операнд имеет тип не int и не double, то сначала приводится:
signed char --> int расширением знакового бита (7)
unsigned char --> int дополнением нулями слева
short --> int расширением знакового бита (15)
unsigned short --> unsigned int дополнением нулями слева
enum --> int порядковый номер в перечислимом типе
float --> double дробная часть дополняется нулями
2. Если любой операнд имеет тип double, то и другой операнд приводится к типу dou-
ble. Результат: типа double. Запишем все дальнейшие преобразования в виде схемы:
А. Богатырев, 1992-95 - 392 - Си в UNIX
если есть то другой результат
операнд типа приводится к типу имеет тип
if(double) -->double double
else if(unsigned long) -->unsigned long unsigned long
else if(long) -->long long
else if(unsigned int) -->unsigned int unsigned int
else оба операнда имеют тип int int
При вызове функций их аргументы - тоже выражения, поэтому в них приводятся char,short
к int и float к double. Это говорит о том, что аргументы (формальные параметры) функ-
ций можно всегда объявлять как int и double вместо char,short и float соответственно.
Зато спецификатор unsigned является существенным.
9.2.2. В присваиваниях.
op = expr;
Тип выражения expr приводится к типу левой части - op. При этом возможны приведения
более "длинного" типа к более "короткому" при помощи усечения, вроде:
int --> char обрубается старший байт.
long --> int обрубается старшее слово.
float --> int отброс дробной части
double --> int и обрубание мантиссы, если не лезет.
double --> float округление дробной части.
Вот еще некоторые приведения типов:
signed --> unsigned виртуально (просто знаковый бит
unsigned --> signed считается значащим или наоборот).
unsigned int --> long добавление нулей слева.
int --> long расширение знакового бита.
float --> int преобразование внутреннего
int --> float представления: машинно зависимо.
Некоторые преобразования могут идти в несколько стадий, например:
char --> long это
char --> int --> long
char --> unsigned long это
char --> int --> unsigned long
9.3. Таблица шестнадцатеричных чисел (HEX).
%d %o %X побитно
--------------------------------
0 0 0x0 0000
1 1 0x1 0001
2 2 0x2 0010
3 3 0x3 0011
4 4 0x4 0100
5 5 0x5 0101
6 6 0x6 0110
7 7 0x7 0111
А. Богатырев, 1992-95 - 393 - Си в UNIX
--------------------------------
8 010 0x8 1000
9 011 0x9 1001
10 012 0xA 1010
11 013 0xB 1011
12 014 0xC 1100
13 015 0xD 1101
14 016 0xE 1110
15 017 0xF 1111
16 020 0x10 10000
9.4. Таблица степеней двойки.
n 2**n | n 2**n
--------------|---------------
0 1 | 8 256
1 2 | 9 512
2 4 | 10 1024
3 8 | 11 2048
4 16 | 12 4096
5 32 | 13 8192
6 64 | 14 16384
7 128 | 15 32768
| 16 65536
9.5. Двоичный код: внутреннее представление целых чисел.
Целые числа в большинстве современных компьютеров представлены в виде двоичного
кода. Пусть машинное слово состоит из 16 бит. Биты нумеруются справа налево начиная
с 0. Обозначим условно бит номер i через b[i]. Значением его может быть либо 0, либо
1.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 0| 0| 0| 0| 1| 0| 1| 1| 0| 1| 1| 0| 1| 1| 0| 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Тогда unsigned число, записанное в слове, равно
d = 2**15 * b[15] +
2**14 * b[14] +
...
2**1 * b[1] +
b[0];
(2**n - это 2 в степени n). Такое разложение числа d единственно. При сложении двух
чисел биты складываются по правилам:
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0 и перенос 1 в разряд слева
Числа со знаком интерпретируются чуть иначе. Бит b[15] считается знаковым: 0 - число
положительно или равно нулю, 1 - отрицательно. Отрицательные числа хранятся в виде
дополнительного кода:
-a = ~a + 1
Например:
А. Богатырев, 1992-95 - 394 - Си в UNIX
2 = 0000000000000010
~2 = 1111111111111101
~2+1 = 1111111111111110 = -2
-1 = 1111111111111111
-2 = 1111111111111110
-3 = 1111111111111101
-4 = 1111111111111100
-5 = 1111111111111011
Такое представление выбрано исходя из правила
a + (-a) = 0
знак|
2 = 0|000000000000010 сложим их
-2 = 1|111111111111110
---------|---------------
сумма: 10|000000000000000
Как видим, произошел перенос 1 в бит номер 16. Но слово содержит лишь биты 0..15 и
бит b[16] просто игнорируется. Получается, что сумма равна
0000000000000000 = 0
что и требовалось. В двоичном коде вычитание реализуется по схеме
a - b = a + (-b) = a + (~b + 1)
Восьмеричные числа соответствуют разбиению двоичного числа на группы по 3 бита и
записи каждой группы в виде соответствующей восьмеричной цифры (смотри таблицу выше).
Шестнадцатеричные числа соответствуют разбиению на группы по 4 бита (nibble):
x = 0010011111011001
число: 0010 0111 1101 1001
16-ричное: 0x 2 7 D 9 = 0x27D9
число: 0 010 011 111 011 001
8-ричное: 0 0 2 3 7 3 1 = 023731
10. Примеры.
В данном приложении приводится несколько содержательных и достаточно больших
примеров, которые иллюстрируют как сам язык Си, так и некоторые возможности системы
UNIX, а также некоторые программистские приемы решения задач при помощи Си. Многие
из этих примеров содержат в качестве своих частей ответы на некоторые из задач.
Некоторые примеры позаимствованы из других книг, но дополнены и исправлены. Все при-
меры проверены в действии. Смысл некоторых функций в примерах может оказаться вам
неизвестен; однако в силу того, что данная книга не является учебником, мы отсылаем
вас за подробностями к "Оперативному руководству" (man) по операционной системе UNIX
и к документации по системе.
И в заключение - несколько слов о путях развития языка "C". Чистый язык "C" уже
отстал от современных технологий программирования. Такие методы как модули (языки
"Modula-2", "Ada", "CLU"), родовые пакеты ("Ada", "CLU"), объектно-ориентированное
программирование ("Smalltalk", "CLU") требуют новых средств. Поэтому в настоящее
время "C" постепенно вытесняется более мощным и интеллектуальным языком "C++" [*],
обладающим средствами для объектно-ориентированного программирования и родовых
____________________
[*] C++ как и C разработан в AT&T; произносится "Си плас-плас"
А. Богатырев, 1992-95 - 395 - Си в UNIX
классов. Существуют также расширения стандартного "C" объектно-ориентированными воз-
можностями ("Objective-C"). Большой простор предоставляет также сборка программ из
частей, написанных на разных языках программирования (например, "C", "Pascal", "Pro-
log").
____________________
[**][**][**] Автор благодарит авторов программ и книг по Си и UNIX, по которым некогда
учился я сам; коллег из ИПК Минавтопрома/Демоса; программистов из сетей Usenet и Rel-
com, давших материалы для задач и рассуждений; слушателей курсов по Си за многочис-
ленный материал для книги.
А.Богатырев.
Ученью не один мы посвятили год,