Типы данных в C и C++: базовые типы, размеры и диапазоны

Типы данных в C и C++: базовые типы, размеры и диапазоны Полезное

Тип данных в C и C++ — это свойство переменной, которое задает, какие значения она может хранить, сколько памяти под нее выделяется и какие операции с ней разрешены. Оба языка статически и строго типизированы: тип указывают при объявлении, и он не меняется во время работы программы.

Ниже разберем базовые типы семейства C (char, int, float, double, bool), как узнать их размер через sizeof, чем отличаются знаковые и беззнаковые целые, и зачем нужны enum, void и типы фиксированной ширины. Все примеры прогнаны и выводы в тексте — реальные (компилятор Clang, 64-битная сборка, macOS/Linux, проверено 17.09.2026).

Семейство C: где действуют эти типы

Речь о языках C и C++. Их система базовых типов почти совпадает: C++ вырос из C и сохранил его числовые типы, добавив свои (например, bool и строгие перечисления). Родственные Objective-C и C# синтаксически похожи, но их типы устроены иначе, и здесь мы их не рассматриваем.

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

Базовые типы данных

Базовые (встроенные) типы — те, что понимает сам компилятор без подключения библиотек. Их удобно держать в одной таблице: по ней видно, что и сколько занимает.

Тип Обычный размер Что хранит Типичный диапазон
char 1 байт символ или маленькое целое -128..127 либо 0..255
short 2 байта короткое целое -32 768..32 767
int 4 байта целое общего назначения -2 147 483 648..2 147 483 647
long 4 или 8 байт большое целое зависит от платформы
long long 8 байт очень большое целое примерно +-9,2 * 10^18
float 4 байта дробное, ~6-7 значащих цифр примерно +-3,4 * 10^38
double 8 байт дробное, ~15-16 значащих цифр примерно +-1,8 * 10^308
bool 1 байт true или false только два значения

Важна оговорка о размерах: стандарт C и C++ не фиксирует их жестко, а задает лишь минимумы и соотношения (char <= short <= int <= long). Приведенные числа верны для распространенных 64-битных компиляторов, но long, например, занимает 8 байт на Linux и macOS и 4 байта на Windows. Поэтому размер всегда лучше не запоминать, а измерять — об этом следующий раздел.

Коротко про отдельные типы:

  • char хранит один байт. Его чаще всего используют для символов в кодировке ASCII, но по сути это целое число, и его можно складывать и сравнивать.
  • int — целочисленный тип по умолчанию, им объявляют счетчики и обычные числа.
  • float и double хранят дробные числа с плавающей точкой. double (двойная точность) точнее и потому используется по умолчанию для вещественных вычислений.
  • bool — логический тип из C++, принимает только true или false. В чистом C для него есть _Bool и заголовок <stdbool.h>.

Размер типа и оператор sizeof

Чтобы не гадать, сколько байт занимает тип на конкретной машине, есть оператор sizeof. Он возвращает размер в байтах и вычисляется на этапе компиляции. Вот полная программа на C, которую можно скопировать и запустить:

#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void) {
    printf("char:   %zu byte,  %d..%d\n", sizeof(char), CHAR_MIN, CHAR_MAX);
    printf("int:    %zu bytes, %d..%d\n", sizeof(int), INT_MIN, INT_MAX);
    printf("long:   %zu bytes\n", sizeof(long));
    printf("float:  %zu bytes, ~%d значащих цифр\n", sizeof(float), FLT_DIG);
    printf("double: %zu bytes, ~%d значащих цифр\n", sizeof(double), DBL_DIG);
    return 0;
}

Вывод программы:

char:   1 byte,  -128..127
int:    4 bytes, -2147483648..2147483647
long:   8 bytes
float:  4 bytes, ~6 значащих цифр
double: 8 bytes, ~15 значащих цифр

Диапазоны здесь взяты не наугад: макросы INT_MIN, INT_MAX из <limits.h> и FLT_DIG, DBL_DIG из <float.h> знают точные границы для текущего компилятора. Это надежнее, чем держать числа в голове.

Обратите внимание на строку char: здесь он оказался знаковым (-128..127), но знаковость char стандартом не задана и на некоторых платформах он беззнаковый (0..255). Если знак важен, пишут явно signed char или unsigned char.

Знаковые и беззнаковые целые

У каждого целого типа есть две версии: знаковая (signed, по умолчанию) хранит и отрицательные, и положительные числа, беззнаковая (unsigned) — только неотрицательные, зато вдвое больший положительный предел. Например, unsigned int вмещает 0..4 294 967 295 вместо -2 147 483 648..2 147 483 647 у int.

Здесь легко поймать ошибку. Беззнаковые не умеют быть отрицательными: при вычитании ниже нуля результат не уходит в минус, а заворачивается к максимальному значению. И при сравнении знакового с беззнаковым знаковое молча превращается в беззнаковое. Покажем это на C++:

#include <iostream>
using namespace std;

int main() {
    unsigned int u = 0;
    u = u - 1;                 // 0 - 1 в беззнаковом типе
    cout << "u = " << u << "\n";

    int a = 3, b = 5;          // a - b = -2
    unsigned int c = 10;
    cout << "a - b = " << (a - b) << "\n";
    cout << "(a - b) < c ? " << ((a - b) < c) << "\n";  // ждем 1, получаем 0
    return 0;
}

Фактический вывод:

u = 4294967295
a - b = -2
(a - b) < c ? 0

Разберем сюрприз в последней строке. Само по себе a - b равно -2, это видно во второй строке вывода. Но в сравнении (a - b) < c слева знаковое число, справа беззнаковое c, поэтому -2 приводится к unsigned и становится тем самым 4 294 967 295. Оно больше 10, и результат сравнения — 0 (ложь), хотя по смыслу -2 меньше 10.

Исправление простое: не смешивать знаковое и беззнаковое в одном сравнении. Здесь достаточно объявить c как int, и (a - b) < c вернет ожидаемую 1. Общий совет — для обычной арифметики брать знаковый int, а unsigned использовать осознанно (битовые маски, размеры контейнеров), помня про заворот.

Перечисления и void

Кроме базовых чисел, в семействе C есть еще два типа, которые часто встречаются новичкам.

Перечисление (enum) — это набор именованных целочисленных констант. Оно делает код читаемее: вместо чисел 0, 1, 2 пишут понятные имена. По умолчанию первая константа равна 0, каждая следующая на единицу больше.

void — это «отсутствие типа». Он не хранит значение и применяется в трех ролях: функция ничего не возвращает (void f()), у функции нет параметров (в C пишут int f(void)), либо как «указатель на что угодно» (void*).

Заодно посмотрим на типы фиксированной ширины из заголовка <cstdint> (в C — <stdint.h>). В отличие от int, чей размер плавает между платформами, int32_t гарантированно занимает ровно 4 байта везде, а int8_t — один. Это удобно, когда важен точный размер: сетевые протоколы, форматы файлов, работа с оборудованием.

#include <iostream>
#include <cstdint>
using namespace std;

enum Level { LOW, MEDIUM, HIGH };   // 0, 1, 2

int main() {
    Level l = MEDIUM;
    cout << "MEDIUM = " << l << "\n";
    cout << "int8_t:  " << sizeof(int8_t)  << " byte\n";
    cout << "int32_t: " << sizeof(int32_t) << " bytes\n";
    cout << "int64_t: " << sizeof(int64_t) << " bytes\n";
    bool flag = true;
    cout << "bool:    " << sizeof(flag) << " byte, value " << flag << "\n";
    return 0;
}

Вывод программы:

MEDIUM = 1
int8_t:  1 byte
int32_t: 4 bytes
int64_t: 8 bytes
bool:    1 byte, value 1

MEDIUM действительно равен 1 (вторая по счету константа), а bool при выводе печатается как 1 для true и 0 для false. Размеры int8_t, int32_t, int64_t совпадают с числом в имени и не зависят от платформы — в этом их смысл.

Как выбрать тип под задачу

Короткий ориентир, а не полный каталог:

  • Целое общего назначения (счетчики, индексы, обычные числа) — int.
  • Число больше двух миллиардов — long long (или int64_t).
  • Дробное число, деньги-суммы для расчетов — double; float берут ради экономии памяти в больших массивах.
  • Один символ или байт — char; текст — массив char или std::string в C++.
  • Логический флаг — bool (C++).
  • Точный размер поля в байтах — типы из <cstdint> (int32_t и подобные).

Общее правило: берите самый простой тип, которого хватает по диапазону, и не смешивайте знаковые с беззнаковыми в одном выражении.

Выводы

  • Тип данных в C и C++ задает набор возможных значений, размер в памяти и допустимые операции; тип фиксируется при объявлении и не меняется.
  • Базовые типы: целые (char, short, int, long, long long), дробные (float, double) и логический bool; их размеры стандарт задает не жестко, а через минимумы и соотношения.
  • Размер типа надежнее измерять оператором sizeof, а границы брать из <limits.h>/<float.h>, а не запоминать.
  • Знаковые и беззнаковые целые нельзя бездумно смешивать: беззнаковое вычитание заворачивается, а в сравнении знаковое приводится к беззнаковому.
  • Для точного размера поля берут типы фиксированной ширины из <cstdint>.

Где применяется и что учить дальше

Понимание типов данных — это фундамент любой программы на C или C++: от него зависят и корректность вычислений, и расход памяти, и скорость. Ошибка в выборе типа (переполнение int, потеря точности float, заворот unsigned) — одна из самых частых причин трудноуловимых багов, поэтому тему разбирают одной из первых.

Освойте тему на практике

Если хочется освоить C++ системно — от типов и указателей до классов и работы с памятью — посмотрите курс C++ Developer. Basic. Оценить формат и уровень до старта помогают бесплатные открытые уроки Otus — живые занятия с преподавателями.

Смежные темы: Структуры и типы данных, Кодировка данных: кириллица, Языки программирования: характеристика, описание, виды.

FAQ

Чем float отличается от double, если оба дробные? Разрядностью и точностью: float занимает 4 байта и хранит около 6-7 значащих цифр, double — 8 байт и около 15-16. Для расчетов по умолчанию берут double, float — когда критична экономия памяти.

Почему sizeof(char) всегда равен 1, а размеры других типов измеряют в этих единицах? По стандарту sizeof(char) равен 1 по определению — это «единица измерения» памяти в C и C++. Размеры остальных типов выражаются в таких же единицах, то есть в числе байт, занимаемых одним char.

Можно ли хранить в char русскую букву? Одна кириллическая буква в кодировке UTF-8 занимает два байта, а char — это один байт, поэтому целиком она в один char не помещается. Русский текст хранят в массиве char (последовательности байт) или в строковом типе.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)