Вся правда о целочисленных типах в C / Habr
Для начала несколько вопросов:- Тип
char
по умолчанию знаковый или нет? Аint
? - Законно ли неявное приведение
(signed char *)
к(char *)
? А то же дляint
? - Сколько бит в
unsigned char
? - Какое максимальное число гарантированно можно поместить в
int
? А минимальное? - Тип
long
определённо больше, чемchar
, не так ли?
Разумеется, экспериментально искать ответы на эти вопросы с помощью вашего любимого компилятора в вашей любимой системе на вашем любимом компьютере1) — не лучшая идея. Мы говорим о стандарте языка (С99 и новее).
Если вы уверенно сможете правильно ответить на эти вопросы, тогда эта статья не для вас. В противном случае десять минут, потраченные на её чтение, будут весьма полезны.
Предположу, что вы ответили- Знаковые оба.
- Законны оба.
- 8.
- 2147483647. -2147483648.
- Конечно, Кэп.
А правильные ответы такие
char
— не регламентируется,int
— знаковый.- Для
int
— законно, а дляchar
— нет. - Не менее 8.
- 32767. -32767
- Вообще говоря, нет.
Про 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_BIT
2)) для данной платформы записано в заголовочном файле limits.h
.
Размеры целочисленных типов в С
C переносимый, поэтому в нём базовые целочисленные типы (
char
, short
, int
и др.) не имеют строго установленного размера, а зависят от платформы. Однако эти типы не были бы переносимы, если бы signed char:
-127…127 (не -128…127; аналогично другие типы)unsigned char
: 0…255 (= 28−1)signed short
: -32767…32767unsigned short
: 0…65535 (= 216−1)signed int
: -32767…32767unsigned int
: 0…65535 (= 216−1)signed long
: -2147483647…2147483647unsigned long
: 0…4294967295 (= 232−1)signed long long
: -9223372036854775807…9223372036854775807unsigned 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-типы фактически являются заменой рассмотренных выше типов
, short
, long
и т. п. только вдобавок дают программисту возможность выбора между скоростью и размером.
От типов вроде int16_t
, со строгим указанием размера, страдает переносимость: скажем, на архитектуре с 9-битным байтом может просто не найтись 16-битного регистра. Поэтому стандарт тут явно говорит, что эти типы опциональны. Но учитывая, что какой бы код вы ни писали, чуть менее чем во всех случаях целевая архитектура фиксирована даже в худшем случае с точностью до семейства (скажем, x86 или AVR), внутри которого, размер байта не может вдруг поменяться, то переносимость фактически сохраняется. Более того, типы вроде int16_t
оказались даже более популярными, чем int_least16_t
и int_fast16_t
, а при низкоуровневом программировании (микроконтроллеры, драйверы устройств) и подавно, ибо там зачастую неопределённость размера переменной просто непозволительна.
1) Для удобства тройку архитектура+ОС+компилятор далее будем называть просто платформой.
2) Этот макрос правильнее было бы назвать
UCHAR_BIT
, но по причинам совместимости он называется так, как называется.habr.com
Типы данных
Основные типы данных Турбо Паскаля имеют соответствующие эквиваленты в Турбо Си. Однако Си имеет как значительно большее разнообразие типов данных с широким выбором числовых диапазонов для значений целых и с плавающей точкой, так и спецификаторов signed и unsigned (со знаком и без знака). В Си по сравнению с Паскалем отсутствуют типы: диапазон, логический, процедурный (вместо него есть указатель на функцию), строковый, множество.
Кроме того, в Си в отличие от Паскаля, есть правила умолчания. Так, если забыли описать переменную, то по умолчанию она получит тип Signed int.
Классификация типов данных
Модификаторы типа:
Целые
(пределы их значений в Си хранятся в limits.h)
unsigned char (1 байт) 0 — 255
unsigned char (1 байт) 0 – 255
Замечания:
на Си не различаются символьный и целый типы (хранятся и обрабатываются одинаково). Символьные константы и переменные можно использовать в арифметических выражениях:
d=c-‘s’;
для представления русских символов требуется unsigned char (по умолчанию будет signed char)
signed char 1 байт (-128 .. +127)
Замечание: можно явно не указывать signed. Оно подразумевается по умолчанию
short (2 байта) -32768..32767 (всегда 2 байта)
int (2 байта) -32769..32767 (или 4 байта)
по умолчанию размер int совпадает с размером слова на данной ЭВМ (на 16-разр. ЭВМ — 16 бит, на 32-разр. ЭВМ — 32 бит = 4 байта)
unsigned int (2 байта) 0..65535. У констант суффикс U : 325U
long (4 байта) -2^31..2^31-1. У констант суффикс L: 75000L
unsigned long (4 байта) 0..(2^32-1). У констант суффикс UL: 125000UL
В ASNI C-99 long long – 8 байт.
Вещественные
(пределы их значений в Си хранятся в float.h)
Имя типа | размер | диапазон |
float | 4 байта | ±3.4E+308 |
double | 8 байт | ±1.7E+308 |
long double | 10 байт | |
unsigned long | 8 байт |
Логический
В Си нет логического типа данных: выражения, в которых требуются логические значения, интерпретируют значения «ноль», как false(ложь), а все другие (не равные нулю), как «true» (истина).
В Borland C++ есть тип BOOL со значениями false и true.
Диапазоны представления данных
Тип | Размер, бит | Диапазон |
unsigned char | 8 | 0…255 |
char | 8 | –128…127 |
enum | 16 | –32 768…32 767 |
unsigned short | 16 | 0…65535 |
short | 16 | –32768…32767 |
unsigned int | 16 32 | 0…65535 0…4 294 967 295 |
int | 16 32 | –32 768…32 767 –2 147 483 648 …2 147 483 647 |
unsigned long | 32 | 0…4 294 967 295 |
long | 32 | –2 147 483 648 …2 147 483 647 |
float | 32 | 3.4*10–38…3.4*10+38 |
double | 64 | 1.7*10–308…1.7*10+308 |
long double | 80 | 1.7*10–4932…1.7*10+4932 |
pointer | 16 32 | (указатели near, _cs, _ds, _es, _ss) (указатели far, huge) |
Объявление переменных
Основная форма объявления переменных имеет вид
type <список_переменных> ;
Здесь type должен быть одним из существующих в С типов переменных, а <список_переменных> может состоять из одной или нескольких переменных, разделенных запятыми. При объявлении переменных компилятор выделяет место в памяти компьютера, необходимое для размещения переменной данного типа. Примеры объявлений переменных:
int x, у, z;
float radius;
unsigned char ch;
long double integral;
studfile.net
Общаться с 64-битным long long в Visual C++ / Sandbox / Habr
Тип данных long long — это единственный целочисленный тип данных в Visual С++, который весит 8 байт и может использовать значения выше 2^31 — 1…..-2^31. Если у вас 64-битная система, то тип long тоже будет 8-байтовым. А в 32-разрядной системе приходится довольствоваться только им. Этот тип данных получил права только в последних версиях С++, например, в С++ 6.0 вы его найдёте, но работать с ним там одно удовольствие.Несмотря на то, что у long long есть закреплённые права и обязанности. Использовать его всё равно следует по-особенному. Для начала следует учесть, что простое присваивание ему значений не прокатит. Если вы хотите присвоить значение переменной типа long long, то не забудьте приставить к числу две буквы LL (которые видимо символизируют, что число «longlong» 64-битное).
long long temp = 1100LL;
Далее: про соответствие типов. Думаю, когда человек первый раз «берёт в руки» этот тип данных ему становится интересно, а как он соотносится с типом int?
Отношения у long long c int очень интересные. Если вы хотите складывать два числа int, зная, что результат выйдет за диапазон int, то вы можете использовать long long. Сложите два int присвоите long long и все будут счастливы.
int a1 = 2000000000;
int a2 = 2000000000;
long long b = a1 + a2;
b равен 400000000. И здесь проблем возникнуть не должно.
С умножением int’ов дела гораздо интереснее. Если вы просто возьмёте две переменные типа int и перемножите их между собой, зная, что результат выйдет за пределы int, то ничего у вас не получится. Переменная типа long long будет просто равна нулю. В итоге, чтобы умножать в Visual C++ числа типа int и получить long long следует проделать сложения через цикл, т.е. если мы хотим умножить число а на число b, то всё будет выглядеть так.
long long temp = 0;
for (int i = 0; i < a; i++) {
temp += b;
}
И только тогда захочет быть произведением.
habr.com