# Разработка C++

# C: общее

[Исходный код из книги по C](https://www.pearsonglobaleditions.com)

P. J. Plauger’s The Standard C Library - желательная для прочтения.

Поиск нужного пакета:

```
apt-cache search manpages | grep ^manpag
```

Установка пакетов

```
sudo apt install manpages-ru manpages-ru-dev
```

Список доступных локалей

```
locale -a
```

Установка локали для man страницы

```
LANG=ru_RU.utf8 man 2 open
```

**Компиляция**

```
cc test.c
cc -0 test test.c
```

Вариант через gcc

```
gcc -Wall -Wextra -Werror -g program.c -o my_program
```

Добавить поиск в текущей директории (чтобы не писать ./)

```
export PATH=$PATH:.
```

**Базовое описание языка**

```c
1 // A first program in C.
2 #include <stdio.h>
3 
4 // function main begins program execution 
5 int main(void) {
6    printf("Welcome to C!\n");
7 }
```

\# - директивы препроцессора.

\*.h заголовочные файлы, в которых содержится описание функций и ссылки на файлы с их расположением.

**Константы**

добавляется слово const

```c++
const int var=7;
```

**Комментарии**

```c++
// comments
/* comments
long */
```

**Циклы**

Цикл for:

```c
for (j=0; j<9; j++) {}
```

Цикл с предусловием:

```c
while (i < 5) {}
```

Цикл с послеусловием:

```c
do {
    // тело цикла
} while (условие);
```

break — немедленный выход из цикла  
continue — переход к следующей итерации (пропуск остатка тела)

**Функции**

**Шаблон описания**

```c
тип_возвращаемого_значения название(входящие аргументы) {

}

int add(int a, int b);     // Функция, возвращающая int
void print_message();      // Функция, не возвращающая значение
int (*func_ptr)(int, int); // Указатель на функцию
```

void - отсутствие аргумента

Функция int main(void) - точка входа.

**Получение нескольких значений из функции**

Передача указателя

```c
#include <stdio.h>

// Функция возвращает int, а через указатели изменяет float
int get_values(int *integer_part, float *float_part) {
    *integer_part = 42;      // Записываем по адресу
    *float_part = 3.14;      // Записываем по адресу
    return 1;                // Статус: успешно
}

int main() {
    int num;
    float pi;
    
    int status = get_values(&num, &pi);  // Передаем адреса!
    
    printf("Целое: %d\n", num);   // 42
    printf("Дробное: %f\n", pi);  // 3.140000
    printf("Статус: %d\n", status); // 1
    
    return 0;
}
```

Структура

```c
#include <stdio.h>

// Определяем структуру для хранения двух значений
typedef struct {
    int integer;
    float decimal;
} TwoValues;

// Функция возвращает структуру
TwoValues get_values() {
    TwoValues result;
    result.integer = 42;
    result.decimal = 3.14;
    return result;  // Возвращаем структуру целиком
}

int main() {
    TwoValues values = get_values();
    
    printf("Целое: %d\n", values.integer);   // 42
    printf("Дробное: %f\n", values.decimal); // 3.140000
    
    return 0;
}
```

Глобальные переменные

Массив

```c
#include <stdio.h>

void get_values(int arr[]) {
    arr[0] = 42;
    arr[1] = 100;
    arr[2] = 255;
}

int main() {
    int values[3];
    get_values(values);  // Массив передается по указателю
    
    for (int i = 0; i < 3; i++) {
        printf("%d ", values[i]);  // 42 100 255
    }
    
    return 0;
}
```

# C: стандартные функции

**stdio.h**

Модуль ввода/вывода.

**printf()** Форматированный вывод на консоль.

```c
#include <stdio.h>

int printf(const char *format, ...);
```

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

<table border="1" id="bkmrk-%25c-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4-%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 20.2977%;"></col><col style="width: 79.8215%;"></col></colgroup><tbody><tr><td>%c</td><td>вывод отдельного символа (char)</td></tr><tr><td>%s</td><td>вывод строки (char\*)</td></tr><tr><td>%c</td><td>один символ</td></tr><tr><td>%d или %i</td><td>вывод целого числа со знаком (int)</td></tr><tr><td>%u</td><td>Целое число без знака (unsigned int) в десятичной системе</td></tr><tr><td>%f или %F</td><td>вывод числа с плавающей точкой (float, double)</td></tr><tr><td>%o</td><td>вывод в восьмеричной системе</td></tr><tr><td>%x или %X</td><td>вывод в шестнадцатеричной системе (строчными и заглавными буквами соответственно)</td></tr><tr><td>%%</td><td>Символ процента %</td></tr></tbody></table>

Модификаторы типов:  
h — для short int (%hd).  
l — для long int (%ld) или double (%lf).  
L — для long double (%Lf).

Управляющие последовательности внутри строки формата тоже обрабатываются:  
\\n — перевод на новую строку;  
\\t — табуляция;  
\\\\ — обратный слеш;  
\\" — двойная кавычка.

```c
printf("Привет, %s!", "мир");  // Выведет «Привет, мир!»
```

Дополнительные возможности форматирования

Флаги:

<table border="1" id="bkmrk---%D0%B2%D1%8B%D1%80%D0%B0%D0%B2%D0%BD%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BF%D0%BE-%D0%BB%D0%B5" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 18.9882%;"></col><col style="width: 81.131%;"></col></colgroup><tbody><tr><td>-</td><td>выравнивание по левому краю</td></tr><tr><td>+</td><td>принудительный вывод знака</td></tr><tr><td>\#</td><td>альтернативное представление для некоторых типов</td></tr></tbody></table>

Ширина поля:

%5d — число будет выведено в поле шириной в 5 символов (по умолчанию — выравнивание по правому краю).   
Точность: %.2f — число с плавающей точкой будет выведено с 2 знаками после запятой.   
Модификатор размера: %ld (длинное целое), %lld (длинное длинное).

```c
printf("%5d", 123);   // '   123'
printf("%-5d", 123);  // '123 ' (выравнивание по левому краю)
printf("%05d", 12);   // '00123' (ведущие нули)
printf("%+8.2f", 3.14159);  // ' +3.14' (знак и ширина)
```

**scanf()** Считывание данных из стандартного потока ввода (обычно с клавиатуры) и сохранение в переменных. Работает по принципу форматированного ввода: вы указываете шаблон (форматную строку), а функция интерпретирует введённые данные в соответствии с ним.

```c
int scanf(const char *format, ...);
```

Возвращает количество успешно прочитанных и присвоенных значений. Если происходит ошибка или конец файла достигнут, возвращается EOF. Однако если например вместо ожидаемой цифры будет введен символ, то он останется в буфере.

Передача адресов переменных  
Ключевой момент: функция scanf записывает данные в память, и ей нужно передать адреса переменных, куда эти данные сохранятся. Для этого перед именем переменной ставится амперсанд &amp; (операция взятия адреса).

```c
int num;
scanf("%d", &num); // Передаём адрес переменной num
```

Пример программы

```c
#include <stdio.h>

int main() {
   int age;
   printf("Введите возраст: ");
   if (scanf("%d", &age) != 1) {
      printf(stderr, "Ошибка ввода");
      return 1;
      }
   printf("Вы ввели: %d", age);
   return 0;
}
```

Интересные вещи возникают при неправильном вводе.

```c
#include <stdio.h>

int main() {
   int age;
   char my;
   printf("Введите возраст: ");
   if (scanf("%d", &age) != 1) {
      printf("Введено: %d\n", age);
      scanf("%c", &my);
      printf("Введено: %c\n", my);
      return 1;
      }
   printf("Вы ввели: %d\n", age);
   return 0;
}
```

Результат:

```bash
Введите возраст: tre
Введено: 32765
Введено: t
```

- Если переменная не инициализирована, в ней будет какой-то мусор (в данном случае 32765)
- После несоответствия формата ожидаемого ввода, переменная не меняется. То есть в age остается мусор.
- Ввод попадает в буфер, и только потом считывается scanf.
- Если в буфере будет больше элементов, чем ожидается, то в буфере останется остаток. Он будет считан при последующем вызове функции scanf.
- \\n считается символом, поэтому если последовательно получать число и символ, а ввести число и нажать Enter то во вторую переменную попадет \\n. То есть буфер после считывания не очищается.

# С: переменные

Переменные бывают локальные (в пределах функции) и глобальные. Глобальные определяются до функции main вне остальных функций.

Переменные можно просто объявлять и дополнительно инициализировать начальное значение. Размеры типов зависят от платформы и компилятора! Вывод размера:

```c
sizeof(имя_типа);
```

**Квалификаторы типов (модификаторы)**

Они не создают новые типы, но изменяют их свойства.  
const Значение нельзя изменить const int x = 5;  
volatile Значение может измениться внешне (прерывания, hardware) volatile int status;  
restrict Указатель — единственный способ доступа к данным (для оптимизации) int \*restrict ptr;  
signed Тип со знаком (по умолчанию для char не определено) signed char c;  
unsigned Тип без знака (только положительные числа) unsigned int u;

**Псевдонимы типов (typedef)**

Позволяет дать новое имя существующему типу.

```c
typedef int my_int;           // my_int — это псевдоним для int
typedef unsigned long ulong;  // ulong — псевдоним для unsigned long

my_int x = 10;                // То же, что int x = 10;
ulong y = 100;                // То же, что unsigned long y = 100;
```

Часто используется для упрощения сложных типов:

```c
typedef struct {
    char name[50];
    int age;
} Person;                    // Теперь можно писать Person p; вместо struct Person p;
```

**Базовые типы:**

<table border="1" id="bkmrk-integer-%28int%29-%D0%A6%D0%B5%D0%BB%D1%8B%D0%B5-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 12.5149%;"></col><col style="width: 33.0085%;"></col><col style="width: 12.8794%;"></col><col style="width: 41.5971%;"></col></colgroup><tbody><tr><td>void</td><td>Нет значений Означает "отсутствие типа"</td><td>Нет размера</td><td>  
</td></tr><tr><td>integer (int)</td><td>Целые числа, -2,147,483,648 … 2,147,483,647

Зависит от разрядности ОС!

</td><td>4</td><td>```c++
int var;
int var=7;
```

  
</td></tr><tr><td>unsigned int</td><td>Беззнаковое целое 0 … 4,294,967,295</td><td>4</td><td>  
</td></tr><tr><td>short</td><td>Числа со знаком -32,768 … 32,767</td><td>2</td><td>  
</td></tr><tr><td>unsigned short</td><td>Беззнаковое короткое целое 0 … 65,535</td><td>2</td><td>  
</td></tr><tr><td>long</td><td>  
</td><td>4 или 8 Зависит от платформы</td><td>  
</td></tr><tr><td>unsigned long</td><td>  
</td><td>  
</td><td>  
</td></tr><tr><td>long long</td><td>Очень длинное целое</td><td>8 байт</td><td>  
</td></tr><tr><td>unsigned long long</td><td>  
</td><td>8</td><td>  
</td></tr><tr><td>float</td><td>Дробные с точностью до 7 знака</td><td>4</td><td>```c++
float var_float = 12.45;
```

  
</td></tr><tr><td>double</td><td>Дробные с точностью 15 знаков</td><td>8</td><td>  
</td></tr><tr><td>long double</td><td>  
</td><td>10, 12 или 16 Зависит от платформы</td><td>  
</td></tr><tr><td>bool</td><td>Логическое</td><td>  
</td><td>```c++
bool vr = true;
```

  
</td></tr><tr><td>char</td><td>Символ

Желательно указывать явно signed / unsigned

</td><td>1</td><td>```c++
char n = 'g';
```

  
</td></tr><tr><td>unsigned char</td><td>Беззнаковый символ 0 … 255</td><td>1</td><td>  
</td></tr></tbody></table>

**Массивы**

Хранят несколько элементов одного типа подряд в памяти. Из базовых.

```c
int numbers[10];           // Массив из 10 целых чисел
char name[50];             // Массив из 50 символов
double matrix[3][3];       // Двумерный массив (матрица 3x3)
```

**Указатели (pointer)**

Хранят адрес в памяти, где находится значение. Для обращения по адресам необходимы:

1. Возможность находить адрес, по которому в памяти располагается та или иная переменная;
2. Место, где этот адрес можно было бы сохранить для дальнейшей работы;
3. Возможность обращаться к ячейке памяти по ее адресу.

Операция получения адреса переменной &amp; (амперсанд). Хранятся адреса переменных тоже в переменных, в специальном типе,   
 переменные указатели. Количество указательных типов, равно количеству типов данных. Объявление переменной-указателя

```c
int *ptr, i;               // Указатель на int и обычный int
char *str;                 // Указатель на char (строка)
void *generic;             // Универсальный указатель (на любой тип)
```

Для изменения / получения данных из адреса оператор разыменовывания \* но в операциях с переменной:

```c
char i = 0, *p; 
p = &i;
*p = 2; 
i = i + 2; 
printf("%d %d", *p, i);
```

Но если попытаться напрямую изменить адрес, например p=p+2; то будет ошибка (такого адреса нет).

В ячейке памяти хранится адрес первой ячейки памяти. Тип переменной указывается для понимания, сколько байт нужно считывать.

**Перечисления (enum)**

Определяют набор именованных целочисленных констант.

```c
enum Color {
    RED,    // 0
    GREEN,  // 1
    BLUE    // 2
};

enum Color favorite = GREEN;
```

**Структуры**

Объединяют разные типы в один.

```c
struct Person {
    char name[50];
    int age;
    float height;
};

struct Person person1;
```

**Объединения (union)**

Как структура, но все поля используют одну и ту же область памяти (экономят память).

```c
union Data {
    int i;
    float f;
    char str[20];
};

union Data data;           // data.i, data.f, data.str занимают одну память
```

**Преобразование типов.**

Бывают явные (делает программист) и неявные (компилятор). Неявные делятся на:

1. При присваивании результата вычисления переменной, тип результата к преобразуется к типу переменной. Округление происходит не по математическим законам, а путем отбрасывания значения.
2. При вычислении выражения операнды каждой операции преобразуются к какому-то одному типу (если они различаются), при этом результат операции будет того же типа. Правило «меньший» тип к «большему», для минимизации потерю информации. Операции преобразования сначала выполняются от самого глубокого до внешнего, одного приоритета слева направо.

```c
float c = 5/6;
```

5 и 6 - целочисленные, поэтому результат целочисленный. И равен 0. Только после этого он преобразуется в float, но уже поздно. В с будет 0. А если сделать один операнд с плавающей точкой - второй преобразуется во float и дробная часть не потеряется.

```c
float c = 5.0/6;
```

Пример приоретизации операций:

```c
2 / 4 * 3,14 
// результат 0, так как одноуровневые операции слева направо
```

Явное преобразование:

```c
double result = (double)a / b;
```