Перейти к содержимому

Типы данных в C/C++ [2.2]

Типы данных в C/C++ [2.2]

В этом уроке мы разбираем типы данных в C++ и то, как с ними работать в робототехнике. Вы узнаете, что такое статическая типизация, какие бывают фундаментальные типы, как правильно инициализировать переменные, зачем нужны const и constexpr, и как работает auto. В конце есть практические задачи и чек-лист самопроверки.

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

Содержание

1. Цели урока

  • Понять, что такое статическая типизация и почему она полезна.
  • Познакомиться с фундаментальными типами данных в C++.
  • Освоить способы инициализации переменных и понять, что такое narrowing (сужение типов).
  • Научиться оценивать размеры и диапазоны типов через sizeof, signed/unsigned.
  • Понять природу ошибок округления у float/double и как с ними работать.
  • Разобраться в const, constexpr и auto.
Что важно запомнить: тип данных выбирают под задачу. Для робота это часто "точность измерений", "диапазон значений" и "скорость вычислений".
^ К оглавлению

2. Статическая типизация в C++

Статическая типизация -- это подход, при котором переменная связывается с типом хранимого значения в момент объявления, и тип не может быть изменен позже. Компилятор проверяет типы заранее и останавливает сборку, если вы пытаетесь сделать невозможное (например, записать дробь в int без явного преобразования).

int speed = 100;     // speed -- целое число
// speed = 3.14;      // так нельзя без преобразования: тип int не хранит дробную часть

Фундаментальные типы данных

Фундаментальные типы -- это базовые типы языка, из которых строится все остальное.

КатегорияТипЗначениеПример
Логические bool true/false true
Символьные char ASCII-коды символов 'a'
Плавающая точка float, double, long double Дробные числа 2.7
Целые short, int, long, long long Целые числа 128
Ничего void Нет значения (нет)
^ К оглавлению

3. Инициализация переменных

Инициализация -- это задание первого значения переменной. Не путайте с присваиванием: присваивание меняет значение уже существующей переменной.

4 способа инициализации

int var1 = 5;  // копирующая инициализация
int var2(5);   // прямая инициализация
int var3{5};   // uniform-инициализация (рекомендуется)
int var4{};    // инициализация значением по умолчанию (для int будет 0)

Компактная запись:

int a = 5, b = 6;
int c(7), d(8);
int e{9}, f{10};

Сужение типов (narrowing)

Определение: narrowing -- это преобразование типа, при котором можно потерять данные. Пример: преобразование 3.14 (double) в int приводит к потере дробной части.
int x = 3.14;   // компилируется, но x станет 3 (дробная часть потеряна)

// int y{3.14}; // обычно не компилируется: фигурные скобки защищают от narrowing

Почему это важно для робота: если вы храните измерение с датчика (например, 0.1 метра) в int, вы потеряете дробную часть, и управление станет грубым или неверным.

Типичные ошибки

Ошибка: переменная не инициализирована

int a;          // значение неопределено
// std::cout << a;  // может вывести что угодно

Исправление: задавайте начальное значение, например int a{}; или int a = 0;.

^ К оглавлению

4. Размеры и диапазоны типов

Размер типа зависит от компилятора и архитектуры. Стандарт C++ задает минимальные размеры, но реальные значения могут отличаться. Проверить размер можно оператором sizeof.

Оператор sizeof

sizeof возвращает размер типа или переменной в байтах.

#include

int main() {
    std::cout << "int: " << sizeof(int) << " bytes\n";
    std::cout << "double: " << sizeof(double) << " bytes\n";
    return 0;
}

signed и unsigned

Целочисленные типы бывают знаковыми (signed) и беззнаковыми (unsigned). У знаковых есть отрицательные числа, у беззнаковых -- нет.

Диапазоны (если n бит):
signed: от -2^(n-1) до 2^(n-1)-1
unsigned: от 0 до 2^n-1

char и ASCII

Тип char -- особый случай. Формально это целочисленный тип, который хранит ASCII-код символа. ASCII -- таблица кодов от 0 до 127 для символов английского алфавита и некоторых служебных знаков. Символы в C++ пишутся в одинарных кавычках.

#include <iostream>

int main() {
    char ch = 'a';
    int code = ch; // неявно получаем числовой код
    std::cout << "ch: " << ch << "\n";
    std::cout << "code: " << code << "\n"; // обычно 97
    return 0;
}

Типичные ошибки

Ошибка: переполнение целого типа

Переполнение -- это ситуация, когда значение выходит за диапазон типа и начинает "переворачиваться". На роботе это может дать отрицательную скорость или странные показания.

// Пример идеи (конкретное поведение зависит от типа и платформы):
// unsigned char u = 255;
// u = u + 1; // может стать 0
^ К оглавлению

5. Числа с плавающей точкой: float и double

Что такое IEEE 754

IEEE 754 -- стандарт, который описывает, как компьютер хранит числа с плавающей точкой. Внутри это не "дробь как на бумаге", а двоичное представление с ограниченной точностью.

ТипМин. размерТочность (приблизительно)
float 4 байта от 6 до 9 цифр
double 8 байт от 15 до 18 цифр
long double 8, 12 или 16 байт зависит от платформы

Ошибки округления

Некоторые десятичные числа (например, 0.1) не представимы точно в двоичной системе, поэтому возникают небольшие ошибки. Они накапливаются при сложении и могут влиять на фильтры, ПИД-регуляторы и интеграторы.

#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::setprecision(17);

    double d = 0.1;
    std::cout << d << "\n"; // 0.10000000000000001 (примерно)

    double s = 0.0;
    for (int i = 0; i < 10; ++i) s += 0.1;
    std::cout << s << "\n"; // 0.99999999999999989 (примерно)

    return 0;
}

nan и inf

В IEEE 754 существуют специальные значения: inf (бесконечность) и nan (не число). Они появляются при делении на ноль и других некорректных операциях.

#include <iostream>

int main() {
    double zero = 0.0;

    double posinf = 5.0 / zero;
    double neginf = -5.0 / zero;
    double nan = zero / zero;

    std::cout << posinf << "\n";
    std::cout << neginf << "\n";
    std::cout << nan << "\n";

    return 0;
}

Типичные ошибки

Ошибка: сравнение double через ==

Из-за ошибок округления два числа, которые "должны быть равны", могут отличаться в последних знаках. Правильнее сравнивать с допуском (epsilon).

#include <cmath>

bool almostEqual(double a, double b, double eps) {
    return std::abs(a - b) < eps;
}
^ К оглавлению

6. const, constexpr и auto

6.1. const и constexpr

const и constexpr используются для констант. Константа -- это значение, которое нельзя изменить после инициализации.

  • const -- константа времени выполнения: значение фиксируется при запуске, менять нельзя.
  • constexpr -- константа времени компиляции: значение должно быть известно компилятору заранее.
constexpr double gravity(9.8);
const double mass{90};
Практический смысл: constexpr позволяет компилятору заранее подставить значение и оптимизировать код. const полезен, когда значение задается один раз и не должно меняться (например, масса робота или параметр калибровки).

6.2. Ключевое слово auto

auto просит компилятор вывести тип переменной по правой части инициализации. Это удобно, когда тип очевиден или слишком длинный.

auto x = 4.0;     // double
auto y = 3 + 4;   // int

6.3. Типичные ошибки

Ошибка: const без инициализации

// const int a; // ошибка: константа должна быть инициализирована
const int a = 10;  // правильно

Ошибка: auto скрывает неожиданный тип

Иногда auto выводит не то, что вы ожидали. Привычка: смотреть на литералы справа и помнить правила.

auto a = 1;   // int
auto b = 1.0; // double
^ К оглавлению

7. Практика: типовые задачи (с решениями)

Базовые задачи по типам

Задача 1: Таблица sizeof

Выведите размер нескольких типов в байтах.

#include <iostream>

int main() {
    std::cout << "bool: " << sizeof(bool) << " bytes\n";
    std::cout << "char: " << sizeof(char) << " bytes\n";
    std::cout << "int: " << sizeof(int) << " bytes\n";
    std::cout << "double: " << sizeof(double) << " bytes\n";
    return 0;
}

Задача 2: char как число

Выведите символ и его код.

#include <iostream>

int main() {
    char ch = 'b';
    std::cout << "ch: " << ch << "\n";
    std::cout << "code: " << static_cast(ch) << "\n";
    return 0;
}

Задачи по float/double

Задача 3: Ошибка округления

Выведите 0.1 с высокой точностью.

#include <iostream>
#include <iomanip>

int main() {
    std::cout << std::setprecision(17);
    double d = 0.1;
    std::cout << d << "\n";
    return 0;
}

Задача 4: nan и inf

Получите inf и nan и выведите их.

#include <iostream>

int main() {
    double zero = 0.0;
    std::cout << (5.0 / zero) << "\n";
    std::cout << (zero / zero) << "\n";
    return 0;
}

Задачи по const/constexpr/auto

Задача 5: const и constexpr

Создайте одну константу const и одну constexpr.

constexpr double g = 9.8;
const double mass = 90.0;

Задача 6: auto

Проверьте, какой тип выводится для 4.0 и для 3 + 4.

auto x = 4.0;   // double
auto y = 3 + 4; // int
^ К оглавлению

8. Чек-лист самопроверки знаний

Отметьте пункты, которые вы действительно понимаете и можете применить без подсказок.

+/-НавыкПроверка
Статическая типизация Могу объяснить, почему тип задается при объявлении и не меняется
Фундаментальные типы Знаю, какие типы относятся к bool, char, целым и дробным
Инициализация Умею использовать =, (), {}, {} и понимаю, чем они отличаются
Narrowing Понимаю, что такое сужение типов и почему {} помогает ловить ошибки
sizeof Могу вывести размер типа/переменной и объяснить, почему размеры зависят от платформы
signed/unsigned Могу объяснить диапазоны: signed от -2^(n-1) до 2^(n-1)-1, unsigned от 0 до 2^n-1
float/double Понимаю, почему бывают ошибки округления, и почему опасно сравнение через ==
nan/inf Знаю, откуда берутся nan и inf и почему их надо учитывать в расчетах
const/constexpr Отличаю константу времени выполнения от константы времени компиляции
auto Понимаю, что auto выводит тип из инициализатора и умею проверять ожидания
^ К оглавлению

Ссылки по теме

Среда, 25 февраля 2026
Типы данных в C/C++ [2.2]