Язык программирования Си. Глава(Chapter) 16
Published: 2022-06-24 18:00:42

1. Этапы компиляции: трансляция — компилятор(знает правила языка) проводит проверку символов на соответствие(<:, :>, ?:), удаляет косые черты с последующим символом новой строки(Enter) с преобразованием в логическую строку(logical line), так как препроцессор работает с одной логической строкой, но не с несколькими физическими, разбиение на лексемы(комментарий заменяется на один пробел), предварительная обработка — препроцессор(не знает правила языка и не проводит вычисления) ищет свои директивы, начинающиеся с символа #, далее вычисление компилятором константных значений и конкатенация строк

2. Директивы препроцессора: длина ограничена одной логической строкой, могут предваряться символами пробела или табуляции в ANSI C, но не другими символами

3. Символические константы(Symbolic Constants) обычно записываются прописными буквами с помощью #define и const: увеличивают изменяемость(#define CAPACITY 20), удобочитаемость(#define BUFSIZE 512) и переносимость(#define EOF '^Z' или #define EOF '^D')

4. `#define` — директива для создания символических(symbolic) или именованных(manifest) констант, иначе объектных(object-like macros) макросов, и вдобавок функциональных макросов(function-like macros), правила именования  идентификатора макросов(как у переменных), список замены(replacement list) или тело(body) является строкой лексем(tokens), расширение(expansion) макроса(то есть подстановка тела), вложенность(nesting) макросов(#define MALLOCINT10 (int *)malloc(sizeof(int) * SIZE)) и переопределение(redefining) констант(разрешено в ANSI C, если константы совпадают(одинаковые лексемы, где лексема(token) — "слово" в теле определения макроса): `#define SIX 2 * 3` и `#define SIX 2   *   3`, но не `#define SIX 2*3`(одна лексема))

5. const — квалификатор типа позволяет создавать глобальные или локальные константы любых данных(структура, массив), в C(но не в C++) не являются константными выражениями, а только неизменяемыми(`const int size = 4; int arr[size];` где `arr` — не автоматический массив, а VLA)

6. Функциональные макросы(идентификатор, аргумент и тело) или макрос с аргументами(подобны обычным функциям): (желательно нежелательно использовать инкремент или декремент в аргументе макроса) и ещё создание(объединение) строк из аргументов макроса с помощью `#`(`#define PSQR(X) printf("Квадрат " #Х " равен %d.\n", ((Х)*(Х)));`), средство слияния(объединения лексем) препроцессора(Preprocessor Glue) с помощью `##`(`#define XNAME(n) int х ## n` и `#define PRINT_XN(n) printf("х" #n " = %d\n", х ## n);`) и макросы(в C99) с переменным числом аргументов(Variadic Macros) используя ...(последний аргумент) и __VA_ARGS__(`#define PR(X, ...) printf("Message " #X ": " __VA_ARGS__)`)

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

8. `#include`(<> — в UNIX поиск в системных каталогах, "" — изначальный поиск в текущем рабочем каталоге, в каталоге исходного файла или в каталоге проекта(зависит от компилятора), но в ANSI C нет разницы между <> и ""): директива ищет файл с указанным именем(#include "\Desktop\Project\header.h") и включает(не вставка и не копирование) его содержимое(для компилятора) в исходный файл

9. Другие директивы(Directives): `#undef` — отменяет определение  константы `#define`(`#define LIMIT 400` или `#define LIMIT` и `#undef LIMIT`, теперь `LIMIT` не определён); `#ifdef`, `#else`, `#endif` — для условной компиляции; `#ifndef`(инверсия `#ifdef`) и `#define` — обычно для предотвращения многократных определений одного макроса или файла(в которых есть определения структур)(есть в стандартных заголовочных файлах #ifndef _STDIO_H, #define _STDIO_H и #endif); #if, #else и #elif — препроцессорный аналог if и else(оперируют с логическими выражениями) и взаимозаменяемость #if defined (IBMPC) и #ifdef IBMPC; #line — переустановка текущего номера строки(__LINE__ с #line 1000) и имени файла(__LINE__ и __FILE__ с #line 10 "add.c"); #error — вывод сообщения ошибки с остановкой компиляции(#error System is not defined!); #pragma и аналог _Pragma(в C99) — для указания инструкций компилятору(#pragrna с9х on или _Pragma("C9X ON");) вместо аргумента командной строки(gcc -std=c11 main.c) или меню IDE

10. Предопределённые идентификаторы или макросы(Predefined Identifiers or Macros): `__TIME__`(строка "чч:мм:сс"), __DATE__(строка "Ммм дд гггг"), __FILE__(строка имени исходного файла), __LINE__(номер текущей строки исходного файла), __func__(строка "имя текущей функции"),  __STDC__(1 если реализация соответствует стандарту C), __STDC_HOSTED__, __STDC_VERSION__(199901L для C99 и 201112L для С11)

11. Выражения обобщённого выбора(Generic Selection Expression) или обобщённое программирование(Generic Programming) в C11(в C++ реализован как шаблон template<>): _Generic(обычно используется с #define) — оператор обобщённого выбора(похож на switch или ручной аналог typeid в C++)

12. Встраиваемые функции в C99(не обязательно встраивается код как у макросов, но как минимум оптимизируется функция): функция должна быть короткой(для ускорения), с внутренним связыванием, определена(не только объявлена) до первого вызова и находиться в том же файле, где она применяется(inline static void fun(){ ... }), также нельзя получить адрес, может быть не видна в отладчике, обычно определяется в заголовочном файле, также спецификатор функции в C11_Noreturn или noreturn(в stdnoreturn.h)(подобно inline, но не static(спецификатор класса хранения)) — не возвращает управление вызывающей функции(например exit())

13. Библиотека С(The C Library): получение доступа — автоматическое(с IDE), включение(inclusion) файлов и библиотек(сообщить системе, где искать код функций) с #include; математические функции в math.h — exp(), log(), log10(), pow(), sqrt(), cbrt(), ceil(), fabs(), floor(), atan(), atan2() для double и их версии для float(sqrtf()) и long double(sinl()); tgmath.h в C99 с определением обобщённых макросов для всех типов, включая complex(#define sqrt(X) _Generic((X), long double: sqrtl, default: sqrt, float: sqrtf, csqrtf, csqrt, csqrtl)(X))

14. Библиотека утилит общего назначения(The General Utilities Library) stdlib.h:  exit() — вызывается в любом случае при завершении main(), сбрасывает и закрывает все потоки и временные файлы от tmpfile(), передаёт управление и состояние(код ошибки) в ОС и atexit() — для регистрации не менее 32 функций(в ANSI C), предназначенных для вызова начиная с последней функции в списке при выполнении exit(); также qsort() — сортировка массивов с прототипом void qsort (void * base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));, где base — указатель на массив, nmemb — количество элементов, size — размер элемента, compar — указатель на функцию сравнения(подобно strcmp())

15. Библиотека утверждений(The Assert Library) assert.h: assert()(аналог выполняемого(run time) if() { puts(); abort(); }) — принимает целочисленное выражение, если оно ложно, то обычно выводит в stderr выражение, имя файла с номером строки и вызывает abort()(в stdlib.h), также макрос(#define NDEBUG) перед включением assert.h для отключения всех assert(), а ещё _Static_assert или static_assert(в assert.h)(аналог компилируемого(compile time) #if, #error и #endif) в C11 — принимает целочисленную константу и строку

16. Функции в string.h: memmove()(с прототипом void * memmove(void * destination, const void * source, size_t n);) — для побайтового(не поэлементного, то есть на зависящего от типа) копирования данных(перекрытие данных допустимо) и memcpy() — аргументы с restrict предполагают отсутствие перекрытия(overlapping) данных

17. Переменное число аргументов(Variable Arguments) функций в stdarg.h: при указании последнего из как минимум двух параметров функции троеточием(...), где предшествующий троеточию параметр(parmN) — количество аргументов, также создание переменной типа va_list в функции и его инициализация макросом va_start(), последовательный с удалением(при первом вызове первый аргумент и т.д.) доступ к списку аргументов с указанием типа через va_arg()(без преобразования типов), va_copy() — для копирования объектов типа va_list и макрос va_end()(подобно free()) — для очистки памяти

```c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void rip() { puts("Rest in Peace."); }
void _Noreturn static _Noreturn inline inline dead(int bullet) { atexit(atexit(rip)); exit(bullet); }
void invitation() { puts("Try once more."); }
void alive() { puts("Unfortunately, you are alive."); }

int main(void)
{
    srand(time(0));
    puts("Welcome to the Russian Roulette!");
    if (rand() % 6 == 0) dead(1);
    atexit(atexit(invitation));
    atexit(alive);
    puts("Have got into a frenzy?");
    return 0;
}
```

```c
#include <stdio.h>
#include <tgmath.h>
#include <string.h>

#define DIGITS 20

int main(void)
{
    char num1[DIGITS];
    sprintf(num1, "%.16f", sqrt(2.F));
    char num2[DIGITS];
    sprintf(num2, "%.16f", (sqrt)(02.f));

    //√2 == 1,414213562373095...
    printf("%s %s %s\n", num1, strcmp(num1, num2) == 0 ? "==" : "!=",  num2);
}
```

```c
#include <stdio.h>

#define TYPENAME(X) _Generic((X), char: "char", short: "short", int: "int", long: "long", long long: "long long", float: "float", double: "double", long double: "long double", default: "other")

#define PRINT_TYPENAME(X) printf(#X "\tis %s\n", TYPENAME(X))

int main(void)
{
    PRINT_TYPENAME('A');
    PRINT_TYPENAME(*"");
    PRINT_TYPENAME(03L + -1UL);
    PRINT_TYPENAME(+4ULL * 3.0f);
    PRINT_TYPENAME((short)5 + (short)1);
}
```

```c
#include <stdio.h>

#undef __DATE__
#ifndef __DATE__
    #define LINE
#endif

#if defined (__LINE__)
    #define LINE __LINE__
    #if __STDC_VERSION__ == 199901L
        #line 11
    #elif __STDC_VERSION__ == 201112
        #line 13 "main.c"
    #else
        #error Required C99 or higher
    #endif
#endif

int main(void) { printf("%d\n", LINE); }
```

```c
#include <stdio.h>

#define SQUARE1(X) X*X
#define SQUARE2(X) (X*X)
#define SQUARE3(X) (X)*(X)
#define SQUARE4(X) ((X)*(X))
int square5(int x) { return x * x; }

int main(void)
{
    int x = 2;
    int y1 = SQUARE1(x + 3);
    int y2 = 100 / SQUARE2(x);
    int y3 = 100 / SQUARE3(x + 3);
    int y4 = SQUARE4(++x);
    int y5 = square5(x++);
    printf("%d\n", x + y1 + y2 + y3 + y4 + y5);
}
```
> Язык программирования Си 6 издание. Стивен Прата
> C Primer Plus 6th edition. Stephen Prata