Начинаем писать свои "библиотеки" в Си
На данный момент мы изучили Си – в том плане, что можем писать программы на Си практически любой сложности. А действительно: основные операции есть; так как всё есть байт, можем хоть какие структуры создавать; да ещё и динамически.
Однако, не это самое сложное и интересное в программировании. Сам по себе язык мало что стоит. А вот "технологический стек", если хотите, или же умение обращаться с "батарейками" – вот где раскрывается любой язык!
Наше фееричное введение в наСИлие на этом этапе принимает ещё более ужасающие обороты! Переходим к "мясу" – к практике, где эклектика тулзовин и археология IT в своих соперниках имеет разве что аппаратные средства и саму физику!
Дальше идут скорее "пометки на полях" – набор пионера, чтобы выжить в джунглях программирования на Си.
Уже ни раз мы подключали функции stdio
с помощью команды препроцессора
#include
. Сам заголовочный файл можно посмотреть:
→ cat /usr/include/stdio.h
И увидеть примерно следующее. Далее будут приведены части файла:
#ifndef _STDIO_H_ #define _STDIO_H_ // ... #endif
ifndef
/ endif
– блок условного оператора макропроцессора. Код между ними
будет исполнен (не вырезан макропроцессором), если макроопределение/макрос определено.
С помощью команды define
можно определить макрос.
В данном случае конструкция, которая обрамляет весь файл – защита от повторного
определения функций и прочего при следующем include
.
Также в заголовочных файлах можно подключать другие.
#include <sys/cdefs.h> #include <Availability.h>
Определять структуры
struct __sbuf { unsigned char *_base; int _size; };
TODO: Про extern рассказывать долго, но надо
extern FILE *__stdinp; extern FILE *__stdoutp; extern FILE *__stderrp;
Объявлять "константы" – макросы, которые будут подставлены в код:
#define _IOFBF 0 /* setvbuf should set fully buffered */ #define _IOLBF 1 /* setvbuf should set line buffered */ #define _IONBF 2 /* setvbuf should set unbuffered */
Мы уже видели подобную "константу", когда читали входной поток и останавливались по EOF.
Но не останавливаемся: тут есть ещё прототипы функций, которые мы сможем использовать:
void clearerr(FILE *); int fclose(FILE *); int feof(FILE *); int ferror(FILE *); int fflush(FILE *); int fgetc(FILE *); int fgetpos(FILE * __restrict, fpos_t *); char *fgets(char * __restrict, int, FILE *);
Этот кусок кода примечателен тем, что макрос может быть не только каким-то значением, но и "функцией" – не в СИшном виде, но по смыслу:
#define __sgetc(p) (--(p)->_r < 0 ? __srget(p) : (int)(*(p)->_p++)) #define __sputc(c, p) \ (--(p)->_w < 0 ? \ (p)->_w >= (p)->_lbfsize ? \ (*(p)->_p = (c)), *(p)->_p != '\n' ? \ (int)*(p)->_p++ : \ __swbuf('\n', p) : \ __swbuf((int)(c), p) : \ (*(p)->_p = (c), (int)*(p)->_p++))
Ну и конец – ставим комменты о том, что закрываем, ибо можно запутаться:
#endif /* _STDIO_H_ */