Pull to refresh

Вся правда о целочисленных типах в C

C *
Sandbox
Для начала несколько вопросов:

  1. Тип char по умолчанию знаковый или нет? А int?
  2. Законно ли неявное приведение (signed char *) к (char *)? А то же для int?
  3. Сколько бит в unsigned char?
  4. Какое максимальное число гарантированно можно поместить в int? А минимальное?
  5. Тип long определённо больше, чем char, не так ли?

Разумеется, экспериментально искать ответы на эти вопросы с помощью вашего любимого компилятора в вашей любимой системе на вашем любимом компьютере1) — не лучшая идея. Мы говорим о стандарте языка (С99 и новее).

Если вы уверенно сможете правильно ответить на эти вопросы, тогда эта статья не для вас. В противном случае десять минут, потраченные на её чтение, будут весьма полезны.

Предположу, что вы ответили
  1. Знаковые оба.
  2. Законны оба.
  3. 8.
  4. 2147483647. -2147483648.
  5. Конечно, Кэп.


А правильные ответы такие
  1. char — не регламентируется, int — знаковый.
  2. Для int — законно, а для char — нет.
  3. Не менее 8.
  4. 32767. -32767
  5. Вообще говоря, нет.




Про signed и unsigned


Все целочисленные типы кроме char, по умолчанию знаковые (signed).

С char ситуация сложнее. Стандарт устанавливает три различных типа: char, signed char, unsigned char. В частности, указатель типа (signed char *) не может быть неявно приведён к типу (char *).

Хотя формально это три разных типа, но фактически char эквивалентен либо signed char, либо unsigned char — на выбор компилятора (стандарт ничего конкретного не требует).

Подробнее про char я написал в комментариях.

О размере unsigned char


Тип unsigned char является абстракцией машинного байта. Важность этого типа проявляется в том, что С может адресовать память только с точностью до байта. На большинстве архитектур размер байта равен 8 бит, но бывают и исключения. Например, процессоры с 36-битной архитектурой как правило имеют 9-битный байт, а в некоторых DSP от Texas Instruments байты состоят из 16 или 32 бит. Древние архитектуры могут иметь короткие байты из 4, 5 или 7 бит.

Стандарт С вынужден отказаться от допотопных архитектур и требует, чтобы байты были как минимум 8-битные. Конкретное значение (CHAR_BIT2)) для данной платформы записано в заголовочном файле limits.h.

Размеры целочисленных типов в С


C переносимый, поэтому в нём базовые целочисленные типы (char, short, int и др.) не имеют строго установленного размера, а зависят от платформы. Однако эти типы не были бы переносимы, если бы
их размеры были совершенно произвольные: стандарт устанавливает минимальные диапазоны принимаемых значений для всех базовых целочисленные типов. А именно,

  • signed char: -127...127 (не -128...127; аналогично другие типы)
  • unsigned char: 0...255 (= 28−1)
  • signed short: -32767...32767
  • unsigned short: 0...65535 (= 216−1)
  • signed int: -32767...32767
  • unsigned int: 0...65535 (= 216−1)
  • signed long: -2147483647...2147483647
  • unsigned long: 0...4294967295 (= 232−1)
  • signed long long: -9223372036854775807...9223372036854775807
  • unsigned long long: 0...18446744073709551615 (= 264−1)

Стандарт требует, чтобы максимальное значение unsigned char было 2CHAR_BIT−1 (см. предыдущий пункт).

Стандарт требует sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long). Таким образом, вполне законны ситуации типа sizeof(char)=sizeof(long)=32. Для некоторых DSP от Texas Instruments так и есть.

Конкретные значения этих диапазонов для данной платформы указаны заголовочном файле limits.h.

Новые типы в С99


После того, как C99 добавил тип long long, целочисленных типов и путаницы стало ещё больше. Чтобы навести порядок, стандарт ввёл заголовочный файл stdint.h, где определяются типы вроде int16_t (равно 16 бит), int_least16_t (минимальный тип, способный вместить 16 бит), int_fast16_t (по крайней мере 16 бит, работа с этим типом наиболее быстрая на данной платформе) и т. п.

least- и fast-типы фактически являются заменой рассмотренных выше типов int, short, long и т. п. только вдобавок дают программисту возможность выбора между скоростью и размером.

От типов вроде int16_t, со строгим указанием размера, страдает переносимость: скажем, на архитектуре с 9-битным байтом может просто не найтись 16-битного регистра. Поэтому стандарт тут явно говорит, что эти типы опциональны. Но учитывая, что какой бы код вы ни писали, чуть менее чем во всех случаях целевая архитектура фиксирована даже в худшем случае с точностью до семейства (скажем, x86 или AVR), внутри которого, размер байта не может вдруг поменяться, то переносимость фактически сохраняется. Более того, типы вроде int16_t оказались даже более популярными, чем int_least16_t и int_fast16_t, а при низкоуровневом программировании (микроконтроллеры, драйверы устройств) и подавно, ибо там зачастую неопределённость размера переменной просто непозволительна.



1) Для удобства тройку архитектура+ОС+компилятор далее будем называть просто платформой.
2) Этот макрос правильнее было бы назвать UCHAR_BIT, но по причинам совместимости он называется так, как называется.
Tags:
Hubs:
Total votes 74: ↑69 and ↓5 +64
Views 126K
Comments 54
Comments Comments 54