Skip to content · ⁨Перейти к содержанию⁩
Subjects · ⁨Предметы⁩
  • 1 C basics · ⁨Основы C⁩
    1.1

    main, printf & functions

    English

    Every C program starts in main. #include <stdio.h> pulls in a header 头文件 so you can use printf to print. A function 函数 is a named block you can call; \n starts a new line. main returns 0 to mean "success".

    Русский

    Каждая программа на C начинается с main. #include <stdio.h> подключает заголовок, чтобы вы могли использовать printf для вывода текста. Функция — это именованный блок кода, который можно вызвать; \n начинает новую строку. main возвращает 0, чтобы означать «успех».

    #include <stdio.h>
    
    void greet(void) {
        printf("Hello, world!\n");
    }
    
    int main(void) {
        greet();
        printf("I am learning C.\n");
        return 0;
    }
    
    Каждая программа на C выполняется из main после подключения заголовков
    Каждая программа на C выполняется из main после подключения заголовков
    1.2

    Переменные, типы и арифметика

    English

    C needs a type for every variable: int (integer 整数), double (floating-point 浮点数), char (one character). printf uses a format specifier 格式说明符 — %d, %f, %c — for each value. / between two ints is integer division; % is the remainder 余数.

    The format string drives both printing and reading:

    Specifier Type Example output
    %d int 17
    %f double (%.2f = 2 decimal places) 3.14
    %c char A
    %s a string Mei
    %zu a size (from sizeof / strlen) 3
    %x int, printed in base 16 ff

    scanf("%d", &n) reads keyboard input with the same specifiers (note the &). Its sibling sscanf parses values out of a string, so it runs anywhere:

    Русский

    C требует тип для каждой переменной: int (целочисленный), double (с плавающей запятой), char (один символ). printf использует спецификатор формата — %d, %f, %c — для каждого значения. / между двумя int — это целочисленное деление; % — это остаток.

    #include <stdio.h>
    
    int main(void) {
        int n = 17;
        double pi = 3.14;
        char grade = 'A';
        printf("%d %.2f %c\n", n, pi, grade);   // 17 3.14 A
        printf("%d %d\n", 7 / 2, 7 % 2);        // 3 1
        return 0;
    }
    

    Форматная строка управляет как выводом, так и чтением:

    Specifier Type Пример вывода
    %d int 17
    %f double (%.2f = 2 знака после десятичной точки) 3.14
    %c char A
    %s строка Mei
    %zu размер (от sizeof / strlen) 3
    %x int, напечатанное в системе счисления 16 ff

    scanf("%d", &n) читает ввод с клавиатуры с теми же спецификаторами (обратите внимание на &). Его собрат sscanf парсит значения из строки, поэтому он может работать везде:

    #include <stdio.h>
    
    int main(void) {
        char line[] = "Mei 88";
        char name[20];
        int score;
        sscanf(line, "%19s %d", name, &score);   // parse text -> values
        printf("%s scored %d\n", name, score);   // Mei scored 88
        return 0;
    }
    
    1.3

    Комментарии и стиль

    English

    A comment 注释 is a note for humans; the compiler ignores it. Use // for one line and /* ... */ for a block. Good indentation 缩进 and clear names make code easy to read.

    Common mistakes

    • Every statement ends with ;, and main should return int (return 0;).
    • printf needs a format string: %d for int, %f for double, and \n for a new line.
    • A wrong format like %d for a double prints garbage — match the type.
    Русский

    Комментарий — это заметка для людей; компилятор игнорирует его. Используйте // для одной строки и /* ... */ для блока. Хорошие отступы и понятные имена делают код легким для чтения.

    #include <stdio.h>
    
    int main(void) {
        // a single-line comment
        /* a block
           comment */
        int total = 3 + 4;       // clear names help
        printf("%d\n", total);   // 7
        return 0;
    }
    

    Распространенные ошибки

    • Каждая инструкция заканчивается ;, и main должна возвращать int (return 0;).
    • printf требует форматную строку: %d для int, %f для double, и \n для новой строки.
    • Неправильный формат, такой как %d для double, выведет мусор — соответствуйте типу.
  • 2 Selection · ⁨Выбор⁩
    2.1

    if / else

    English

    An if runs a block when a condition 条件 is true. C has no real boolean 布尔值 type by default: 0 is false and any non-zero value is true. Chain choices with else if and else. Compare with ==, !=, <, >, <=, >=.

    Русский

    Инструкция if выполняет блок, когда условие истинно. В C нет настоящего типа boolean по умолчанию: 0 означает ложь, а любое ненулевое значение — истину. Цепочку выборов можно строить с помощью else if и else. Сравнивайте с ==, !=, <, >, <=, >=.

    #include <stdio.h>
    
    int main(void) {
        int score = 72;
        if (score >= 90) {
            printf("A\n");
        } else if (score >= 60) {
            printf("Pass\n");
        } else {
            printf("Fail\n");
        }
        return 0;
    }
    
    if выбирает истинную ветку; else — ложную ветку
    if выбирает истинную ветку; else — ложную ветку
    2.2

    switch

    English

    A switch picks one case by an integer value. Each case needs a break to jump out 跳出 — without it, C "falls through" into the next case. default runs when nothing matches.

    Common mistakes

    • if (x = 5) assigns and is always true; use == to compare.
    • Each switch case needs a break;, or it falls through to the next.
    • 0 is false and any non-zero value is true in a condition.
    Русский

    Конструкция switch выбирает один case по целочисленному значению. Каждый case требует break для выхода — без него C «перетекает» (falls through) в следующий case. default выполняется, если ничего не совпадает.

    #include <stdio.h>
    
    int main(void) {
        int day = 3;
        switch (day) {
            case 1: printf("Mon\n"); break;
            case 2: printf("Tue\n"); break;
            case 3: printf("Wed\n"); break;
            default: printf("Other\n");
        }
        return 0;
    }
    

    Распространенные ошибки

    • if (x = 5) присваивает значение и всегда истинно; используйте == для сравнения.
    • Для каждого случая switch требуется break;, иначе выполнение перейдет к следующему.
    • В условии 0 является ложным, а любое ненулевое значение — истинным.
  • 3 Loops · ⁨Циклы⁩
    3.1

    Циклы while & for

    English

    A loop 循环 repeats a block. A while loop runs as long as a condition is true. A for loop packs the start, the test, and the increment 自增 (i++) into one line — best when you know how many times to repeat.

    Русский

    Цикл повторяет блок кода. Цикл while выполняется, пока условие истинно. Цикл for объединяет инициализацию, проверку условия и инкремент (i++) в одну строку — это удобно, когда заранее известно количество повторений.

    #include <stdio.h>
    
    int main(void) {
        int i = 1;
        while (i <= 3) {
            printf("%d ", i);
            i++;
        }
        printf("\n");                    // 1 2 3
        for (int j = 0; j < 5; j++) {
            printf("%d ", j);
        }
        printf("\n");                    // 0 1 2 3 4
        return 0;
    }
    
    while проверяет условие; for считает с шагом
    while проверяет условие; for считает с шагом
    3.2

    Накопление

    English

    A common pattern: start an accumulator 累加器 at 0, then add to it inside a loop. The same idea counts items or finds a running total.

    Русский

    Распространенный шаблон: начать аккумулятор со значения 0, затем прибавлять к нему внутри цикла. Та же идея используется для подсчета элементов или вычисления накопительной суммы.

    #include <stdio.h>
    
    int main(void) {
        int total = 0;
        for (int i = 1; i <= 5; i++) {
            total += i;            // 1 + 2 + 3 + 4 + 5
        }
        printf("%d\n", total);     // 15
        return 0;
    }
    
    3.3

    Вложенные циклы и паттерны

    English

    A loop inside another loop is a nested 嵌套 loop. The inner loop finishes fully for each step of the outer loop — perfect for grids and patterns.

    Common mistakes

    • for (i = 0; i < n; i++) runs n times; a semicolon right after for (...) makes an empty loop.
    • A while whose condition never becomes false loops forever.
    • Declare the counter (int i) before or inside the for header.
    Русский

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

    #include <stdio.h>
    
    int main(void) {
        for (int row = 0; row < 3; row++) {
            for (int col = 0; col < 3; col++) {
                printf("*");
            }
            printf("\n");
        }
        return 0;
    }
    

    Распространенные ошибки

    • for (i = 0; i < n; i++) выполняется n раз; точка с запятой сразу после for (...) образует пустой цикл.
    • Цикл while, у которого условие никогда не становится ложным, будет выполняться бесконечно.
    • Объявите счётчик (int i) до или внутри заголовка for.
  • 4 Functions · ⁨Функции⁩
    4.1

    Параметры и возвращаемые значения

    English

    A function takes parameters 参数 (inputs) and gives back a return value 返回值. The return type comes first (int, double, …); void means it returns nothing.

    Русский

    Функция принимает параметры (входные данные) и возвращает результат. Тип возвращаемого значения указывается первым (int, double, …); ключевое слово void означает, что функция ничего не возвращает.

    #include <stdio.h>
    
    int square(int x) {        // x is a parameter
        return x * x;          // hand back a value
    }
    
    int main(void) {
        int r = square(5);
        printf("%d\n", r);     // 25
        return 0;
    }
    
    Параметры входят; возвращаемое значение выходит
    Параметры входят; возвращаемое значение выходит
    4.2

    Прототипы и области видимости

    English

    C reads top to bottom, so a function must be known before it is called. A prototype 函数原型 — the header line plus ; — declares it early so you can keep main first. A variable's scope 作用域 is the block it lives in: it is local 局部 and disappears when the block ends.

    Common mistakes

    • A function used before it is defined needs a prototype above main.
    • Arguments are passed BY VALUE — changes inside a function do not affect the caller unless you pass a pointer.
    • A variable declared inside a function is local to it.
    Русский

    C обрабатывает код сверху вниз, поэтому функция должна быть объявлена до того, как она вызывается. Прототип — строка заголовка вместе с ; — объявляет функцию заранее, позволяя написать определение функции main раньше main(). Область видимости переменной — это блок кода, в котором она существует: она локальна и исчезает при завершении блока.

    #include <stdio.h>
    
    int add(int a, int b);     // prototype: declared before use
    
    int main(void) {
        printf("%d\n", add(3, 4));   // 7
        return 0;
    }
    
    int add(int a, int b) {    // definition comes later
        int sum = a + b;       // sum is local to add
        return sum;
    }
    

    Распространенные ошибки

    • Если функция используется до своего определения, над ней должна быть прототип main.
    • Аргументы передаются ПО ЗНАЧЕНИЮ — изменения внутри функции не влияют на вызывающий код, если только не передан указатель.
    • Переменная, объявленная внутри функции, является ее локальной.
  • 5 Arrays · ⁨Массивы⁩
    5.1

    1-D arrays

    English

    An array 数组 holds several values of one type. Index 索引 from 0. C does not store an array's length, so a common trick computes the element 元素 count: sizeof(a) / sizeof(a[0]).

    Русский

    An array 数组 holds several values of one type. Index 索引 from 0. C does not store an array's length, so a common trick computes the element 元素 count: sizeof(a) / sizeof(a[0]).

    #include <stdio.h>
    
    int main(void) {
        int scores[3] = {88, 71, 95};
        printf("%d\n", scores[0]);     // 88
        scores[1] = 100;
        printf("%d\n", scores[1]);     // 100
        int n = sizeof(scores) / sizeof(scores[0]);
        printf("%d\n", n);             // 3
        return 0;
    }
    
    Array slots are indexed from 0
    Array slots are indexed from 0
    5.2

    Array algorithms

    English

    Traverse 遍历 an array with a for loop to find a max, a total, or a count. Always loop from 0 up to n - 1.

    Русский

    Traverse 遍历 an array with a for loop to find a max, a total, or a count. Always loop from 0 up to n - 1.

    #include <stdio.h>
    
    int main(void) {
        int a[] = {3, 9, 2, 7};
        int n = sizeof(a) / sizeof(a[0]);
        int max = a[0], total = 0;
        for (int i = 0; i < n; i++) {
            if (a[i] > max) max = a[i];
            total += a[i];
        }
        printf("%d %d\n", max, total);   // 9 21
        return 0;
    }
    
    5.3

    2-D arrays

    English

    A 2-D array is a grid of rows 行 and columns 列: grid[row][col]. Use two nested loops to visit every cell.

    Common mistakes

    • C does NOT check array bounds: writing a[n] in a size-n array is undefined behaviour.
    • Valid indexes run from 0 to n - 1.
    • The array name is a pointer to its first element; sizeof only gives the size in the array's own scope.
    Русский

    A 2-D array is a grid of rows 行 and columns 列: grid[row][col]. Use two nested loops to visit every cell.

    #include <stdio.h>
    
    int main(void) {
        int grid[2][3] = {{1, 2, 3}, {4, 5, 6}};
        printf("%d\n", grid[1][2]);    // 6
        for (int r = 0; r < 2; r++) {
            for (int c = 0; c < 3; c++) {
                printf("%d ", grid[r][c]);
            }
        }
        printf("\n");                  // 1 2 3 4 5 6
        return 0;
    }
    

    Common mistakes

    • C does NOT check array bounds: writing a[n] in a size-n array is undefined behaviour.
    • Valid indexes run from 0 to n - 1.
    • The array name is a pointer to its first element; sizeof only gives the size in the array's own scope.
  • 6 Pointers · ⁨Указатели⁩
    6.1

    &, * и передача по указателю

    English

    A pointer 指针 stores the address 地址 of a variable. &x gives the address of x; *p dereferences 解引用 the pointer — it reads or writes the value stored there. Passing a pointer lets a function change the caller's variable (pass-by-pointer).

    The classic use is a swap function. Passing values only copies them — the swap is lost. Passing pointers lets the function reach the caller's variables:

    An array name acts as a pointer to its first element, and pointer arithmetic 指针运算 steps by whole elements:

    • NULL is the pointer that points at nothing — check for it before you dereference.

    Common mistakes

    • &x is the address of x; *p is the value stored at p.
    • Never use *p on an uninitialised or NULL pointer — it crashes or corrupts memory.
    • To change a caller's variable, pass its address and write through the pointer.
    Русский

    Указатель хранит адрес переменной. Оператор &x дает адрес переменной x; оператор *p разыменовывает указатель — он читает или записывает значение, хранящееся по этому адресу. Передача указателя позволяет функции изменить переменную вызывающего кода (передача по указателю).

    Указатель хранит адрес; разыменование следует по стрелке к значению
    Указатель хранит адрес; разыменование следует по стрелке к значению
    #include <stdio.h>
    
    void addOne(int *p) {     // p holds an address
        *p = *p + 1;          // change the value at that address
    }
    
    int main(void) {
        int x = 10;
        int *ptr = &x;        // & takes the address of x
        printf("%d\n", *ptr); // * reads the value: 10
        addOne(&x);
        printf("%d\n", x);    // 11 — changed through the pointer
        return 0;
    }
    

    Классическое применение — функция обмена (swap). Передача значений лишь копирует их — обмен теряется. Передача указателей позволяет функции получить доступ к переменным вызывающего кода:

    #include <stdio.h>
    
    void swap_values(int a, int b) {      // copies: the caller sees nothing
        int t = a; a = b; b = t;
    }
    
    void swap_pointers(int *a, int *b) {  // addresses: the swap is real
        int t = *a; *a = *b; *b = t;
    }
    
    int main(void) {
        int x = 1, y = 2;
        swap_values(x, y);
        printf("%d %d\n", x, y);   // 1 2  (unchanged!)
        swap_pointers(&x, &y);
        printf("%d %d\n", x, y);   // 2 1  (swapped)
        return 0;
    }
    

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

    #include <stdio.h>
    
    int main(void) {
        int a[] = {10, 20, 30};
        int *p = a;                 // same as &a[0]
        printf("%d\n", *p);         // 10
        printf("%d\n", *(p + 2));   // 30 — same as a[2]
        return 0;
    }
    
    • NULL — это указатель, который ни на что не указывает — проверяйте его на NULL перед разыменованием.

    Распространенные ошибки

    • &x — это адрес x; *p — значение, хранящееся по адресу p.
    • Никогда не используйте *p на неинициализированном или NULL указателе — это приведет к сбою или повреждению памяти.
    • Чтобы изменить переменную вызывающего кода, передайте её адрес и запишите значение через указатель.
  • 7 Strings · ⁨Строки⁩
    7.1

    Char arrays & the null terminator

    English

    A C string 字符串 is an array of char. Every string ends with a hidden null terminator 空终止符 '\0', which marks where it stops. strlen (from <string.h>) counts characters up to that '\0'. Print a whole string with %s and one character 字符 with %c.

    You can traverse a string by looping until you reach '\0'.

    Русский

    A C string 字符串 is an array of char. Every string ends with a hidden null terminator 空终止符 '\0', which marks where it stops. strlen (from <string.h>) counts characters up to that '\0'. Print a whole string with %s and one character 字符 with %c.

    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
        char name[] = "Mei";            // 'M', 'e', 'i', '\0'
        printf("%s\n", name);           // Mei
        printf("%zu\n", strlen(name));  // 3 (stops at '\0')
        printf("%c\n", name[0]);        // M
        return 0;
    }
    
    A C string is a char array ending in a hidden null terminator
    A C string is a char array ending in a hidden null terminator

    You can traverse a string by looping until you reach '\0'.

    #include <stdio.h>
    
    int main(void) {
        char word[] = "banana";
        int count = 0;
        for (int i = 0; word[i] != '\0'; i++) {
            if (word[i] == 'a') count++;
        }
        printf("%d\n", count);   // 3
        return 0;
    }
    
    7.2

    The string.h library

    #include <string.h> gives you the everyday string tools:

    Function Does
    strlen(s) the length, not counting the terminator
    strcpy(dst, src) copy — dst must be big enough
    strcat(dst, src) append src onto the end of dst
    strcmp(a, b) compare: 0 when equal, else negative / positive
    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
        char full[40];
        strcpy(full, "Mei");           // full is now "Mei"
        strcat(full, " Chen");         // append -> "Mei Chen"
        printf("%s (%zu)\n", full, strlen(full));       // Mei Chen (8)
        printf("%d\n", strcmp("apple", "apple") == 0);  // 1 (equal)
        return 0;
    }
    
    • strcmp returns 0 for equal, so the test is strcmp(a, b) == 0 — never a == b.
    • The destination array must have room for the result plus the terminator.

    Common mistakes

    • A C string needs one extra byte for the \0 terminator: a 5-letter word needs char[6].
    • Copy and compare strings with strcpy / strcmp, not = / ==.
    • Forgetting the \0 makes printf("%s", ...) run past the end of the text.
  • 8 Structs · ⁨Структуры⁩
    8.1

    struct, typedef, . vs ->

    English

    A struct 结构体 groups related values into one type. Each value is a member 成员. typedef gives the struct a short name so you can write Dog instead of struct Dog. Use . on a struct value, but -> on a pointer to a struct.

    Common mistakes

    • Use . on a struct value and -> on a struct pointer.
    • typedef lets you drop the word struct when declaring; without it you write struct Point p;.
    • Assigning one struct to another copies all its fields.
    Русский

    Структура (struct) группирует связанные значения в один тип. Каждое значение является членом структуры. Конструкция typedef присваивает структуре короткое имя, позволяя писать Dog вместо длинного имени struct Dog. Используйте точку . для значений структур, но стрелку -> для указателей на структуры.

    #include <stdio.h>
    
    typedef struct {
        char name[20];
        int age;
    } Dog;
    
    int main(void) {
        Dog d = {"Rex", 3};
        printf("%s is %d\n", d.name, d.age);   // Rex is 3   (. on a value)
    
        Dog *p = &d;
        p->age = 4;                            // -> on a pointer
        printf("%s is %d\n", d.name, d.age);   // Rex is 4
        return 0;
    }
    

    Распространенные ошибки

    • Используйте . для значения структуры и -> для указателя на структуру.
    • Конструкция typedef позволяет опускать слово struct при объявлении; без нее нужно писать полное имя struct Point p;.
    • Присвоение одной структуры другой копирует все её поля.
    Точка для значений структур; стрелка для указателей на структуры
    . Точка для значений структур; стрелка для указателей на структуры
  • 9 Dynamic memory · ⁨Динамическая память⁩
    9.1

    malloc, free & realloc

    English

    malloc allocates 分配 memory on the heap 堆 at run time and returns a pointer to it. realloc resizes that block; free gives it back. Every malloc needs a matching free, or you get a memory leak 内存泄漏. These live in <stdlib.h>.

    • calloc(n, size) allocates an array like malloc AND fills it with zeros.

    Common mistakes

    • Every malloc needs a matching free; forgetting to free leaks memory.
    • Using memory after free (a "use-after-free") is a serious bug.
    • Check that malloc did not return NULL before you use the memory.
    Русский

    Функция malloc выделяет память в куче во время выполнения и возвращает указатель на неё. Функция realloc изменяет размер этого блока; функция free освобождает его. На каждый вызов malloc нужен соответствующий вызов free, иначе возникнет утечка памяти. Эти функции определены в заголовочном файле <stdlib.h>.

    Локальные переменные находятся в стеке; память, выделенная через malloc, находится в куче, пока вы не освободите её
    Локальные переменные находятся в стеке; память, выделенная через malloc, находится в куче, пока вы не освободите её
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
        int n = 3;
        int *a = malloc(n * sizeof(int));    // room for 3 ints
        for (int i = 0; i < n; i++) a[i] = i * 10;
    
        a = realloc(a, 4 * sizeof(int));     // grow to 4 ints
        a[3] = 30;
    
        for (int i = 0; i < 4; i++) printf("%d ", a[i]);
        printf("\n");                        // 0 10 20 30
    
        free(a);                             // hand the memory back
        return 0;
    }
    
    • calloc(n, size) выделяет массив, подобно malloc, И заполняет его нулями.

    Распространенные ошибки

    • Каждый malloc требует соответствующего free; если забыть освободить, произойдет утечка памяти.
    • Использование памяти после free (так называемое "использование после освобождения", use-after-free) — это серьезная ошибка.
    • Проверьте, что malloc не вернул NULL, прежде чем использовать память.
  • 10 Data structures · ⁨Структуры данных⁩
    10.1

    Связные списки

    English

    A linked list 链表 is a chain of nodes 节点. Each node holds a value and a pointer to the next node; the last one points to NULL. Unlike an array it grows one node at a time with malloc. Always free every node when done.

    Русский

    Связный список — это цепочка узлов. Каждый узел хранит значение и указатель на следующий next; последний указывает на NULL. В отличие от массива он увеличивается по одному узлу с каждым malloc. Обязательно free каждый узел после завершения работы.

    Связный список: каждый узел хранит значение и указатель на следующий, заканчивая NULL
    Связный список: каждый узел хранит значение и указатель на следующий, заканчивая NULL
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct Node {
        int value;
        struct Node *next;
    } Node;
    
    int main(void) {
        Node *head = NULL;
        for (int v = 10; v <= 30; v += 10) {   // prepend 10, 20, 30
            Node *n = malloc(sizeof(Node));
            n->value = v;
            n->next = head;
            head = n;
        }
        for (Node *p = head; p != NULL; p = p->next) {
            printf("%d ", p->value);           // 30 20 10
        }
        printf("\n");
    
        while (head != NULL) {                 // free the whole list
            Node *t = head;
            head = head->next;
            free(t);
        }
        return 0;
    }
    
    10.2

    Стек и очередь

    English

    A stack 栈 is LIFO 后进先出 (last in, first out): push and pop at the same end. A queue 队列 is FIFO 先进先出 (first in, first out): add at the back, remove from the front. Both are easy to build on an array with index variables.

    Common mistakes

    • In a linked list, never lose the head pointer, or the whole list is unreachable.
    • Free every node when you are done, walking the list before you unlink.
    • Check for an empty list (a NULL head) before you pop.
    Русский

    Стек работает по принципу LIFO (последним пришел — первым ушел): push и pop выполняются с одного конца. Очередь работает по принципу FIFO (первым пришел — первым ушел): элементы добавляются сзади, удаляются спереди. Оба структуры легко реализовать на массиве с использованием индексных переменных.

    #include <stdio.h>
    
    int main(void) {
        int stack[10];
        int top = 0;                       // next free slot
        stack[top++] = 1;                  // push
        stack[top++] = 2;
        stack[top++] = 3;
        while (top > 0) {
            printf("%d ", stack[--top]);   // pop: 3 2 1
        }
        printf("\n");
        return 0;
    }
    
    #include <stdio.h>
    
    int main(void) {
        int queue[10];
        int front = 0, back = 0;
        queue[back++] = 1;                       // enqueue
        queue[back++] = 2;
        queue[back++] = 3;
        while (front < back) {
            printf("%d ", queue[front++]);       // dequeue: 1 2 3
        }
        printf("\n");
        return 0;
    }
    

    Распространенные ошибки

    • В связном списке никогда не теряйте указатель на head, иначе весь список станет недоступным.
    • Освобождайте каждый узел по завершении, проходя по списку перед разрывом связей.
    • Проверяйте пустой список (пустую голову/NULL head), прежде чем выполнять pop.
  • 11 Searching, sorting & recursion · ⁨Поиск, сортировка & рекурсия⁩
    11.1

    Линейный и бинарный поиск

    English

    Linear search 线性查找 checks each element in turn — it works on any array. Binary search 二分查找 is far faster but needs a sorted 已排序 array: it checks the middle and discards half each step. Both return the index, or -1 if missing.

    Русский

    Линейный поиск последовательно проверяет каждый элемент — он работает с любым массивом. Бинарный поиск намного быстрее, но требует отсортированного массива: он проверяет середину и отбрасывает половину на каждом шаге. Оба возвращают индекс или -1, если элемент отсутствует.

    #include <stdio.h>
    
    int linear(int a[], int n, int target) {
        for (int i = 0; i < n; i++)
            if (a[i] == target) return i;
        return -1;
    }
    
    int binary(int a[], int n, int target) {
        int lo = 0, hi = n - 1;
        while (lo <= hi) {
            int mid = (lo + hi) / 2;
            if (a[mid] == target) return mid;
            else if (a[mid] < target) lo = mid + 1;
            else hi = mid - 1;
        }
        return -1;
    }
    
    int main(void) {
        int a[] = {2, 5, 8, 12, 16, 23};
        int n = sizeof(a) / sizeof(a[0]);
        printf("%d %d %d\n", linear(a, n, 12), binary(a, n, 12), binary(a, n, 9));
        return 0;   // 3 3 -1
    }
    
    Линейный перебирает всё; бинарный делит отсортированный диапазон пополам
    Линейный перебирает всё; бинарный делит отсортированный диапазон пополам
    11.2

    Сортировка

    English

    Bubble sort 冒泡排序 repeatedly compares neighbours and swaps 交换 any pair that is out of order. After each pass the largest value "bubbles" to the end.

    Русский

    Пузырьковая сортировка (bubble sort) многократно сравнивает соседние элементы и меняет местами любую пару, которая стоит в неправильном порядке. После каждого прохода максимальное значение «всплывает» в конец.

    #include <stdio.h>
    
    int main(void) {
        int a[] = {5, 2, 9, 1, 7};
        int n = sizeof(a) / sizeof(a[0]);
        for (int i = 0; i < n - 1; i++) {
            for (int j = 0; j < n - 1 - i; j++) {
                if (a[j] > a[j + 1]) {
                    int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t;
                }
            }
        }
        for (int i = 0; i < n; i++) printf("%d ", a[i]);
        printf("\n");   // 1 2 5 7 9
        return 0;
    }
    
    11.3

    Рекурсия

    English

    Recursion 递归 is a function that calls itself. It needs a base case 基准情形 to stop, and a recursive call that steps toward it. Without a base case it never ends.

    Common mistakes

    • Binary search needs a sorted array; recursion needs a base case.
    • Each recursive call must move closer to the base case, or the stack overflows.
    • Bubble and insertion sort are O(n²) — fine to learn, slow at scale.
    Русский

    Рекурсия — это функция, вызывающая саму себя. Ей необходим базовый случай для остановки и рекурсивный вызов, который приближает к нему. Без базового случая она никогда не закончится.

    #include <stdio.h>
    
    int factorial(int n) {
        if (n <= 1) return 1;          // base case
        return n * factorial(n - 1);   // recursive call
    }
    
    int main(void) {
        printf("%d\n", factorial(5));  // 120
        return 0;
    }
    

    Распространенные ошибки

    • Бинарный поиск требует отсортированного массива; рекурсия требует базового случая.
    • Каждый рекурсивный вызов должен приближаться к базовому случаю, иначе произойдет переполнение стека.
    • Пузырьковая и сортировка вставками имеют сложность O(n²) — отлично подходят для изучения, но медленны при больших объемах данных.
  • 12 Files & errors · ⁨Файлы & ошибки⁩
    12.1

    Текстовые файлы

    English

    fopen returns a file pointer 文件指针; the mode 模式 string says what to do — "w" write, "r" read, "a" append. Write with fprintf, read with fscanf, and always fclose when done.

    Русский

    fopen возвращает указатель на файл; строка режима указывает действие — "w" запись, "r" чтение, "a" дописывание. Записывайте с помощью fprintf, читайте с помощью fscanf, и всегда fclose по завершении.

    #include <stdio.h>
    
    int main(void) {
        FILE *out = fopen("scores.txt", "w");   // open for writing
        fprintf(out, "Alice 80\nBob 95\n");
        fclose(out);
    
        FILE *in = fopen("scores.txt", "r");    // open for reading
        char name[20];
        int score;
        while (fscanf(in, "%s %d", name, &score) == 2) {
            printf("%s -> %d\n", name, score);
        }
        fclose(in);
        return 0;
    }
    
    fopen → чтение/запись → fclose
    fopen → чтение/запись → fclose
    12.2

    Обработка ошибок через коды возврата

    English

    C has no exceptions. Instead a function signals failure with a return code 返回码 — by convention 0 means success and non-zero means an error. The caller checks the code before trusting the result. (fopen follows the same idea: it returns NULL when it fails.)

    Common mistakes

    • Check that fopen did not return NULL before reading or writing.
    • Always fclose a file when you are done.
    • Return a non-zero code from main to signal an error to the caller.
    Русский

    В C нет исключений. Вместо этого функция сигнализирует об ошибке через код возврата — по соглашению 0 означает успех, а ненулевое значение — ошибку. Вызывающий код проверяет код, прежде чем доверять результату. (fopen следует той же идее: он возвращает NULL при неудаче.)

    #include <stdio.h>
    
    // returns 0 on success, -1 if the divisor is 0
    int safe_divide(int a, int b, int *result) {
        if (b == 0) return -1;          // error code
        *result = a / b;
        return 0;                       // success
    }
    
    int main(void) {
        int r;
        if (safe_divide(10, 2, &r) == 0)
            printf("10 / 2 = %d\n", r);          // 10 / 2 = 5
        if (safe_divide(10, 0, &r) != 0)
            printf("cannot divide by zero\n");   // error reported
        return 0;
    }
    

    Распространенные ошибки

    • Проверяйте, что fopen не вернул NULL, перед чтением или записью.
    • Всегда fclose файл по завершении работы.
    • Возвращайте ненулевой код из main, чтобы сообщить об ошибке вызывающему коду.
  • 13 Bits & data · ⁨Биты & данные⁩
    13.1

    Bits, binary & bitwise operators

    English

    A bit 位 is a single 0 or 1; numbers are stored in binary 二进制. Bitwise 按位 operators work on the bits directly: & AND, | OR, ^ XOR, << shift left (×2 each step), >> shift right (÷2).

    Hexadecimal 十六进制 (base 16) writes binary compactly: one hex digit is exactly four bits. Write hex literals with 0x; print with %x:

    Русский

    A bit 位 is a single 0 or 1; numbers are stored in binary 二进制. Bitwise 按位 operators work on the bits directly: & AND, | OR, ^ XOR, << shift left (×2 each step), >> shift right (÷2).

    #include <stdio.h>
    
    int main(void) {
        int a = 12;              // 1100
        int b = 10;              // 1010
        printf("%d\n", a & b);   // 8   AND  -> 1000
        printf("%d\n", a | b);   // 14  OR   -> 1110
        printf("%d\n", a ^ b);   // 6   XOR  -> 0110
        printf("%d\n", a << 1);  // 24  left shift
        printf("%d\n", a >> 1);  // 6   right shift
        return 0;
    }
    

    Hexadecimal 十六进制 (base 16) writes binary compactly: one hex digit is exactly four bits. Write hex literals with 0x; print with %x:

    #include <stdio.h>
    
    int main(void) {
        printf("%d\n", 0xFF);   // 255  (0x marks a hex literal)
        printf("%x\n", 255);    // ff
        printf("%x\n", 12);     // c
        return 0;
    }
    
    Bitwise operators work on the bits of integers
    Bitwise operators work on the bits of integers
    13.2

    Run-length encoding

    English

    Run-length encoding (RLE) is a simple compression 压缩 method: replace each run 游程 of repeated characters with a count and the character. It is lossless 无损 — the original is fully recoverable.

    Common mistakes

    • & is bitwise AND and && is logical AND — do not confuse them.
    • A left shift << 1 doubles a value; a right shift >> 1 halves it.
    • Bit n has value 1 << n (C has no ** power operator); check a bit with (x >> n) & 1.
    Русский

    Run-length encoding (RLE) is a simple compression 压缩 method: replace each run 游程 of repeated characters with a count and the character. It is lossless 无损 — the original is fully recoverable.

    #include <stdio.h>
    #include <string.h>
    
    int main(void) {
        char *s = "aaabbc";
        int n = strlen(s);
        for (int i = 0; i < n; ) {
            char c = s[i];
            int run = 0;
            while (i < n && s[i] == c) { run++; i++; }
            printf("%d%c", run, c);     // 3a2b1c
        }
        printf("\n");
        return 0;
    }
    

    Common mistakes

    • & is bitwise AND and && is logical AND — do not confuse them.
    • A left shift << 1 doubles a value; a right shift >> 1 halves it.
    • Bit n has value 1 << n (C has no ** power operator); check a bit with (x >> n) & 1.
  • 14 Preprocessor & qualifiers · ⁨Предпроцессор & модификаторы⁩
    14.1

    Предпроцессор, const, static, enum

    English

    The preprocessor 预处理器 runs before compiling. #define makes a macro 宏 — plain text replaced everywhere. const makes a constant 常量 that cannot change. static inside a function keeps a variable's value between calls. An enum (enumeration 枚举) names a set of integers starting at 0.

    Bigger programs split across files: declarations go in a header 头文件 file (mine.h), included with #include "mine.h" — quotes mean your own file, < > means the standard library. An include guard (#ifndef MINE_H / #define MINE_H / #endif) stops a header being included twice.

    Common mistakes

    • #define does plain text replacement with no type checking; wrap macro bodies and arguments in parentheses.
    • A const value cannot be changed after it is set.
    • A static local variable keeps its value between calls to the function.
    Русский

    Препроцессор выполняется перед компиляцией. #define создает макрос — простой текст, заменяемый повсюду. const создает константу, которую нельзя изменить. static внутри функции сохраняет значение переменной между вызовами. Перечисление (enumeration) enum задает набор целых чисел, начинающихся со значения 0.

    #include <stdio.h>
    
    #define MAX 100                     // macro: text replaced before compiling
    
    enum Color { RED, GREEN, BLUE };    // named integers 0, 1, 2
    
    int next_id(void) {
        static int count = 0;           // keeps its value between calls
        count++;
        return count;
    }
    
    int main(void) {
        const double PI = 3.14;         // cannot be changed
        printf("%d %.2f\n", MAX, PI);            // 100 3.14
        printf("%d %d %d\n", RED, GREEN, BLUE);  // 0 1 2
        int a = next_id();
        int b = next_id();
        printf("%d %d\n", a, b);                 // 1 2
        return 0;
    }
    

    Большие программы разбиваются на файлы: объявления помещают в заголовочный файл (mine.h), который подключают через #include "mine.h" — кавычки означают ваш собственный файл, угловые скобки (< >) — стандартную библиотеку. Предупреждение о включении (#ifndef MINE_H / #define MINE_H / #endif) предотвращает повторное подключение заголовочного файла.

    Распространенные ошибки

    • #define выполняет простую замену текста без проверки типов; оборачивайте тела макросов и аргументы в скобки.
    • Значение константы const нельзя изменить после присвоения.
    • Статическая локальная переменная (static) сохраняет свое значение между вызовами функции.
    #define, const, enum и static делают код понятным
    #define, const, enum и static делают код понятным

Log in or create account · ⁨Войти или создать аккаунт⁩

IGCSE, A-Level & AP