5.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 intsigned charlong 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 *data = malloc(rows * cols * sizeof(int));
int **matrix = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
matrix[i] = data + i * cols;
}
// освобождение:
free(matrix);
free(data);
Второй вариант эффективнее по памяти и кэш-локальности.
Функции, рекурсия, область видимости, классы памяти и компиляция
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
Применяется в двух контекстах:
-
Для локальных переменных:
- Время жизни — вся программа.
- Инициализируется один раз.
- Сохраняет значение между вызовами функции.
void counter(void) {
static int count = 0;
count++;
printf("%d\n", count);
} -
Для глобальных переменных и функций:
- Ограничивает область видимости текущим файлом.
- Предотвращает конфликты имён при линковке нескольких файлов.
extern
- Указывает, что переменная или функция определена в другом файле.
- Используется для совместного доступа к глобальным данным между единицами компиляции.
Файл globals.c:
int global_var = 42;
Файл main.c:
extern int global_var; // ссылка на переменную из другого файла
5. Компиляция и сборка программы
Процесс преобразования исходного кода в исполняемый файл включает несколько этапов.
Этапы компиляции
-
Препроцессирование
Выполняется препроцессором (cpp). Обрабатывает директивы:#include— вставка содержимого файла#define— макроподстановка#if,#ifdef,#ifndef,#else,#elif,#endif— условная компиляция#pragma— специфичные для компилятора инструкции
-
Компиляция
Преобразует предварительно обработанный.cфайл в ассемблерный код или объектный файл (.oили.obj). -
Ассемблирование (если генерируется ассемблер)
Преобразует ассемблерный код в машинный код в объектном файле. -
Линковка
Объединяет объектные файлы и библиотеки в единый исполняемый файл.- Разрешает внешние ссылки (
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 Data {
int i;
float f;
char str[20];
};
union Data d;
d.i = 10; // запись в поле i
d.f = 220.5; // перезаписывает содержимое i
Важно: в каждый момент времени в объединении корректно только одно поле — то, в которое последним выполнялась запись.
Применение
- Интерпретация одного и того же участка памяти как разных типов.
- Экономия памяти, когда объект может быть одного из нескольких типов, но не всех одновременно.
3. Перечисления (enum)
Перечисление — тип данных, определяющий набор именованных целочисленных констант.
Объявление
enum Color {
RED,
GREEN,
BLUE
};
По умолчанию:
RED == 0GREEN == 1BLUE == 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
Стандартная библиотека — ввод-вывод, память, строки, математика, время, символы
Стандартная библиотека языка Си предоставляет переносимые функции для работы с файлами, памятью, строками, математическими вычислениями, временем и другими базовыми задачами. Все функции объявлены в заголовочных файлах, поставляемых вместе с компилятором.
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);— →longlong 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);— завершает программу, вызывает обработчикиatexitvoid _Exit(int status);— немедленное завершение (C99)- Коды:
EXIT_SUCCESS,EXIT_FAILURE
Выполнение команд оболочки
int system(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);— длина начального сегмента, содержащего только символы изacceptsize_t strcspn(const char *s, const char *reject);— длина сегмента до первого символа изrejectchar *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,tandouble asin(double x);,acos,atan,atan2double exp(double x);— e^xdouble 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— eM_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);— в UTCtime_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'