Этапы компиляции: трансляция — компилятор(знает правила языка) проводит проверку символов на соответствие(<:, :>, ?:), удаляет косые черты с последующим символом новой строки(Enter) с преобразованием в логическую строку(logical line), так как препроцессор работает с одной логической строкой, но не с несколькими физическими, разбиение на лексемы(комментарий заменяется на один пробел), предварительная обработка — препроцессор(не знает правила языка и не проводит вычисления) ищет свои директивы, начинающиеся с символа #, далее вычисление компилятором константных значений и конкатенация строк
Директивы препроцессора: длина ограничена одной логической строкой, могут предваряться символами пробела или табуляции в ANSI C, но не другими символами
Символические константы(Symbolic Constants) обычно записываются прописными буквами с помощью #define и const: увеличивают изменяемость(#define CAPACITY 20), удобочитаемость(#define BUFSIZE 512) и переносимость(#define EOF '^Z' или #define EOF '^D')
#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
(одна лексема))const — квалификатор типа позволяет создавать глобальные или локальные константы любых данных(структура, массив), в C(но не в C++) не являются константными выражениями, а только неизменяемыми(
const int size = 4; int arr[size];
гдеarr
— не автоматический массив, а VLA)Функциональные макросы(идентификатор, аргумент и тело) или макрос с аргументами(подобны обычным функциям): (желательно нежелательно использовать инкремент или декремент в аргументе макроса) и ещё создание(объединение) строк из аргументов макроса с помощью
#
(#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__)
)Взаимозаменяемость(не всегда корректная) макросов(используется обычно с простыми действиями(возведение в квадрат, модуль), но вставкой кода увеличивается размер исходного файла, а выполнение быстрое) и функций(вызов функции занимает время, но код не копируется лишний раз)
#include
(<> — в UNIX поиск в системных каталогах, "" — изначальный поиск в текущем рабочем каталоге, в каталоге исходного файла или в каталоге проекта(зависит от компилятора), но в ANSI C нет разницы между <> и ""): директива ищет файл с указанным именем(#include "\Desktop\Project\header.h") и включает(не вставка и не копирование) его содержимое(для компилятора) в исходный файлДругие директивы(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Предопределённые идентификаторы или макросы(Predefined Identifiers or Macros):
__TIME__
(строка "чч:мм:сс"), DATE(строка "Ммм дд гггг"), FILE(строка имени исходного файла), LINE(номер текущей строки исходного файла), func(строка "имя текущей функции"), STDC(1 если реализация соответствует стандарту C), STDC_HOSTED, STDC_VERSION(199901L для C99 и 201112L для С11)Выражения обобщённого выбора(Generic Selection Expression) или обобщённое программирование(Generic Programming) в C11(в C++ реализован как шаблон template<>): _Generic(обычно используется с #define) — оператор обобщённого выбора(похож на switch или ручной аналог typeid в C++)
Встраиваемые функции в C99(не обязательно встраивается код как у макросов, но как минимум оптимизируется функция): функция должна быть короткой(для ускорения), с внутренним связыванием, определена(не только объявлена) до первого вызова и находиться в том же файле, где она применяется(inline static void fun(){ ... }), также нельзя получить адрес, может быть не видна в отладчике, обычно определяется в заголовочном файле, также спецификатор функции в C11_Noreturn или noreturn(в stdnoreturn.h)(подобно inline, но не static(спецификатор класса хранения)) — не возвращает управление вызывающей функции(например exit())
Библиотека С(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))
Библиотека утилит общего назначения(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())
Библиотека утверждений(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 — принимает целочисленную константу и строку
Функции в string.h: memmove()(с прототипом void * memmove(void * destination, const void * source, size_t n);) — для побайтового(не поэлементного, то есть на зависящего от типа) копирования данных(перекрытие данных допустимо) и memcpy() — аргументы с restrict предполагают отсутствие перекрытия(overlapping) данных
Переменное число аргументов(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;
}
#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);
}
#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);
}
#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); }
#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
Top comments (0)