LEARN X · ЗА 18 МИН

C

Весь язык C на одной странице: типы, указатели, массивы, структуры, malloc, препроцессор и файлы — экспресс-тур в комментариях кода.

C — компактный компилируемый язык, на котором написаны ядра ОС, драйверы и интерпретаторы. Весь язык — на одной странице, прямо в комментариях кода. Компилируй: gcc file.c -o file и запускай ./file.

1. Структура программы

// Это однострочный комментарий
/* А это многострочный
   комментарий */

#include <stdio.h>   // подключаем стандартный ввод-вывод (printf и т.д.)

// int main — точка входа. Возвращает код завершения (0 = успех)
int main(void) {
    printf("Привет, мир!\n");  // \n — перевод строки
    return 0;                   // 0 сообщает ОС: всё прошло успешно
}

// Вывод программы:
// Привет, мир!

2. Переменные и типы

В C переменную надо объявить с типом. Размеры зависят от платформы (даны типичные).

#include <stdio.h>
#include <stdbool.h>  // для типа bool (C99)

int main(void) {
    int    i = 42;          // целое, обычно 4 байта
    short  s = 10;          // короткое целое, обычно 2 байта
    long   l = 1000000L;    // длинное целое
    float  f = 3.14f;       // вещественное одинарной точности (суффикс f)
    double d = 2.71828;     // вещественное двойной точности
    char   c = 'A';         // символ (1 байт), хранит код 65
    _Bool  b = 1;           // логический тип C99 (или bool из stdbool.h)
    unsigned int u = 4000000000U;  // только неотрицательные

    // Спецификаторы printf:
    printf("%d\n", i);      // %d — int            -> 42
    printf("%ld\n", l);     // %ld — long          -> 1000000
    printf("%f\n", f);      // %f — float/double   -> 3.140000
    printf("%.2f\n", d);    // %.2f — 2 знака      -> 2.72
    printf("%c\n", c);      // %c — символ         -> A
    printf("%d\n", c);      // как число           -> 65
    printf("%u\n", u);      // %u — unsigned       -> 4000000000
    printf("%x\n", 255);    // %x — hex            -> ff

    // Константа: значение нельзя менять
    const double PI = 3.14159;
    printf("%f\n", PI);     // -> 3.141590
    return 0;
}

3. Ввод-вывод

#include <stdio.h>

int main(void) {
    int age;
    float height;

    printf("Введите возраст: ");
    scanf("%d", &age);       // & — передаём адрес переменной!

    printf("Введите рост: ");
    scanf("%f", &height);    // для float в scanf нужен %f

    // Вывод нескольких значений в одной строке:
    printf("Вам %d лет, рост %.1f\n", age, height);

    char name[50];
    scanf("%49s", name);     // строка без пробелов; имя массива = адрес, & не нужен
    printf("Привет, %s!\n", name);
    return 0;
}
// Пример сессии (ввод: 25, 1.80, Аня):
// Вам 25 лет, рост 1.8
// Привет, Аня!

4. Операторы и условия

#include <stdio.h>

int main(void) {
    int a = 10, b = 3;
    printf("%d\n", a + b);   // 13
    printf("%d\n", a - b);   // 7
    printf("%d\n", a * b);   // 30
    printf("%d\n", a / b);   // 3  (целочисленное деление!)
    printf("%d\n", a %% b);  // 1  (остаток); в коде пишется один %

    // Сравнения: ==, !=, <, >, <=, >=
    // Логика: && (и), || (или), ! (не)
    if (a > b && b > 0) {
        printf("a больше b и b положительно\n");
    } else if (a == b) {
        printf("равны\n");
    } else {
        printf("иначе\n");
    }

    // Тернарный оператор:  условие ? если_да : если_нет
    int max = (a > b) ? a : b;
    printf("max = %d\n", max);   // max = 10

    // switch — выбор по значению
    int day = 3;
    switch (day) {
        case 1: printf("Пн\n"); break;  // break обязателен!
        case 3: printf("Ср\n"); break;  // -> Ср
        default: printf("другой\n");
    }
    return 0;
}

5. Циклы

#include <stdio.h>

int main(void) {
    // for: инициализация; условие; шаг
    for (int i = 0; i < 3; i++) {
        printf("i=%d ", i);   // i=0 i=1 i=2
    }
    printf("\n");

    // while: проверка перед телом
    int n = 3;
    while (n > 0) {
        printf("%d ", n);     // 3 2 1
        n--;
    }
    printf("\n");

    // do-while: тело выполнится хотя бы раз
    int k = 0;
    do {
        printf("раз ");       // раз
    } while (k > 0);
    printf("\n");

    // break — выйти из цикла, continue — к следующей итерации
    for (int i = 0; i < 10; i++) {
        if (i == 2) continue; // пропустить 2
        if (i == 5) break;    // остановиться на 5
        printf("%d ", i);     // 0 1 3 4
    }
    printf("\n");
    return 0;
}

6. Массивы и строки

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

#include <stdio.h>
#include <string.h>   // strlen, strcpy, strcmp, strcat

int main(void) {
    int nums[5] = {10, 20, 30, 40, 50};  // массив из 5 int
    printf("%d\n", nums[0]);   // 10  (индексация с нуля)
    nums[1] = 99;
    printf("%d\n", nums[1]);   // 99

    int sizeN = sizeof(nums) / sizeof(nums[0]);  // длина = 5
    printf("длина: %d\n", sizeN);

    // Строки
    char s1[20] = "Привет";   // массив char с '\0' в конце
    char s2[20];

    printf("%zu\n", strlen("abc"));  // strlen — длина -> 3
    strcpy(s2, s1);                   // копирование s1 в s2
    strcat(s2, "!");                  // конкатенация -> "Привет!"
    printf("%s\n", s2);               // Привет!

    // strcmp: 0 если строки равны
    if (strcmp("abc", "abc") == 0)
        printf("строки равны\n");
    return 0;
}

7. Указатели

Указатель хранит адрес переменной. & — взять адрес, * — разыменовать (получить значение по адресу).

#include <stdio.h>

int main(void) {
    int x = 42;
    int *p = &x;        // p указывает на x (хранит адрес x)

    printf("%d\n", x);   // 42  (значение)
    printf("%p\n", (void*)&x); // адрес x, напр. 0x7ffe...
    printf("%d\n", *p);  // 42  (* — значение по адресу)

    *p = 100;            // меняем x через указатель
    printf("%d\n", x);   // 100

    // Арифметика указателей: шаг = размер типа
    int arr[3] = {7, 8, 9};
    int *q = arr;        // имя массива = адрес первого элемента
    printf("%d\n", *q);     // 7
    printf("%d\n", *(q+1)); // 8  (q+1 -> следующий int)
    printf("%d\n", q[2]);   // 9  (q[2] == *(q+2))

    int *nothing = NULL; // NULL — "никуда не указывает"
    if (nothing == NULL) printf("пустой указатель\n");
    return 0;
}

8. Функции

Прототип объявляет функцию заранее; передача по умолчанию — по значению (копия). Чтобы изменить аргумент, передают указатель.

#include <stdio.h>

// Прототипы (объявления до main)
int sum(int a, int b);
void increment(int *n);

int main(void) {
    printf("%d\n", sum(2, 3));  // 5

    int x = 10;
    increment(&x);   // передаём адрес
    printf("%d\n", x); // 11  (значение изменилось)

    int y = 10;
    // передача по значению ничего бы не изменила:
    // increment_value(y) -> y осталось бы 10
    (void)y;
    return 0;
}

// Определения функций
int sum(int a, int b) {     // a, b — копии аргументов
    return a + b;
}

void increment(int *n) {    // n — адрес; *n — само значение
    (*n)++;                 // меняем переменную вызвавшего
}

9. Структуры

#include <stdio.h>
#include <string.h>

// struct объединяет поля разных типов
struct Point {
    int x;
    int y;
};

// typedef задаёт короткое имя типа
typedef struct {
    char name[30];
    int  age;
} Person;

int main(void) {
    struct Point p = {3, 4};
    printf("%d, %d\n", p.x, p.y);   // 3, 4   (доступ через .)
    p.x = 10;

    Person alice;
    strcpy(alice.name, "Алиса");
    alice.age = 30;
    printf("%s, %d\n", alice.name, alice.age);  // Алиса, 30

    // Через указатель используют стрелку ->
    Person *ptr = &alice;
    printf("%d\n", ptr->age);  // 30  (то же, что (*ptr).age)
    return 0;
}

10. Динамическая память

Память в куче выделяется вручную и так же вручную освобождается через free — иначе утечка.

#include <stdio.h>
#include <stdlib.h>   // malloc, calloc, realloc, free

int main(void) {
    // malloc — выделить N байт (неинициализированных)
    int *arr = malloc(3 * sizeof(int));
    if (arr == NULL) return 1;   // проверка: память могла не выделиться
    arr[0] = 1; arr[1] = 2; arr[2] = 3;
    printf("%d\n", arr[1]);      // 2

    // calloc — выделить и обнулить
    int *zeros = calloc(3, sizeof(int));
    printf("%d\n", zeros[0]);    // 0

    // realloc — изменить размер блока
    arr = realloc(arr, 5 * sizeof(int));
    arr[3] = 4; arr[4] = 5;
    printf("%d\n", arr[4]);      // 5

    free(arr);    // обязательно освобождаем!
    free(zeros);
    arr = NULL;   // хорошая привычка после free
    return 0;
}

11. Препроцессор

#include <stdio.h>

#define PI 3.14159            // макрос-константа (текстовая замена)
#define SQUARE(x) ((x) * (x)) // макрос-функция (скобки важны!)
#define DEBUG 1

int main(void) {
    printf("%f\n", PI);        // 3.141590
    printf("%d\n", SQUARE(5)); // 25
    printf("%d\n", SQUARE(2 + 3)); // 25  (благодаря скобкам в макросе)

    // Условная компиляция
#ifdef DEBUG
    printf("режим отладки\n");  // попадёт в код, т.к. DEBUG определён
#endif

#if DEBUG == 1
    printf("DEBUG == 1\n");
#else
    printf("релиз\n");
#endif
    return 0;
}
// Вывод:
// 3.141590
// 25
// 25
// режим отладки
// DEBUG == 1

12. Файлы

#include <stdio.h>

int main(void) {
    // Запись: "w" — создать/перезаписать
    FILE *f = fopen("data.txt", "w");
    if (f == NULL) return 1;          // файл мог не открыться
    fprintf(f, "%d %s\n", 42, "hello"); // пишем как printf, но в файл
    fclose(f);                        // обязательно закрываем

    // Чтение: "r"
    FILE *r = fopen("data.txt", "r");
    if (r == NULL) return 1;
    int n;
    char word[50];
    fscanf(r, "%d %s", &n, word);     // читаем как scanf, но из файла
    printf("%d %s\n", n, word);       // 42 hello
    fclose(r);

    // Режимы: "r" чтение, "w" запись, "a" дозапись в конец,
    //         "rb"/"wb" — бинарный режим
    return 0;
}

Итог: ты прошёл весь базовый C — от printf до указателей, ручного управления памятью и работы с файлами. Дальше — заголовочные файлы (.h), битовые операции, union и стандартная библиотека. Компилируй с флагами -Wall -Wextra, чтобы ловить ошибки раньше.

Поддержать проект