Данный репозиторий является конспектом самых разных источников:
Лекции Сергея Балакирева по языку 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
выполняет выражение, если оно истинно, выполнение программы происходит без изменений. Если выражение ложно, то выполнение программы останавливается и выполняется диагностика.
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 необходимо тщательно проверить код, убедиться, что указатели корректно инициализированы, не выходят за границы памяти и не используются после ее освобождения.