Загрузка данных


Хорошо. Вот все 30 вопросов (из двух списков) с развёрнутыми, но сжатыми ответами, охватывающими все подпункты.

---

1. Обзор языка C: особенности, область применения, история, связь с Unix. Структура программы на Си, функция main(), компиляция с помощью gcc.
C создан Деннисом Ритчи в 1970-х для переписывания Unix, что сделало его переносимым. Особенности: низкоуровневость (указатели, память), эффективность, минимализм, переносимость. Применение: ОС, драйверы, встраиваемые системы, высоконагруженные приложения. Структура: директивы препроцессора, глобальные объявления, функция main() — точка входа, другие функции. main() бывает void или с argc/argv, возвращает int (0 = успех). Компиляция: gcc source.c -o program; этапы: препроцессинг → компиляция в ассемблер → ассемблирование → линковка.

---

2. Типы данных и их вывод. Целочисленные и вещественные типы. Функция printf().
Целые: char (1 байт), short, int (обычно 4), long, long long — со знаком и без (unsigned). Вещественные: float (4), double (8), long double. Для вывода используется printf("формат", переменные): %d/%i для int, %u для unsigned, %ld для long, %f для float/double (с точностью %.2f), %e для экспоненциальной формы, %c для char, %s для строк. Также есть модификаторы ширины и выравнивания.

---

3. Символьный тип данных. Управляющие символы.
char — 1 байт, хранит код символа (ASCII). Может быть знаковым (-128..127) или беззнаковым (0..255). Вывод через %c, ввод через %c или getchar(). Управляющие символы (escape-последовательности): \n (новая строка), \t (табуляция), \r (возврат каретки), \b (забой), \a (звуковой сигнал), \\ (обратный слэш), \' и \" для кавычек, \0 — нуль-терминатор (конец строки).

---

4. Логические выражения и операции.
В C ложь — это 0, истина — любое ненулевое значение (обычно 1). Логические операции: && (логическое И) — истинно, если оба операнда истинны; || (логическое ИЛИ) — истинно, если хотя бы один истинен; ! (логическое НЕ) — инвертирует. Они вычисляются с сокращением (short-circuit): при && если первый ложный, второй не вычисляется; при || если первый истинный, второй не вычисляется. Используются в условиях if, while, for.

---

5. Операторы ветвления. if-else, тернарный, switch.
if (условие) { ... } else if (условие) { ... } else { ... } — условное выполнение. Тернарный оператор: условие ? выражение1 : выражение2 — возвращает первое, если условие истинно, иначе второе. switch (целочисленное_выражение) { case значение1: ... break; case значение2: ... break; default: ... } — множественный выбор, сравнивает выражение с константными метками; break обязателен для выхода, иначе выполнение провалится в следующий case.

---

6. Циклы в языке C. while, do-while, for. Инкремент и декремент.
while (условие) { тело } — с предусловием (может не выполниться ни разу). do { тело } while (условие); — с постусловием (выполнится минимум один раз). for (инициализация; условие; шаг) { тело } — компактная запись для счётных циклов (любая часть может быть пустой). Инкремент ++ увеличивает на 1, декремент -- уменьшает на 1. Префиксная форма (++i) — сначала изменение, потом использование; постфиксная (i++) — сначала использование, потом изменение.

---

7. Битовые операции.
Работают на уровне отдельных битов целочисленных типов: & (побитовое И) — устанавливает 1 только если оба бита 1; | (ИЛИ) — 1 если хотя бы один 1; ^ (исключающее ИЛИ, XOR) — 1 если биты разные; ~ (побитовое НЕ) — инвертирует все биты; << (сдвиг влево) — умножение на 2^n, младшие биты заполняются нулями; >> (сдвиг вправо) — деление на 2^n, для знаковых заполнение знаковым битом (арифметический), для беззнаковых — нулями. Используются в флагах, криптографии, работе с железом.

---

8. Посимвольный ввод и вывод. Понятие буфера.
getchar() — читает один символ из стандартного ввода, возвращает int (значение EOF при ошибке/конце). putchar(int c) — выводит один символ. Буфер — область памяти, где временно накапливаются вводимые/выводимые данные. Для ввода буфер обычно построчный (данные передаются программе только после нажатия Enter), что позволяет редактировать строку до отправки. Для вывода буфер накапливает данные и выводит при заполнении, при закрытии программы или при fflush(stdout). Работа через буфер повышает производительность (меньше медленных операций ввода-вывода).

---

9. Переменные, адреса и указатели.
Переменная — именованная область памяти, хранящая значение. Адрес — номер байта в памяти, где располагается переменная (получается через оператор &). Указатель — переменная, которая хранит адрес другой переменной; объявляется с * (например, int *p). Чтобы получить значение по адресу, используется оператор разыменования * (например, *p = 10). Указатели обязаны иметь тип, чтобы знать, сколько байт читать и как интерпретировать данные. Нулевой указатель (NULL) — адрес, гарантированно не указывающий на допустимую память.

---

10. Функции. Передача аргументов по значению и по ссылке.
Функция — блок кода с именем, параметрами и возвращаемым значением. При передаче по значению создаётся копия аргумента, изменения внутри функции не влияют на оригинал. При передаче по ссылке (в Си реализуется через указатели) в функцию передаётся адрес переменной, и через разыменование можно изменить оригинал. Пример: void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } — вызываем swap(&x, &y). Передача по ссылке экономит память для больших структур и позволяет возвращать несколько значений.

---

11. Форматированный ввод данных.
scanf("формат", &переменная1, &переменная2, ...) — читает данные из стандартного ввода по формату. Спецификаторы: %d для int, %f для float, %lf для double, %c для char, %s для строки (без пробелов), %u для unsigned. Важно передавать адреса переменных (через &). Возвращает количество успешно прочитанных элементов. Пропуск пробелов и переводов строк автоматический (кроме %c). Для безопасного чтения строк используют fgets() вместо scanf("%s"). Можно задавать ширину поля: %10s — не более 10 символов.

---

12. Псевдослучайные числа. Функции rand и srand. Диапазоны значений.
rand() возвращает псевдослучайное целое от 0 до RAND_MAX (обычно 32767). srand(seed) инициализирует генератор начальным числом; если не вызвать, seed = 1 (последовательность будет одинаковой). Для разных запусков используют srand(time(NULL)) из <time.h>. Для получения числа в диапазоне [min, max]: rand() % (max - min + 1) + min. Для равномерного распределения лучше использовать (double)rand() / RAND_MAX * (max - min) + min. Генератор линейный конгруэнтный, не подходит для криптографии.

---

13. Массивы и указатели. Адресная арифметика.
Массив — непрерывная область памяти для однотипных элементов. Имя массива является указателем на первый элемент (arr == &arr[0]). Адресная арифметика: arr + i даёт адрес i-го элемента; *(arr + i) эквивалентно arr[i]. Шаг арифметики зависит от типа: для int arr+1 сдвигается на 4 байта. Размер массива определяется при компиляции, узнать его можно через sizeof(arr)/sizeof(arr[0]), но при передаче в функцию массив "вырождается" в указатель, и размер теряется. Многомерные массивы хранятся построчно (row-major).

---

14. Массивы и функции. Передача указателя на массив.
При передаче массива в функцию фактически передаётся указатель на первый элемент: void func(int *arr, int size) или void func(int arr[], int size) — эквивалентно. Размер массива внутри функции недоступен через sizeof, поэтому его нужно передавать отдельным параметром. Для изменения элементов массива внутри функции достаточно разыменования указателя. Для передачи двумерного массива нужно указать размер второго измерения: void func(int arr[][cols], int rows). Можно также передавать указатель на указатель для динамических массивов.

---

15. Особенности работы со строками.
Строка в C — массив символов char, оканчивающийся нуль-терминатором '\0'. Длина строки — количество символов до '\0' (не включая его). Объявление: char s1[] = "Hello"; (размер 6) или char *s2 = "World"; (указатель на строковой литерал, неизменяемый). Для ввода строк с пробелами используют fgets(s, size, stdin), которая сохраняет '\n'. Важно выделять достаточно памяти для терминатора. Строковые литералы неизменяемы; попытка изменить s2[0] — неопределённое поведение. Для копирования, сравнения, конкатенации используют функции из <string.h>.

---

16. Функции для обработки строк. Заголовочный файл string.h.
Основные функции: strlen(s) — длина без '\0'; strcpy(dest, src) — копирование (небезопасна, нет проверки размера); strncpy(dest, src, n) — с ограничением; strcat(dest, src) — конкатенация; strncat(dest, src, n) — с ограничением; strcmp(s1, s2) — сравнение лексикографическое (0 если равны, отрицательное если s1 < s2); strncmp(s1, s2, n) — с ограничением; strchr(s, c) — поиск символа; strstr(s, sub) — поиск подстроки; strtok(s, delim) — разбиение на токены (изменяет исходную строку). Безопасные аналоги: strcpy_s, strcat_s (C11). Для работы с памятью: memcpy, memmove, memset, memcmp.

---

17. Структурный тип данных. Указатели и структуры. Массив структур.
Структура (struct) — пользовательский тип, объединяющий разнотипные данные: struct Student { char name[50]; int age; float gpa; };. Доступ к полям через . (для переменной) и -> (для указателя). Указатель на структуру объявляется как struct Student *p = &s;, доступ: p->age = 20; или (*p).age = 20;. Массив структур: struct Student group[30]; — обращение через индекс и точку: group[i].age. Структуры можно передавать в функции по значению (копия) или по указателю (экономия памяти и возможность изменения). Для выравнивания памяти компилятор может добавлять отступы (padding).

---

18. Динамические структуры данных. Стек.
Стек (LIFO — Last In, First Out) — структура, где добавление и удаление происходят с одной стороны (вершины). Реализуется на основе динамических узлов: каждый узел содержит данные и указатель на следующий. Операции: push — добавляет новый узел на вершину (выделяет память через malloc), pop — удаляет вершину и возвращает данные (освобождает память через free), peek/top — просмотр без удаления. Вершина хранится в указателе top. При push новый узел ссылается на старый top, затем top обновляется. При pop сохраняется старый top, top сдвигается на следующий, старый освобождается. Проверка на пустоту: top == NULL.

---

19. Динамические структуры данных. Очередь.
Очередь (FIFO — First In, First Out) — структура, где добавление в конец, удаление из начала. Реализуется на узлах с указателями на следующий. Хранит два указателя: front (начало, откуда удаляем) и rear (конец, куда добавляем). Операции: enqueue — создаёт новый узел, если очередь пуста — front = rear = new_node, иначе rear->next = new_node; rear = new_node;; dequeue — сохраняет front, перемещает front на следующий, освобождает старый узел, если очередь опустела — rear = NULL. Проверка пустоты: front == NULL. Очередь используется в планировщиках задач, буферах данных, обходе графов (BFS).

---

20. Ввод данных из файла и вывод в файл. Текстовые файлы. Бинарные файлы.
Работа с файлами через FILE * и функции: fopen("file.txt", "r/w/a/b") — открытие; fclose(fp) — закрытие. Текстовые файлы: fprintf(fp, ...) — форматированный вывод; fscanf(fp, ...) — форматированный ввод; fgets(buf, size, fp) и fputs(str, fp) — построчный ввод/вывод. Бинарные файлы: fwrite(ptr, size, count, fp) — запись блоков памяти; fread(ptr, size, count, fp) — чтение блоков; режим "rb"/"wb". Бинарные файлы компактнее и быстрее, но непереносимы между платформами. Для произвольного доступа: fseek(fp, offset, SEEK_SET/CUR/END), ftell(fp). Обязательно проверять возврат fopen на NULL.

---

21. Передача аргументов в программу. Параметры функции main.
main имеет прототип int main(int argc, char *argv[]) (или char **argv). argc — количество аргументов командной строки (включая имя программы). argv[0] — имя исполняемого файла, argv[1]...argv[argc-1] — переданные строки. argv[argc] всегда равен NULL. Аргументы передаются как строки, для преобразования в числа используют atoi(), atol(), strtol() (более безопасен). Пример: ./program input.txt 5 → argc=3, argv[1]="input.txt", argv[2]="5". Обработку ошибок (недостаточно аргументов, неверный формат) делают вручную. Это позволяет делать утилиты командной строки.

---

22. Препроцессор языка C. Директивы и макросы.
Препроцессор обрабатывает код до компиляции. Основные директивы: #include — вставка содержимого файла (угловые скобки для системных, кавычки для своих); #define — определение макроса (константы или функции-макросы): #define PI 3.14159, #define SQUARE(x) ((x)*(x)); #undef — отмена определения. Условная компиляция: #ifdef, #ifndef, #if, #else, #elif, #endif — используется для платформозависимого кода и защиты от повторного включения (#pragma once или #ifndef HEADER_H). Макросы — текстовая подстановка, поэтому важно заключать параметры в скобки. # превращает параметр в строку, ## конкатенирует токены.

---

23. Многофайловые программы. MakeFile.
Код разбивается на .c (реализация) и .h (заголовочные с объявлениями) для переиспользования и ускорения компиляции. Каждый .c компилируется в .o (объектный файл), затем линкуются в исполняемый. Заголовочные файлы защищают от двойного включения (#ifndef ... #define ... #endif). Makefile описывает правила сборки: цели, зависимости, команды. Пример:

```makefile
program: main.o utils.o
    gcc main.o utils.o -o program
main.o: main.c utils.h
    gcc -c main.c
utils.o: utils.c utils.h
    gcc -c utils.c
```

Переменные (CC=gcc, CFLAGS=-Wall), автоматические переменные ($@, $^), неявные правила упрощают Makefile. make пересобирает только изменённые файлы.

---

24. Создание библиотеки. Статическая и динамическая библиотека.
Статическая (.a в Linux) — архив объектных файлов. Создание: gcc -c lib.c → ar rcs libmylib.a lib.o. Использование: при линковке копируется в исполняемый файл — программа независима, но размер большой, обновление требует перелинковки. Динамическая (.so в Linux) — разделяемая библиотека. Создание: gcc -shared -fPIC -o libmylib.so lib.c. Использование: при запуске загружается в память, код не копируется — экономия памяти, можно обновлять без перекомпиляции, но нужна совместимость версий. Переменные LD_LIBRARY_PATH для поиска .so. Под Windows аналоги: .lib (статическая) и .dll (динамическая).

---

25. Время и даты.
Заголовочный файл <time.h>. time_t — целочисленный тип (секунды с 1 января 1970, Unix Epoch). time(&t) или time_t t = time(NULL) — получить текущее время. struct tm — разбитое представление: секунды, минуты, часы, день, месяц (0-11), год (с 1900), день недели и т.д. localtime(&t) — преобразует в локальное время (структура tm). gmtime(&t) — в UTC. asctime(&tm) — строка фиксированного формата. strftime(buf, size, format, &tm) — форматированный вывод (например, "%Y-%m-%d %H:%M:%S"). difftime(t1, t2) — разница в секундах. clock() — процессорное время. sleep() для задержек (Unix) или Sleep() (Windows).

---

26. Базовые команды навигации в Linux. Перемещение по директориям. Создание файлов/директорий.
pwd — показать текущую директорию. cd <путь> — перейти в директорию (cd .. — на уровень выше, cd ~ — в домашнюю, cd / — в корень). ls — список файлов (-l — подробно, -a — включая скрытые). mkdir <имя> — создать директорию (-p — с промежуточными). touch <файл> — создать пустой файл или обновить время. cp <источник> <назначение> — копировать (-r — рекурсивно для директорий). mv <источник> <назначение> — переместить/переименовать. rm <файл> — удалить (-r — рекурсивно, -f — без подтверждения). rmdir — удалить пустую директорию. cat — показать содержимое файла. man <команда> — справка.

---

27. Bash-script. Что это, понимание работы и умение написания простейшего калькулятора.
Bash-скрипт — текстовый файл с последовательностью команд, исполняемый оболочкой. Начинается с #!/bin/bash (шебанг). Переменные: name="value", использование $name. Арифметика: $((a + b)), expr. Ввод: read a b. Условные операторы: if [ "$a" -eq "$b" ]; then ...; fi. Циклы: for i in {1..5}; do ...; done; while [ $i -le 10 ]; do ...; done. Функции: func_name() { ... }. Права на выполнение: chmod +x script.sh, запуск ./script.sh или bash script.sh. Калькулятор:

```bash
#!/bin/bash
echo "Введите два числа:"
read a b
echo "Введите оператор (+ - * /):"
read op
result=$((a $op b))
echo "Результат: $result"
```

Обработку деления на ноль и ввод можно добавить.

---

28. Ссылки Linux.
Жёсткая ссылка (hard link) — дополнительное имя для того же inode (файла). Создание: ln исходный_файл ссылка. Ограничения: нельзя на директории и на разные файловые системы. Удаление оригинала не удаляет данные, пока есть хотя бы одна жёсткая ссылка. Счётчик ссылок виден в ls -l. Символическая ссылка (symbolic link, symlink) — отдельный файл, содержащий путь к целевому объекту. Создание: ln -s цель ссылка. Может указывать на директории и на другие файловые системы. При удалении оригинала становится "битой". Отличие: жёсткая ссылка неотличима от оригинала (кроме inode), символическая — видна по l в начале прав и стрелке в ls -l. readlink — показывает путь симлинка.

---

29. Системные процессы exec, fork, vfork и clone.
fork() — создаёт дочерний процесс путём копирования родительского (копирование страниц памяти с механизмом copy-on-write). Возвращает 0 в дочернем, PID дочернего в родительском, -1 при ошибке. exec() (семейство: execl, execlp, execle, execv, execvp, execve) — заменяет текущий образ процесса новым, PID сохраняется. Не возвращает при успехе. vfork() — более старая версия, создаёт процесс без копирования памяти (блокирует родителя до exec или exit), используется только перед exec, сейчас редко. clone() — расширенная версия fork, позволяет тонко настроить, что разделять (память, файловые дескрипторы, пространства имён), используется для создания потоков (в Linux — pthread_create через clone).

---

30. МПК Linux. Каналы и Сигналы.
МПК (межпроцессное взаимодействие). Каналы (pipes): pipe(int fd[2]) создаёт однонаправленный канал — fd[0] для чтения, fd[1] для записи. Используется между родственными процессами (после fork). Для двусторонней связи нужны два канала. popen() — открывает канал для выполнения внешней программы. Именованные каналы (FIFO) — mkfifo для взаимодействия любых процессов через файловую систему. Сигналы — асинхронные уведомления процесса о событиях. Стандартные: SIGINT (Ctrl+C), SIGTERM (завершение), SIGKILL (безусловное завершение), SIGSTOP (остановка). Обработка: signal(SIGINT, handler) или sigaction (более гибкий). Можно отправлять сигналы через kill(pid, sig). Сигналы не могут передавать данные (кроме sigqueue с real-time сигналами). Используются для управления процессами и аварийных ситуаций.

---

Готово. Все 30 вопросов покрыты.