Перейти к основному содержимому

Справочник по языку С

Назначение

Быстрый доступ к языку С: команды, синтаксис, свойства, типовые операции. Не дублирует вводный курс — см. учебные статьи раздела.

Учебный раздел: intro.

Краткое пояснение

Компоненты и ключевые особенности языка.

Быстрый старт

ТипОписаниеРазмер (обычно)Диапазон значений
charСмвол или малое целое1 байт-128 до 127 (signed), 0 до 255 (unsigned)
signed charЦелое со знаком1 байт-128 до 127
unsigned charЦелое без знака1 байт0 до 255
short / short intКороткое целое2 байта-32 768 до 32 767
unsigned shortКороткое целое без знака2 байта0 до 65 535
intЦелое4 байта-2 147 483 648 до 2 147 483 647
unsigned intЦелое без знака4 байта0 до 4 294 967 295
long / long intДлинное целое4 или 8 байтзависит от платформы
unsigned longДлинное целое без знака4 или 8 байтзависит от платформы
long longОчень длинное целое8 байт-9 223 372 036 854 775 808 до 9 223 372 036 854 775 807
unsigned long longОчень длинное целое без знака8 байт0 до 18 446 744 073 709 551 615
floatВещественное одинарной точности4 байта~ ±3.4e±38 (7 цифр точности)
doubleВещественное двойной точности8 байт~ ±1.7e±308 (15–16 цифр точности)
long doubleВещественное расширенной точности10–16 байтзависит от компилятора и архитектуры

Справочные таблицы

Основы
1. Структура программы на языке С

Программа на языке С состоит из одной или нескольких функций. Обязательной является функция main, с которой начинается выполнение программы.

#include <stdio.h> // подключение заголовочного файла стандартной библиотеки

int main(void) {
printf("Hello, World!\n");
return 0;
}
  • #include — директива препроцессора для включения содержимого заголовочного файла.
  • int main(void) — определение главной функции без параметров, возвращающей целое число.
  • printf — функция вывода текста в стандартный поток вывода.
  • return 0; — завершение функции main со значением 0, что означает успешное завершение программы.
2. Базовые типы данных

Язык С предоставляет следующие фундаментальные типы данных:

ТипОписаниеРазмер (обычно)Диапазон значений
charСмвол или малое целое1 байт-128 до 127 (signed), 0 до 255 (unsigned)
signed charЦелое со знаком1 байт-128 до 127
unsigned charЦелое без знака1 байт0 до 255
short / short intКороткое целое2 байта-32 768 до 32 767
unsigned shortКороткое целое без знака2 байта0 до 65 535
intЦелое4 байта-2 147 483 648 до 2 147 483 647
unsigned intЦелое без знака4 байта0 до 4 294 967 295
long / long intДлинное целое4 или 8 байтзависит от платформы
unsigned longДлинное целое без знака4 или 8 байтзависит от платформы
long longОчень длинное целое8 байт-9 223 372 036 854 775 808 до 9 223 372 036 854 775 807
unsigned long longОчень длинное целое без знака8 байт0 до 18 446 744 073 709 551 615
floatВещественное одинарной точности4 байта~ ±3.4e±38 (7 цифр точности)
doubleВещественное двойной точности8 байт~ ±1.7e±308 (15–16 цифр точности)
long doubleВещественное расширенной точности10–16 байтзависит от компилятора и архитектуры

Размеры типов зависят от архитектуры процессора и компилятора. Для точного определения размера используется оператор sizeof.

Пример:

printf("Размер int: %zu байт\n", sizeof(int));
3. Модификаторы типов

Модификаторы изменяют свойства базовых типов:

  • signed — явное указание знакового типа (по умолчанию для int, char может быть signed или unsigned в зависимости от реализации).
  • unsigned — указывает, что значение не может быть отрицательным.
  • short — уменьшает размер целого типа.
  • long — увеличивает размер целого или вещественного типа.
  • long long — дополнительно увеличивает размер целого типа.

Комбинации:

  • unsigned long long int
  • signed char
  • long double
4. Константы

Константы — значения, которые не изменяются во время выполнения программы.

Целочисленные константы
  • Десятичные: 42
  • Восьмеричные: 052 (начинаются с 0)
  • Шестнадцатеричные: 0x2A (начинаются с 0x или 0X)
  • Двоичные (в некоторых компиляторах): 0b101010 (не входит в стандарт C89/C90, но поддерживается в GCC и Clang как расширение)

Можно добавлять суффиксы:

  • U или u — беззнаковый (42U)
  • L или l — длинный (42L)
  • LL или ll — очень длинный (42LL)
  • Комбинации: 42ULL, 123456789012345LL
Вещественные константы
  • Десятичные: 3.14, .5, 1.
  • Экспоненциальная форма: 6.02e23, 1.23E-4
  • Суффиксы: f или F — float (3.14f), l или L — long double (3.14L)
Смвольные константы
  • 'A' — один символ, хранится как его код ASCII
  • Escape-последовательности: '\n', '\t', '\\', '\'', '\"', '\0' (нулевой символ)
Строковые литералы
  • "Hello" — массив символов, завершённый нулём (\0)
  • Многострочные литералы:
    "Первая часть "
    "вторая часть"
    автоматически объединяются в одну строку.
Константы через #define
#define PI 3.14159
#define MAX_SIZE 100
Константы через const
const int MAX = 100;
const char* GREETING = "Привет";
5. Переменные

Переменная — именованная область памяти, хранящая значение определённого типа.

Объявление:

int age;
float price = 19.99;
char initial = 'T';

Правила именования:

  • Имя начинается с буквы или подчёркивания _
  • Может содержать буквы, цифры, подчёркивания
  • Регистрозависимо: count и Count — разные переменные
  • Не может совпадать с ключевыми словами (int, return, if и т.д.)

Область видимости:

  • Локальные переменные — внутри функции или блока { }
  • Глобальные переменные — вне всех функций, доступны во всей программе

Хранение:

  • auto — автоматическая (по умолчанию для локальных)
  • static — сохраняет значение между вызовами функции
  • extern — ссылка на глобальную переменную, определённую в другом файле
  • register — подсказка компилятору разместить переменную в регистре (редко используется)
6. Операторы
Арифметические операторы
  • + — сложение
  • - — вычитание
  • * — умножение
  • / — деление (целочисленное, если оба операнда целые)
  • % — остаток от деления (только для целых)
Операторы присваивания
  • = — простое присваивание
  • +=, -=, *=, /=, %= — составные присваивания
Операторы инкремента и декремента
  • ++ — увеличение на 1
  • -- — уменьшение на 1
  • Префиксная форма (++x) — сначала изменение, потом использование
  • Постфиксная форма (x++) — сначала использование, потом изменение
Операторы сравнения
  • == — равно
  • != — не равно
  • < — меньше
  • > — больше
  • <= — меньше или равно
  • >= — больше или равно
Логические операторы
  • && — логическое И
  • || — логическое ИЛИ
  • ! — логическое НЕ
Побитовые операторы
  • & — побитовое И
  • | — побитовое ИЛИ
  • ^ — побитовое исключающее ИЛИ (XOR)
  • ~ — побитовое НЕ (инверсия)
  • << — сдвиг влево
  • >> — сдвиг вправо
Условный оператор (тернарный)
result = (a > b) ? a : b;
Оператор sizeof

Возвращает размер типа или переменной в байтах:

size_t s = sizeof(int);
Оператор взятия адреса & и разыменования *
  • &x — адрес переменной x
  • *p — значение по адресу, на который указывает указатель p

Указатели, массивы, строки и управление памятью
1. Указатели

Указатель — переменная, хранящая адрес другой переменной в памяти.

Объявление указателя
int *p; // указатель на int
char *str; // указатель на char
void *generic; // универсальный указатель (без типа)
Инициализация
int x = 42;
int *p = &x; // p содержит адрес переменной x
Разыменование
int value = *p; // значение по адресу, на который указывает p (равно 42)
*p = 100; // изменение значения переменной x через указатель
Арифметика указателей
  • Прибавление целого числа к указателю смещает его на n * sizeof(тип) байт.
  • Разность двух указателей одного типа даёт количество элементов между ними.

Пример:

int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // эквивалентно &arr[0]
printf("%d\n", *(p + 2)); // выводит 30
Указатели и функции

Указатели позволяют передавать аргументы по ссылке:

void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}

Вызов:

int x = 5, y = 10;
swap(&x, &y);
Указатель на указатель
int x = 42;
int *p = &x;
int **pp = &p;
printf("%d\n", **pp); // 42
Константные указатели
  • const int *p — указатель на константное значение (нельзя изменить значение через p)
  • int *const p = &x — константный указатель (нельзя изменить адрес, на который он указывает)
  • const int *const p = &x — константный указатель на константное значение
2. Массивы

Массив — упорядоченный набор элементов одного типа, размещённых в непрерывной области памяти.

Объявление и инициализация
int arr[5]; // массив из 5 неинициализированных int
int nums[] = {1, 2, 3, 4, 5}; // размер определяется автоматически
int matrix[3][3]; // двумерный массив
Доступ к элементам
arr[0] = 100; // первый элемент
printf("%d", arr[2]); // третий элемент

Индексация начинается с 0. Максимальный допустимый индекс — размер - 1.

Массивы и указатели

Имя массива без индекса преобразуется в указатель на первый элемент:

int arr[5];
int *p = arr; // то же, что и &arr[0]

Размер массива можно получить только в той области видимости, где он объявлен как массив (не как параметр функции):

int size = sizeof(arr) / sizeof(arr[0]); // работает только внутри той же функции
Передача массива в функцию

Массив всегда передаётся как указатель:

void print_array(int arr[], int n); // эквивалентно int *arr
void print_array(int *arr, int n);

Функция не знает реальный размер массива — его нужно передавать отдельно.

3. Строки

В языке С строка — это массив символов, завершённый нулевым символом \0.

Объявление строк
char str1[] = "Hello"; // автоматический размер (6 байт: 5 букв + \0)
char str2[10] = "Hi"; // остаток заполняется нулями
char *str3 = "World"; // указатель на строковый литерал (константный!)

Важно: строковые литералы ("...") размещаются в защищённой памяти. Попытка изменить str3[0] = 'w' вызывает неопределённое поведение.

Функции работы со строками (из <string.h>)
ФункцияОписание
strlen(s)Возвращает длину строки (без учёта \0)
strcpy(dest, src)Копирует src в dest (включая \0)
strncpy(dest, src, n)Копирует не более n символов; не гарантирует завершение \0
strcat(dest, src)Добавляет src к концу dest
strncat(dest, src, n)Добавляет не более n символов из src
strcmp(s1, s2)Сравнивает строки: 0 — равны, <0 — s1 < s2, >0 — s1 > s2
strncmp(s1, s2, n)Сравнивает первые n символов
strchr(s, c)Ищет первое вхождение символа c в строке s
strstr(s1, s2)Ищет подстроку s2 в строке s1
strtok(s, delim)Разбивает строку на токены по разделителям

Пример:

#include <string.h>
char greeting[20] = "Hello";
strcat(greeting, ", World!");
printf("%s\n", greeting); // Hello, World!
4. Управление памятью

Язык С предоставляет ручное управление динамической памятью через функции из <stdlib.h>.

Выделение памяти
  • malloc(size) — выделяет size байт, возвращает void*, содержимое не инициализировано
  • calloc(n, size) — выделяет память для n элементов по size байт, заполняет нулями
  • realloc(ptr, new_size) — изменяет размер ранее выделенного блока

Пример:

int *arr = malloc(10 * sizeof(int));
if (arr == NULL) {
// обработка ошибки
}
// использование arr...
Освобождение памяти
  • free(ptr) — освобождает память, выделенную через malloc, calloc или realloc
  • После free указатель становится недействительным («висячим»). Рекомендуется присвоить NULL.
Правила работы с динамической памятью
  • Всегда проверяйте результат malloc/calloc на NULL
  • Не вызывайте free для одного и того же указателя дважды
  • Не вызывайте free для указателя, не полученного через malloc/calloc/realloc
  • Освобождайте всю выделенную память до завершения программы (во избежание утечек)
Пример безопасного использования
int *create_array(size_t n) {
int *p = calloc(n, sizeof(int));
if (p == NULL) {
fprintf(stderr, "Ошибка выделения памяти\n");
exit(EXIT_FAILURE);
}
return p;
}

void destroy_array(int *p) {
free(p);
}
5. Многомерные массивы
Статические многомерные массивы
int matrix[3][4]; // 3 строки, 4 столбца
matrix[0][0] = 1;

Хранятся в памяти последовательно по строкам.

Динамические двумерные массивы (вариант 1: массив указателей)
int **matrix = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
matrix[i] = malloc(cols * sizeof(int));
}
// использование...
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
Динамические двумерные массивы (вариант 2: единый блок памяти)
int *Данные = malloc(rows * cols * sizeof(int));
int **matrix = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
matrix[i] = Данные + i * cols;
}
// освобождение:
free(matrix);
free(Данные);

Второй вариант эффективнее по памяти и кэш-локальности.


Функции, рекурсия, область видимости, классы памяти и компиляция
1. Функции

Функция — именованный блок кода, выполняющий определённую задачу. Каждая программа на С содержит хотя бы одну функцию — main.

Структура определения функции
возвращаемый_тип имя_функции(список_параметров) {
// тело функции
return значение; // если возвращаемый тип не void
}

Пример:

int max(int a, int b) {
return (a > b) ? a : b;
}
Объявление (прототип) функции

Перед использованием функции её можно объявить:

int max(int a, int b); // прототип

Прототип указывает компилятору сигнатуру функции: имя, типы параметров и возвращаемый тип. Это позволяет вызывать функцию до её определения.

Параметры функции
  • Параметры передаются по значению: внутри функции создаются копии аргументов.
  • Для изменения исходных переменных используются указатели.
  • Список параметров может быть пустым: void func(void) — явное указание отсутствия параметров.
Возвращаемое значение
  • Тип void означает, что функция ничего не возвращает.
  • Оператор return завершает выполнение функции и возвращает значение.
  • В функции main возврат 0 означает успешное завершение; ненулевые значения — ошибки.
Перегрузка функций

Язык С не поддерживает перегрузку функций. Каждая функция должна иметь уникальное имя.

2. Рекурсия

Рекурсия — вызов функцией самой себя.

Условия корректной рекурсии
  • Должен существовать базовый случай — условие, при котором рекурсия прекращается.
  • Каждый рекурсивный вызов должен приближать вычисления к базовому случаю.

Пример: вычисление факториала

unsigned long long factorial(int n) {
if (n <= 1) return 1; // базовый случай
return n * factorial(n - 1); // рекурсивный вызов
}
Ограничения рекурсии
  • Глубина рекурсии ограничена размером стека вызовов.
  • Чрезмерная глубина приводит к переполнению стека (stack overflow).
  • Итеративные решения часто эффективнее по памяти.
3. Область видимости (scope)

Область видимости определяет, где в программе доступно имя переменной или функции.

Локальная область видимости
  • Переменные, объявленные внутри блока { }, видны только внутри этого блока и вложенных блоков.
  • Параметры функции также имеют локальную область видимости.
Глобальная область видимости
  • Переменные и функции, объявленные вне всех функций, видны во всём файле от места объявления до конца.
  • Если глобальный идентификатор объявлен до всех функций, он виден во всём файле.
Область видимости файла
  • Идентификаторы с модификатором static на уровне файла видны только в этом файле.
  • Это механизм инкапсуляции на уровне компиляции.

Пример:

static int counter = 0; // видна только в этом .c файле

void increment(void) {
counter++;
}
4. Классы памяти (storage classes)

Класс памяти определяет время жизни, область видимости и способ хранения переменной.

auto
  • По умолчанию для всех локальных переменных.
  • Существует только во время выполнения блока.
  • Хранится в стеке.
register
  • Подсказка компилятору разместить переменную в регистре процессора.
  • Применимо только к целочисленным и указательным типам.
  • Нельзя взять адрес (&) такой переменной.
  • Современные компиляторы игнорируют эту подсказку, так как сами эффективно распределяют регистры.
static

Применяется в двух контекстах:

  1. Для локальных переменных:

    • Время жизни — вся программа.
    • Инициализируется один раз.
    • Сохраняет значение между вызовами функции.
    void counter(void) {
    static int count = 0;
    count++;
    printf("%d\n", count);
    }
  2. Для глобальных переменных и функций:

    • Ограничивает область видимости текущим файлом.
    • Предотвращает конфликты имён при линковке нескольких файлов.
extern
  • Указывает, что переменная или функция определена в другом файле.
  • Используется для совместного доступа к глобальным данным между единицами компиляции.

Файл globals.c:

int global_var = 42;

Файл main.c:

extern int global_var; // ссылка на переменную из другого файла
5. Компиляция и сборка программы

Процесс преобразования исходного кода в исполняемый файл включает несколько этапов.

Этапы компиляции
  1. Препроцессирование
    Выполняется препроцессором (cpp). Обрабатывает директивы:

    • #include — вставка содержимого файла
    • #define — макроподстановка
    • #if, #ifdef, #ifndef, #else, #elif, #endif — условная компиляция
    • #pragma — специфичные для компилятора инструкции
  2. Компиляция
    Преобразует предварительно обработанный .c файл в ассемблерный код или объектный файл (.o или .obj).

  3. Ассемблирование (если генерируется ассемблер)
    Преобразует ассемблерный код в машинный код в объектном файле.

  4. Линковка
    Объединяет объектные файлы и библиотеки в единый исполняемый файл.

    • Разрешает внешние ссылки (extern)
    • Включает код из стандартной библиотеки (libc)
Команды компиляции (на примере GCC)
gcc -E program.c -o program.i # только препроцессирование
gcc -S program.c -o program.s # до ассемблера
gcc -c program.c -o program.o # компиляция без линковки
gcc program.c -o program # полная компиляция и линковка
Многофайловые проекты

Структура:

main.c
utils.c
utils.h

utils.h:

#ifndef UTILS_H
#define UTILS_H
int add(int a, int b);
#endif

utils.c:

#include "utils.h"
int add(int a, int b) {
return a + b;
}

main.c:

#include <stdio.h>
#include "utils.h"

int main(void) {
printf("%d\n", add(2, 3));
return 0;
}

Сборка:

gcc main.c utils.c -o app

Или поэтапно:

gcc -c main.c -o main.o
gcc -c utils.c -o utils.o
gcc main.o utils.o -o app
Заголовочные файлы (.h)
  • Содержат объявления: прототипы функций, определения типов, макросы.
  • Не содержат определений переменных (кроме static inline или const в некоторых случаях).
  • Защищаются от повторного включения через include guards (#ifndef ... #define ... #endif) или #pragma once.
6. Макросы и условная компиляция
Макросы с параметрами
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))

Важно заключать параметры и всё выражение в скобки, чтобы избежать побочных эффектов при подстановке.

Специальные макросы
  • __FILE__ — имя текущего файла
  • __LINE__ — номер текущей строки
  • __func__ — имя текущей функции (C99)
  • __DATE__, __TIME__ — дата и время компиляции
Условная компиляция
#ifdef DEBUG
printf("Отладочная информация\n");
#endif

#if defined(_WIN32)
// код для Windows
#elif defined(__linux__)
// код для Linux
#endif

Часто используется для кроссплатформенности, отладки, включения/выключения функций.


Структуры, объединения, перечисления, typedef, битовые поля и продвинутый препроцессор
1. Структуры (struct)

Структура — составной тип данных, объединяющий несколько переменных (полей) разных типов под одним именем.

Объявление структуры
struct Point {
int x;
int y;
};
Определение переменных структурного типа
struct Point p1; // без инициализации
struct Point p2 = {10, 20}; // инициализация в порядке полей
struct Point p3 = {.y = 5, .x = 3}; // инициализация по именам (C99)
Доступ к полям
  • Через оператор . для переменных:
    p1.x = 100;
    printf("%d\n", p1.y);
  • Через оператор -> для указателей:
    struct Point *pp = &p1;
    pp->x = 200;
    printf("%d\n", pp->y);
Вложенные структуры
struct Address {
char street[50];
int zip;
};

struct Person {
char name[30];
struct Address addr;
};

// доступ:
struct Person p;
p.addr.zip = 12345;
Массивы структур
struct Student students[100];
students[0].grade = 5;
Передача структур в функции
  • По значению (копируется вся структура):
    void print_point(struct Point p) { ... }
  • По указателю (эффективнее для больших структур):
    void move_point(struct Point *p, int dx, int dy) {
    p->x += dx;
    p->y += dy;
    }
Размер структуры

Размер структуры определяется с учётом выравнивания (padding). Поля выравниваются по границам, кратным их размеру, для повышения производительности.

Пример:

struct Example {
char a; // 1 байт
int b; // 4 байта → между a и b добавляется 3 байта padding
};
// sizeof(struct Example) == 8 на большинстве систем

Для управления выравниванием используются компилятор-специфичные директивы, например:

#pragma pack(1) // отключает выравнивание
struct Packed {
char a;
int b;
};
#pragma pack() // восстанавливает настройки
2. Объединения (union)

Объединение — тип данных, все поля которого разделяют одну и ту же область памяти. Размер объединения равен размеру его самого большого поля.

Объявление и использование
union Данные {
int i;
float f;
char str[20];
};

union Данные d;
d.i = 10; // запись в поле i
d.f = 220.5; // перезаписывает содержимое i

Важно: в каждый момент времени в объединении корректно только одно поле — то, в которое последним выполнялась запись.

Применение
  • Интерпретация одного и того же участка памяти как разных типов.
  • Экономия памяти, когда объект может быть одного из нескольких типов, но не всех одновременно.
3. Перечисления (enum)

Перечисление — тип данных, определяющий набор именованных целочисленных констант.

Объявление
enum Color {
RED,
GREEN,
BLUE
};

По умолчанию:

  • RED == 0
  • GREEN == 1
  • BLUE == 2

Можно задавать явные значения:

enum Status {
OK = 0,
ERROR = -1,
TIMEOUT = -2
};
Использование
enum Color background = GREEN;
if (background == RED) { ... }

Перечисления повышают читаемость кода и помогают избежать «магических чисел».

4. Псевдонимы типов (typedef)

Ключевое слово typedef создаёт новое имя для существующего типа.

Примеры
typedef unsigned long ulong;
typedef struct Point Point; // теперь можно писать Point p; вместо struct Point p;
typedef enum Color Color;

Полное объявление с typedef:

typedef struct {
int x, y;
} Point;

Теперь Point — полноценный тип, и struct больше не требуется.

Преимущества
  • Упрощает синтаксис.
  • Скрывает детали реализации (например, что тип — это структура).
  • Улучшает переносимость (например, size_t, time_t в стандартной библиотеке).
5. Битовые поля

Битовые поля позволяют упаковывать данные на уровне отдельных битов внутри структуры.

Объявление
struct Flags {
unsigned int is_ready : 1; // 1 бит
unsigned int is_valid : 1; // 1 бит
unsigned int error_code : 4; // 4 бита
unsigned int reserved : 26; // остальные биты в 32-битном слове
};
Использование
struct Flags f = {0};
f.is_ready = 1;
f.error_code = 5;
Особенности
  • Тип поля должен быть целочисленным (int, unsigned int, _Bool и т.д.).
  • Порядок битов зависит от архитектуры (little-endian / big-endian).
  • Нельзя взять адрес битового поля (&f.is_ready — ошибка).
  • Полезны для работы с аппаратными регистрами, сетевыми протоколами, компактным хранением флагов.
6. Продвинутые возможности препроцессора
Макросы с переменным числом аргументов (variadic macros)

Поддерживаются начиная с C99:

#define LOG(fmt, ...) printf("[LOG] " fmt "\n", __VA_ARGS__)

Использование:

LOG("Значение: %d", 42); // → printf("[LOG] Значение: %d\n", 42);

Если макрос может вызываться без дополнительных аргументов, используется GCC-совместимый синтаксис:

#define DEBUG_PRINT(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
Строкификация (#)

Преобразует макропараметр в строковый литерал:

#define STR(x) #x
char *s = STR(hello); // → "hello"
Конкатенация (##)

Объединяет два токена:

#define VAR(name) var_##name
int VAR(count) = 10; // → int var_count = 10;
Предопределённые макросы
МакросОписание
__STDC__Определяется как 1, если компилятор соответствует ANSI C
__STDC_VERSION__Версия стандарта (например, 201710L для C17)
__LINE__Номер текущей строки
__FILE__Имя текущего файла
__func__Имя текущей функции (C99)
__DATE__Дата компиляции ("Mmm dd yyyy")
__TIME__Время компиляции ("hh:mm:ss")
Условная компиляция: расширенные формы
#if defined(MAX_SIZE) && MAX_SIZE > 100
// код для больших размеров
#elif defined(DEBUG)
// отладочный код
#else
// общий случай
#endif
Защита заголовочных файлов

Стандартный шаблон:

#ifndef MY_HEADER_H
#define MY_HEADER_H

// содержимое заголовка

#endif // MY_HEADER_H

Альтернатива (не во всех компиляторах):

#pragma once

Ошибки

Типичные ошибки

СтуацияЧто проверить
Команда не найденаPATH, имя пакета, версия ОС/ПО
Отказ в доступеправа пользователя, sudo, SELinux/AppArmor
Снтаксическая ошибкаверсия языка/SQL-диалекта, кавычки, экранирование

Совместимость

Стандартная библиотека — ввод-вывод, память, строки, математика, время, символы

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


1. <stdio.h> — Ввод и вывод данных

Этот заголовок содержит функции для работы с потоками (FILE*) — абстракцией над файлами, консолью, сетевыми соединениями и другими источниками/приёмниками данных.

Основные потоки
  • stdin — стандартный ввод (обычно клавиатура)
  • stdout — стандартный вывод (обычно терминал)
  • stderr — стандартный поток ошибок (не буферизуется)
Форматированный вывод
int printf(const char *format, ...); // вывод в stdout
int fprintf(FILE *stream, const char *format, ...); // вывод в указанный поток
int sprintf(char *str, const char *format, ...); // запись в строку
int snprintf(char *str, size_t n, const char *format, ...); // безопасная версия с ограничением длины

Спецификаторы формата:

  • %d, %i — целое со знаком
  • %u — целое без знака
  • %x, %X — шестнадцатеричное (нижний/верхний регистр)
  • %o — восьмеричное
  • %c — символ
  • %s — строка (char*)
  • %f, %F — вещественное (десятичное)
  • %e, %E — экспоненциальная форма
  • %g, %G — автоматический выбор между %f и %e
  • %p — адрес (указатель)
  • %% — вывод символа %

Модификаторы ширины и точности:

  • %5d — минимум 5 символов, выравнивание по правому краю
  • %-10s — минимум 10 символов, выравнивание по левому краю
  • %.2f — 2 знака после запятой
  • %.*s — точность задаётся аргументом
Форматированный ввод
int scanf(const char *format, ...); // чтение из stdin
int fscanf(FILE *stream, const char *format, ...); // чтение из потока
int sscanf(const char *str, const char *format, ...); // чтение из строки

Особенности scanf:

  • Пропускает начальные пробельные символы.
  • Останавливается при несоответствии формата.
  • Для чтения строки: char s[100]; scanf("%99s", s); — защита от переполнения.
  • %[...] — чтение набора символов: scanf("%[a-zA-Z]", s);
  • %n — записывает количество прочитанных символов (не ввод!).
Небуферизованный ввод-вывод
int getchar(void); // читает один символ из stdin
int putchar(int c); // выводит один символ в stdout
int fgetc(FILE *f); // читает символ из потока
int fputc(int c, FILE *f); // записывает символ в поток
Работа с файлами
FILE *fopen(const char *filename, const char *mode);
int fclose(FILE *stream);

Режимы открытия:

  • "r" — чтение (файл должен существовать)
  • "w" — запись (создаёт или обнуляет файл)
  • "a" — добавление (запись в конец)
  • "r+" — чтение и запись (файл существует)
  • "w+" — чтение и запись (создаёт или обнуляет)
  • "a+" — чтение и добавление

Двоичные режимы: добавляют b (например, "rb", "wb"), особенно важно на Windows.

Буферизация
  • setbuf(stream, buf) — установка буфера
  • setvbuf(stream, buf, mode, size) — гибкая настройка
  • fflush(stream) — принудительная запись буфера на диск
Позиционирование в файле
long ftell(FILE *stream); // текущая позиция
int fseek(FILE *stream, long offset, int origin); // перемещение
void rewind(FILE *stream); // переход в начало

Значения origin:

  • SEEK_SET — от начала файла
  • SEEK_CUR — от текущей позиции
  • SEEK_END — от конца файла

2. <stdlib.h> — Общие утилиты

Управление памятью
  • void *malloc(size_t size);
  • void *calloc(size_t nmemb, size_t size);
  • void *realloc(void *ptr, size_t new_size);
  • void free(void *ptr);
Преобразование строк в числа
  • int atoi(const char *nptr); — строка → int (без обработки ошибок)
  • long atol(const char *nptr); — → long
  • long long atoll(const char *nptr); — → long long

Более надёжные функции:

  • long strtol(const char *nptr, char **endptr, int base);
  • double strtod(const char *nptr, char **endptr);
  • float strtof(...), long double strtold(...)

Параметр endptr указывает на первый недопустимый символ — позволяет проверить корректность преобразования.

Генерация случайных чисел
int rand(void); // возвращает число от 0 до RAND_MAX
void srand(unsigned int seed); // инициализация генератора

Пример:

srand(time(NULL));
int dice = rand() % 6 + 1; // число от 1 до 6
Завершение программы
  • void exit(int status); — завершает программу, вызывает обработчики atexit
  • void _Exit(int status); — немедленное завершение (C99)
  • Коды: EXIT_SUCCESS, EXIT_FAILURE
Выполнение команд оболочки
  • int Система(const char *command); — запускает команду в системной оболочке

3. <string.h> — Операции со строками и памятью

Уже частично рассмотрено в части 2. Здесь — полный охват.

Строковые функции (работают с \0-завершёнными строками)
  • size_t strlen(const char *s);
  • char *strcpy(char *dest, const char *src);
  • char *strncpy(char *dest, const char *src, size_t n);
  • char *strcat(char *dest, const char *src);
  • char *strncat(char *dest, const char *src, size_t n);
  • int strcmp(const char *s1, const char *s2);
  • int strncmp(const char *s1, const char *s2, size_t n);
  • char *strchr(const char *s, int c);
  • char *strrchr(const char *s, int c); — последнее вхождение
  • char *strstr(const char *haystack, const char *needle);
  • size_t strspn(const char *s, const char *accept); — длина начального сегмента, содержащего только символы из accept
  • size_t strcspn(const char *s, const char *reject); — длина сегмента до первого символа из reject
  • char *strtok(char *str, const char *delim); — разбиение на токены (изменяет исходную строку!)
Функции работы с памятью (байтовые операции)
  • void *memcpy(void *dest, const void *src, size_t n); — копирование без пересечения
  • void *memmove(void *dest, const void *src, size_t n); — копирование с возможным пересечением
  • void *memset(void *s, int c, size_t n); — заполнение байтами
  • int memcmp(const void *s1, const void *s2, size_t n); — побайтовое сравнение

4. <math.h> — Математические функции

Требует линковки с математической библиотекой: gcc program.c -lm

Основные функции
  • double sin(double x);, cos, tan
  • double asin(double x);, acos, atan, atan2
  • double exp(double x); — e^x
  • double log(double x); — натуральный логарифм
  • double log10(double x); — десятичный логарифм
  • double pow(double base, double exponent);
  • double sqrt(double x);
  • double fabs(double x); — модуль
  • double ceil(double x); — округление вверх
  • double floor(double x); — округление вниз
  • double round(double x); — математическое округление (C99)
  • double fmod(double x, double y); — остаток от деления вещественных чисел
Константы (если определено _USE_MATH_DEFINES или включены расширения)
  • M_PI — π
  • M_E — e
  • M_SQRT2 — √2

5. <time.h> — Работа со временем

Типы
  • time_t — целочисленный тип для хранения времени (обычно секунды с 1 января 1970)
  • struct tm — структура с полями года, месяца, дня и т.д.
  • clock_t — для измерения процессорного времени
Функции
  • time_t time(time_t *tloc); — текущее календарное время
  • struct tm *localtime(const time_t *timer); — преобразование в местное время
  • struct tm *gmtime(const time_t *timer); — в UTC
  • time_t mktime(struct tm *tm); — обратное преобразование
  • char *asctime(const struct tm *tm); — строковое представление (формат фиксирован)
  • size_t strftime(char *s, size_t max, const char *format, const struct tm *tm); — гибкое форматирование
  • clock_t clock(void); — процессорное время с начала выполнения

Пример форматирования:

time_t now = time(NULL);
struct tm *t = localtime(&now);
char buffer[100];
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", t);
printf("Текущее время: %s\n", buffer);

6. <ctype.h> — Классификация и преобразование символов

Все функции принимают int (для совместимости с EOF), но работают с unsigned char.

Проверки
  • int isalpha(int c); — буква
  • int isdigit(int c); — цифра
  • int isalnum(int c); — буква или цифра
  • int isspace(int c); — пробельный символ (' ', \t, \n и др.)
  • int isupper(int c);, islower(int c);
  • int isxdigit(int c); — шестнадцатеричная цифра
  • int ispunct(int c); — знак препинания
  • int isprint(int c); — печатаемый символ
Преобразования
  • int toupper(int c);
  • int tolower(int c);

Пример:

char c = 'a';
if (islower(c)) c = toupper(c); // → 'A'

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).