Данный репозиторий является конспектом самых разных источников:
Лекции Сергея Балакирева по языку C
Курс по языку C для первого курса МФТИ Татьяны Дербышевой и Татьяны Овсянниковой
CS50 (Гарвардский курс по основам программирования)
Программа-установщик компилятора minGW (Добавить в Path путь C:\MinGW\bin)
sudo apt update - обновление пакетов Linux;
sudo apt install cmake gcc clang gdb build-essential - установка:
- cmake - инструмент для автоматизации процесса сборки программного обеспечения,
- gcc - компилятор языка C,
- clang - альтернативный компилятор C,
- gdb - отладчик для программ,
- build-essential - инструмент для автоматизации процесса сборки программного обеспечения;
sudo apt install valgrind - установка утилиты для отладки и обнаружения утечек памяти;
valgrind --leack-check=full ./main - запуск проверки программы
Для запуска программы на C в PowerShell или bash ввести:
gcc -Wall -Wextra main.c
.\a
Чтобы задать свое имя исполняемому файлу:
gcc main.c -o main -Wall -Wextra -std=c99 -pedantic
-Wallвключает все стандартные предупреждения компилятора (предупреждения о неиспользуемых переменных, некорректных вызовах функций и тд.)-Wextraвключает дополнительные предупреждения (предупреждения о неиспользуемых аргументах функций, несоответствие типов указателей и тд.)-std=c99указывает стандарт языка C-pedanticактивирует проверку на соответствие стандарту языка C
- C/C++
- Code Runner (после установки программа компилируется нажатием CTRL + Alt + N)
В .vscode/tasks.json в список args добавить "-std=c99", после этого программа запускается CTRL + Shift + B
Стандартная библиотека языка C
| Тип данных | 16 бит | 32 бит | 64 бит | Описание | Диапазон |
|---|---|---|---|---|---|
| char | 1 | 1 | 1 | Символьный | [0; 255] или [-128; 127] |
| short[int] | 2 | 2 | 2 | Целочисленный | [-32768; 32767] |
| int | 2 | 4 | 4 | Целочисленный | [-32768; 32767] или [-2147483648; 2147483648] |
| long[int] | 4 | 4 | 8 | Целочисленный | [-2147483648; 2147483648] |
| long long[int] | 8 | 8 | 8 | Целочисленный | ... |
| float | 4 | 4 | 4 | Вещественный | ... |
| double | 8 | 8 | 8 | Вещественный | ... |
| long double | 10 | 10 | 10 | Вещественный | ... |
- unsigned - беззнаковый диапазон
- signed - знаковый диапазон
short, int, long, long long по-умолчанию signed
| Спецификатор | Описание |
|---|---|
| %d | Целое число со знаком в десятичной форме (приводится к int) |
| %i | Целое число в десятичной, 8ной или 16ной системах (приводится к int) |
| %u | Целое unsigned число в десятичной форме (приводится к unsigned int) |
| %o | Целое число в 8ной форме (приводится к int) |
| %x, %X | Целое число в 16ной форме (приводится к int) |
| %f, %e, %g, %F, %E, %G | Вещественное число (приводится к float) |
| %c | Символ в соответствии с текущей кодовой таблицей (приводится к char) |
| %s | Строка, читается до первого пробела, перевода строки или табуляции |
| h | %hd, %hi - для short int, %hx, %ho, hu - для unsigned short |
| hh | %hhd - для signed char, %hhu - для unsigned char |
| l | %ld, %li - для long int, %lf, %lg, %le - для double |
| L | %Lf, %Lg, %Le - для long double |
| ll (в стандарте C99) | %lld - для long long int, %llu - для unsigned long long |
| Цифры | Максимальная ширина ввода |
| * | Пропуск данных |
int num;
printf("Num: %d\n", num);int x;
int input = scanf("%d", &x);
printf("input = %d, value = %d\n", input, x);В переменную input будет помещено количество принятых функцией scanf() значений. В примере - 1.
С помощью &x происходит помещение введенных данных в переменную x.
| Операция | Обозначение | Приоритет (чем выше значение, тем приоритетнее операция) |
|---|---|---|
| ИЛИ | || | 1 |
| И | && | 2 |
| НЕ | ! | 3 |
#include <stdio.h> сначала происходит поиск в системных каталогах, затем в рабочем
#include "utils.h" сначала происходит поиск в рабочем каталоге, затем в системных
Использовать угловые скобки если необходимо подключить системные файлы, поставляемые с компилятором языка C, если подключаются собственные файлы, то использовать двойные кавычки.
| Директива | Описание |
|---|---|
#if |
Проверка произвольного условия |
#else |
Определение условия "иначе" |
#endif |
Завершение блока условия |
#ifdef |
Проверка на наличие макроимени в текущем модуле |
#ifndef |
Проверка на отсутствие макроимени в текущем модуле |
#elif |
Аналог else if, определение условия "иначе" |
#elifdef |
Проверка на наличие макроимени в условном блоке "иначе" |
#elifndef |
Проверка на отсутствие макроимени в условном блоке "иначе" |
for (инициализация; условие; обновление) { // тело цикла // операции, которые нужно выполнить на каждой итерации }
-
инициализация - определение или инициализация переменных, которые будут использоваться в цикле. Обычно этот блок выполняется только один раз в начале цикла.
-
условие - выражение, которое проверяется перед каждой итерацией цикла. Если условие истинно, то тело цикла выполняется. Если условие ложно, то цикл прекращается и исполнение переходит к следующей инструкции после цикла.
-
обновление - операция или изменение переменных, которые должны происходить на каждой итерации цикла. Этот блок выполняется после каждой итерации перед проверкой условия.
for(int value = 1000, value > 0, --value)
printf("%d", value);Цикл выполняется до тех пор, пока верно условие.
int num = 0;
while (1) {
num += 1;
printf("Number: %d\n", num);
}Цикл с постусловием. Это означает, что условие проверяется после каждой итерации цикла. Таким образом, тело цикла выполняется хотя бы один раз, даже если условие изначально ложное.
do { // тело цикла } while (условие);
int i = 1;
do {
printf("%d\n", i);
i++;
} while (i <= 10);Синтаксис switch/case:
switch(symbol) {
case 'a':
case 'A':
printf("Symbol A\n");
break;
case 'b':
case 'B':
printf("Symbol B\n");
break;
default:
printf("Any symbol\n");
}Указатель - это переменная, которая хранит адрес другой переменной, массива, функции и другой структуры данных в оперативной памяти. Указатели предназначены для динамического выделения блоков памяти и для оптимизации работы программы.
char num = 100;
char *nm; // объявление указателя для работы с типом char
nm = # // взятие адреса переменной num и присваивание его указателю nm
char x = *nm; // получение значения по адресу переменной nm
*nm = 200; // запись значения в ячейку, где расположена переменная numОбычно, если нужно передать изменяемое значение переменной в функцию или сохранить изменения в переменной после выполнения функции, используется указатель. Это необходимо, потому что передача аргументов в функцию в C является передачей по значению, что означает, что функция работает с копией переменной, а не с самой переменной.
Таким образом, если нужно, чтобы функция могла изменить значение переменной, передается указатель на эту переменную.
void increment(int *num) {
(*num)++;
}
int main() {
int num = 5;
printf("Before: %d\n", num);
increment(&num);
printf("After: %d\n", num);
return 0;
}В этом примере передается указатель на переменную num в функцию increment, которая увеличивает значение переменной на 1. Затем значение переменной num изменяется после выполнения функции.
Значение указателя изменяется на размер типа данных, для которого он определен.
int num = 10;
int *ptr_f = #
printf("%u\n", ptr_f);
ptr_f += 5;
printf("%u\n", ptr_f);Первый вывод: 898233836 Второй вывод: 898233856 Тип указателя int, int занимает 4 байта, 5 * 4 = 20. Значение адреса изменяется на 20.
void get_pointers(int* one, int* two)
{
int three = *one;
*one = *two;
*two = three;
}Экранирование и спецсимволы
| escape-последовательность | Код ASCII | Описание |
|---|---|---|
\n |
0x0A | Перевод на новую строку |
\r |
0x0D | Перенос каретки |
\t |
0x09 | Табуляция |
\' |
0x27 | Экранирование одинарной кавычки |
\" |
0x22 | Экранирование двойной кавычки |
\? |
0x3F | Экранирование знака вопроса |
\\ |
0x5C | Экранирование обратного слэша |
\0 |
0x00 | Символ конца строки |
\x<число> |
Символ с указанным шестнадцатиричным значением | |
\uxxxx |
Символ с кодом xxxx в UNICODE (в стандарте C99) |
assert выполняет выражение, если оно истинно, выполнение программы происходит без изменений. Если выражение ложно, то выполнение программы останавливается и выполняется диагностика.
sizeof выводит размер переменной или типа в байтах.
sizeof(int val);size_t является беззнаковым целочисленным типом данных, используемым для представления размеров объектов в памяти. Он выбирается таким образом, чтобы быть достаточно большим, чтобы вместить размер любого объекта в памяти. Обычно size_t имеет размер, достаточный для представления максимального возможного размера массива или объекта данных, который может быть выделен в памяти.
size_t широко используется вместе с операторами sizeof и в функциях, которые выполняют операции на памяти, таких как malloc, memcpy, strlen и другие, чтобы представить и возвращать размеры объектов в памяти.
Ключевые слова static и extern относятся к спецификаторам доступа переменных и функций.
static:
- Если
staticиспользуется с переменной внутри функции, то эта переменная становится локальной статической. Она сохраняет свое значение между вызовами функции и инициализируется только один раз. - Если
staticиспользуется с глобальной переменной или функцией, они становятся доступными только в текущем файле (имеют внутреннюю видимость). Такие переменные и функции не могут быть использованы или видимы из других файлов.
extern:
- Используется для объявления переменной или функции, которая определена в другом файле.
- Когда переменная или функция объявлена как
extern, компилятор знает, что она будет определена в другом месте и можно использовать без ошибки "повторного определения". - Также используется для доступа к глобальным переменным или функциям, объявленным в других модулях.
Ключевое слово enum используется для определения пользовательских наборов именованных констант, которые представляют собой список значений. Такое перечисление может использоваться для лучшей читаемости кода и упрощения работы с константами.
enum any_values
{
one = 1,
two = 2,
};
int main(void)
{
enum any_values enum_values = val;
switch(enum_values) {
case one:
puts("Value 1");
break;
case two:
puts("Value 2");
break;
return 0;
}
}Ключевое слово typedef используется для создания псевдонима для существующего типа данных. Это может быть полезно для упрощения именования сложных типов данных или улучшения портативности кода.
typedef unsigned int uint32_t;
В этом примере создается псевдоним uint32_t для типа unsigned int. Теперь можно использовать uint32_t вместо unsigned int для объявления переменных или функций.
Использование псевдонимов с помощью typedef может сделать код более понятным и гибким.
Структура - это составной тип данных, который позволяет объединить несколько переменных разных типов в одну логическую единицу. Структура определяется с помощью ключевого слова struct и может содержать одно или несколько полей (переменных), каждое из которых имеет свое имя и тип данных.
Пример объявления структуры в C:
struct person {
char name[50];
int age;
float height;
}Здесь объявляется структура person, которая содержит три поля: name типа char[50], age типа int и height типа float.
Чтобы создать переменную типа структуры, используется объявленное имя структуры, за которым следует имя переменной:
struct person person1;Затем можно получить доступ к полям структуры с помощью оператора точки:
strcpy(person1.name, "John");
person1.age = 30;
person1.height = 180.5;Также можно объявить структуру с помощью typedef, чтобы использовать созданный тип структуры, не указывая ключевое слово struct при объявлении переменных:
typedef struct {
char name[50];
int age;
float height;
} PERSON;Теперь можно создать переменные типа PERSON без использования struct:
PERSON person2;Структуры полезны для организации данных и создания пользовательских типов, которые могут содержать несколько связанных данных внутри себя.
| Режим | Описание |
|---|---|
| "r" | Открытие текстового файла только на чтение. |
| "w" | Открытие текстового файла только на запись. Если файл не существует, то он создается. Если существует, то все его прежнее содержимое удаляется. |
| "a" | Открытие текстового файла на дозапись (новые данные добавляются в конец файла). Если файл не существует, то он создается. |
| "r+" | Открытие текстового файла для чтения и записи одновременно. |
| "w+" | Открытие текстового файла для чтения и записи. Если файл не существует, то он содается. Если существует, то все содержимое удаляется. |
| "a+" | Открытие текстового файла для чтения и записи. Добавлять данные можно только после имеющегося содержимого. Читать можно все данные. Если файл не существует, то он создается. |
Ошибка "Segmentation fault" (Segmentation fault (core dumped)) возникает, когда программа пытается обратиться к памяти, которая не была выделена ей или к которой у нее нет доступа. Это может происходить, например, при обращении к недопустимому указателю, при выходе за границы массива или при доступе к памяти, которая была освобождена.
Возможные причины ошибки Segmentation fault:
- Недопустимая работа с указателями: попытка работать с нулевым указателем или освободить память, которая уже была освобождена.
- Выход за границы массива: при обращении к элементу массива за его границами.
- Недопустимые операции с памятью: попытка чтения или записи в неправильный адрес памяти.
- Неправильное использование указателей: использование указателей, указывающих в неправильное место памяти.
- Неправильное использование функций: передача неправильных аргументов функциям или неправильное использование возвращаемых значений.
Для исправления ошибки Segmentation fault необходимо тщательно проверить код, убедиться, что указатели корректно инициализированы, не выходят за границы памяти и не используются после ее освобождения.