DEV Community

Amirshokh
Amirshokh

Posted on

1 1

Язык программирования Си. Глава(Chapter) 16

  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()) — для очистки памяти

#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;
}
Enter fullscreen mode Exit fullscreen mode
#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);
}
Enter fullscreen mode Exit fullscreen mode
#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);
}
Enter fullscreen mode Exit fullscreen mode
#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); }
Enter fullscreen mode Exit fullscreen mode
#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);
}
Enter fullscreen mode Exit fullscreen mode

Язык программирования Си 6 издание. Стивен Прата
C Primer Plus 6th edition. Stephen Prata

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay