Диапазон значений int c: C++ | Типы данных

Содержание

Диапазоны типов данных | Microsoft Docs



  • Чтение занимает 2 мин

В этой статье

Компиляторы Microsoft C++ 32-bit и 64-bit распознают типы в таблице далее в этой статье.

  • int (unsigned int)

  • __int8 (unsigned __int8)

  • __int16 (unsigned __int16)

  • __int32 (unsigned __int32)

  • __int64 (unsigned __int64)

  • short (unsigned short)

  • long (unsigned long)

  • long long (unsigned long long)

Если имя начинается с двух символов подчеркивания (__), тип данных является нестандартным.

Диапазоны, представленные в следующей таблице, включают указанные значения.

Имя типаБайтыДругие именаДиапазон значений
int4signedОт -2 147 483 648 до 2 147 483 647
unsigned int4unsignedОт 0 до 4 294 967 295
__int81charОт -128 до 127
unsigned __int81unsigned charОт 0 до 255
__int162short, short int, signed short intОт -32 768 до 32 767
unsigned __int162unsigned short, unsigned short intОт 0 до 65 535
__int324signed, signed int, intОт -2 147 483 648 до 2 147 483 647
unsigned __int324unsigned, unsigned intОт 0 до 4 294 967 295
__int648long long, signed long longОт -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807
unsigned __int648unsigned long longОт 0 до 18 446 744 073 709 551 615
bool1нетfalse или true
char1нетот-128 до 127 по умолчанию

от 0 до 255 при компиляции с помощью /J

signed char1нетОт -128 до 127
unsigned char1нетОт 0 до 255
short2short int, signed short intОт -32 768 до 32 767
unsigned short2unsigned short intОт 0 до 65 535
long4long int, signed long intОт -2 147 483 648 до 2 147 483 647
unsigned long4unsigned long intОт 0 до 4 294 967 295
long long8нет (но эквивалентно __int64 )От -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807
unsigned long long8нет (но эквивалентно unsigned __int64 )От 0 до 18 446 744 073 709 551 615
enumнепостояннонет
float4нет3,4E +/- 38 (7 знаков)
double8нет1,7E +/- 308 (15 знаков)
long doubleто же, что doubleнетТо же, что double
wchar_t2__wchar_tОт 0 до 65 535

В зависимости от того, как он используется, переменная __wchar_t определяет либо тип расширенного символа, либо многобайтовый символ. Чтобы указать константу расширенного символьного типа, перед символьной или строковой константой следует использовать префикс L .

signed и unsigned — это модификаторы, которые можно использовать с любым целочисленным типом, за исключением bool . Обратите внимание, что char , signed char и unsigned char — это три различных типа для таких механизмов, как перегрузка и шаблоны.

int Типы и unsigned int имеют размер четыре байта. Однако переносимый код не должен зависеть от размера, int так как языковой стандарт позволяет реализовать его в зависимости от реализации.

C и C++ в Visual Studio также поддерживают целочисленные типы с указанием размера. Дополнительные сведения см. в разделе __int8, __int16, __int32, __int64 и ограничения целых чисел.

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

Диапазон перечисляемых типов зависит от контекста языка и указанных флажков компилятора. Дополнительные сведения см. в статьях Объявления перечислений C и Объявления перечислений C++.

См. также раздел

Ключевые слова
Встроенные типы



Целочисленные типы данных в C++: short, int и long | Уроки С++

  Обновл. 22 Апр 2021  | 

На этом уроке мы рассмотрим целочисленные типы данных в языке С++, их диапазоны значений, операцию деления, а также переполнение (что это такое и примеры).

Целочисленные типы данных

Целочисленный тип данных — это тип, переменные которого могут содержать только целые числа (без дробной части, например: -2, -1, 0, 1, 2). В языке C++ есть 5 основных целочисленных типов, доступных для использования:

КатегорияТипМинимальный размер
Символьный тип данныхchar1 байт
Целочисленный тип данныхshort2 байта
int2 байта (но чаще всего 4 байта)
long4 байта
long long8 байт

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

Основным различием между целочисленными типами, перечисленными выше, является их размер, чем он больше, тем больше значений сможет хранить переменная этого типа.

Объявление целочисленных переменных

Объявление происходит следующим образом:

char c;
short int si; // допустимо
short s; // предпочтительнее
int i;
long int li; // допустимо
long l; // предпочтительнее
long long int lli; // допустимо
long long ll; // предпочтительнее

char c;

short int si; // допустимо

short s;      // предпочтительнее

int i;

long int li; // допустимо

long l;      // предпочтительнее

long long int lli; // допустимо

long long ll;      // предпочтительнее

В то время как полные названия short intlong int и long long int могут использоваться, их сокращенные версии (без int) более предпочтительны для использования. К тому же постоянное добавление int затрудняет чтение кода (легко перепутать с именем переменной).

Диапазоны значений и знак целочисленных типов данных

Как вы уже знаете из предыдущего урока, переменная с n-ным количеством бит может хранить 2возможных значений. Но что это за значения? Это значения, которые находятся в диапазоне. Диапазон — это значения от и до, которые может хранить определенный тип данных. Диапазон целочисленной переменной определяется двумя факторами: её размером (измеряется в битах) и её знаком (который может быть signed или unsigned).

Целочисленный тип signed (со знаком) означает, что переменная может содержать как положительные, так и отрицательные числа. Чтобы объявить переменную как signed, используйте ключевое слово signed:

signed char c;
signed short s;
signed int i;
signed long l;
signed long long ll;

signed char c;

signed short s;

signed int i;

signed long l;

signed long long ll;

По умолчанию, ключевое слово signed пишется перед типом данных.

1-байтовая целочисленная переменная со знаком (signed) имеет диапазон значений от -128 до 127, т.е. любое значение от -128 до 127 (включительно) может храниться в ней безопасно.

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

Целочисленный тип unsigned (без знака) может содержать только положительные числа. Чтобы объявить переменную как unsigned, используйте ключевое слово unsigned:

unsigned char c;
unsigned short s;
unsigned int i;
unsigned long l;
unsigned long long ll;

unsigned char c;

unsigned short s;

unsigned int i;

unsigned long l;

unsigned long long ll;

1-байтовая целочисленная переменная без знака (unsigned) имеет диапазон значений от 0 до 255.

Обратите внимание, объявление переменной как unsigned означает, что она не сможет содержать отрицательные числа (только положительные).

Теперь, когда вы поняли разницу между signed и unsigned, давайте рассмотрим диапазоны значений разных типов данных:

Размер/ТипДиапазон значений
1 байт signedот -128 до 127
1 байт unsignedот 0 до 255
2 байта signedот -32 768 до 32 767
2 байта unsignedот 0 до 65 535
4 байта signedот -2 147 483 648 до 2 147 483 647
4 байта unsignedот 0 до 4 294 967 295
8 байтов signedот -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807
8 байтов unsignedот 0 до 18 446 744 073 709 551 615

Для математиков: Переменная signed с n-ным количеством бит имеет диапазон от -(2n-1) до 2n-1-1. Переменная unsigned с n-ным количеством бит имеет диапазон от 0 до (2n)-1.

Для нематематиков: Используем таблицу 🙂

Начинающие программисты иногда путаются между signed и unsigned переменными. Но есть простой способ запомнить их различия. Чем отличается отрицательное число от положительного? Правильно! Минусом спереди. Если минуса нет, значит число — положительное. Следовательно, целочисленный тип со знаком (signed) означает, что минус может присутствовать, т.е. числа могут быть как положительными, так и отрицательными. Целочисленный тип без знака (unsigned) означает, что минус спереди отсутствует, т.е. числа могут быть только положительными.

Что используется по умолчанию: signed или unsigned?

Так что же произойдет, если мы объявим переменную без указания signed или unsigned?

КатегорияТипПо умолчанию
Символьный тип данныхcharsigned или unsigned (в большинстве случаев signed)
Целочисленный тип данныхshortsigned
intsigned
longsigned
long longsigned

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

В большинстве случаев ключевое слово signed не пишется (оно и так используется по умолчанию).

Программисты, как правило, избегают использования целочисленных типов unsigned, если в этом нет особой надобности, так как с переменными unsigned ошибок, по статистике, возникает больше, нежели с переменными signed.

Правило: Используйте целочисленные типы signed, вместо unsigned.

Переполнение

Вопрос: «Что произойдет, если мы попытаемся использовать значение, которое находится вне диапазона значений определенного типа данных?». Ответ: «Переполнение».

Переполнение (англ. «overflow») случается при потере бит из-за того, что переменной не было выделено достаточно памяти для их хранения.

На уроке №28 мы говорили о том, что данные хранятся в бинарном (двоичном) формате и каждый бит может иметь только 2 возможных значения (0 или 1). Вот как выглядит диапазон чисел от 0 до 15 в десятичной и двоичной системах:

Десятичная системаДвоичная система
00
11
210
311
4100
5101
6110
7111
81000
91001
101010
111011
121100
131101
141110
151111

Как вы можете видеть, чем больше число, тем больше ему требуется бит. Поскольку наши переменные имеют фиксированный размер, то на них накладываются ограничения на количество данных, которые они могут хранить.

Примеры переполнения

Рассмотрим переменную unsigned, которая состоит из 4-х бит. Любое из двоичных чисел, перечисленных в таблице выше, поместится внутри этой переменной.

«Но что произойдет, если мы попытаемся присвоить значение, которое занимает больше 4-х бит?». Правильно! Переполнение. Наша переменная будет хранить только 4 наименее значимых (те, что справа) бита, все остальные — потеряются.

Например, если мы попытаемся поместить число 21 в нашу 4-битную переменную:

Десятичная системаДвоичная система
2110101

Число 21 занимает 5 бит (10101). 4 бита справа (0101) поместятся в переменную, а крайний левый бит (1) просто потеряется. Т.е. наша переменная будет содержать 0101, что равно 101 (нуль спереди не считается), а это уже число 5, а не 21.

Примечание: О конвертации чисел из двоичной системы в десятичную и наоборот будет отдельный урок, где мы всё детально рассмотрим и обсудим.

Теперь рассмотрим пример в коде (тип short занимает 16 бит):

#include <iostream>

int main()
{
unsigned short x = 65535; // наибольшее значение, которое может хранить 16-битная unsigned переменная
std::cout << «x was: » << x << std::endl;
x = x + 1; // 65536 — это число больше максимально допустимого числа из диапазона допустимых значений. Следовательно, произойдет переполнение, так как переменнная x не может хранить 17 бит
std::cout << «x is now: » << x << std::endl;
return 0;
}

#include <iostream>

int main()

{

    unsigned short x = 65535; // наибольшее значение, которое может хранить 16-битная unsigned переменная

    std::cout << «x was: » << x << std::endl;

    x = x + 1; // 65536 — это число больше максимально допустимого числа из диапазона допустимых значений. Следовательно, произойдет переполнение, так как переменнная x не может хранить 17 бит

    std::cout << «x is now: » << x << std::endl;

    return 0;

}

Результат выполнения программы:

x was: 65535
x is now: 0

Что случилось? Произошло переполнение, так как мы попытались присвоить переменной x значение больше, чем она способна в себе хранить.

Для тех, кто хочет знать больше: Число 65 535 в двоичной системе счисления представлено как 1111 1111 1111 1111. 65 535 — это наибольшее число, которое может хранить 2-байтовая (16 бит) целочисленная переменная без знака, так как это число использует все 16 бит. Когда мы добавляем 1, то получаем число 65 536. Число 65 536 представлено в двоичной системе как 1 0000 0000 0000 0000, и занимает 17 бит! Следовательно, самый главный бит (которым является 1) теряется, а все 16 бит справа — остаются. Комбинация 0000 0000 0000 0000 соответствует десятичному 0, что и является нашим результатом.

Аналогичным образом, мы получим переполнение, использовав число меньше минимального из диапазона допустимых значений:

#include <iostream>

int main()
{
unsigned short x = 0; // наименьшее значение, которое 2-байтовая unsigned переменная может хранить
std::cout << «x was: » << x << std::endl;
x = x — 1; // переполнение!
std::cout << «x is now: » << x << std::endl;
return 0;
}

#include <iostream>

int main()

{

    unsigned short x = 0; // наименьшее значение, которое 2-байтовая unsigned переменная может хранить

    std::cout << «x was: » << x << std::endl;

    x = x — 1; // переполнение!

    std::cout << «x is now: » << x << std::endl;

    return 0;

}

Результат выполнения программы:

x was: 0
x is now: 65535

Переполнение приводит к потере информации, а это никогда не приветствуется. Если есть хоть малейшее подозрение или предположение, что значением переменной может быть число, которое находится вне диапазона допустимых значений используемого типа данных — используйте тип данных побольше!

Правило: Никогда не допускайте возникновения переполнения в ваших программах!

Деление целочисленных переменных

В языке C++ при делении двух целых чисел, где результатом является другое целое число, всё довольно предсказуемо:

#include <iostream>

int main()
{
std::cout << 20 / 4 << std::endl;
return 0;
}

#include <iostream>

int main()

{

    std::cout << 20 / 4 << std::endl;

    return 0;

}

Результат:

5

Но что произойдет, если в результате деления двух целых чисел мы получим дробное число? Например:

#include <iostream>

int main()
{
std::cout << 8 / 5 << std::endl;
return 0;
}

#include <iostream>

int main()

{

    std::cout << 8 / 5 << std::endl;

    return 0;

}

Результат:

1

В языке C++ при делении целых чисел результатом всегда будет другое целое число. А такие числа не могут иметь дробь (она просто отбрасывается, не округляется!).

Рассмотрим детально вышеприведенный пример: 8 / 5 = 1.6. Но как мы уже знаем, при делении целых чисел результатом является другое целое число. Таким образом, дробная часть (0.6) значения отбрасывается и остается 1.

Правило: Будьте осторожны при делении целых чисел, так как любая дробная часть всегда отбрасывается.

Оценить статью:

Загрузка…

Поделиться в социальных сетях:

C: Переменные — Linux FAQ

Переменные являются именованными указателями на области памяти, хранящие их значения. В языке C каждая переменная должна иметь тип, описывающий структуру данных, хранящихся в памяти. Эта информация позволяет компьютеру выяснить, сколько памяти необходимо зарезервировать для хранения значения той или иной перемененной.

Язык C поддерживает несколько базовых типов данных, таких, как char, int, float и double. Существуют и дополнительные типы данных, объявляемые с использованием слов signed, unsigned и long, которые позволяют хранить в памяти значения из больших диапазонов.

Целые числа

Целочисленная переменная типа int позволяет хранить целые числа в четырех байтах памяти. При присваивании целочисленной переменной значения с плавающей точкой типа float или double, это значение будет автоматически преобразовано в целочисленное.

int count = 8;

Также в распоряжении разработчика имеются беззнаковые (unsigned int), короткие (short int),  длинные (long int) и расширенные длинные (long long int) целочисленные значения. Они могут использоваться при необходимости:

  • Переменная типа short int может хранить значения из диапазона от -32768 до 32768 и занимает два байта памяти.
  • Переменная типа unsigned short int может хранить значения из диапазона от 0 до 65535 занимает два байта памяти.
  • Переменная типа int может хранить значения из диапазона от -2147483648 до  2147483648 и занимает четыре байта памяти.
  • Переменная типа unsigned int может хранить значения из диапазона от 0 до 4294967295 и занимает четыре байта памяти.
  • Переменная типа long int может хранить значения из диапазона от -2147483648 до  2147483648 и занимает четыре байта памяти на 32-битных системах и значения из диапазона от -9223372036854775807 до 9223372036854775807 и занимает восемь байт памяти на 64-битных системах.
  • Переменная типа unsigned long int может хранить значения из диапазона от 0 до 4294967295 и занимает четыре байта памяти на 32-битных системах и значения из диапазона от 0 до 18446744073709551615 и занимает восемь байт памяти на 64-битных системах.
  • Переменная типа long long int может хранить значения из диапазона от -9223372036854775807 до 9223372036854775807 и занимает восемь байт памяти на любых системах.
  • Переменная типа unsigned long long int может хранить значения из диапазона от 0 до 18446744073709551615 и занимает восемь байт памяти на любых системах.

Обозначение типа данных int может отбрасываться в случае использования обозначений типов данных long и short. Помимо обозначения беззнаковых переменных (unsigned), существует обозначение знаковых переменных (signed), которое используется по умолчанию на большинстве платформ. Например, следующие объявления являются эквивалентными:

long lightspeed = 299792458;
long int lightspeed = 299792458;
signed long lightspeed = 299792458;
signed long int lightspeed = 299792458;

Числа с плавающей точкой

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

float pi = 3.14;

По аналогии с переменной типа float, переменная типа double также позволяет хранить вещественные числа, но эти числа могут иметь двойную точность, так как под них отводится восемь байт памяти.

double score = 53.92819384;

Кроме того, имеется тип данных long double для работы с вещественными числами с повышенной точностью.

Символы

Символьная переменная типа char позволяет хранить один символ в одном байте памяти. Хотя в качестве значения переменной и устанавливается символ, в памяти хранится его целочисленное представление.

char initial = 'd';

Помимо обычной символьной переменной существует знаковая (signed char) и беззнаковая (unsigned char) символьные переменные.

Строки

В языке C для строк не используется отдельного типа данных, как это делается в таких языках, как Python. Вместо этого строки представлены массивами символьных переменных.
Подробнее об этом будет рассказано в разделе «Строки».

Логические значения

Современный стандарт языка C99 включает поддержку логических переменных типа bool (принимающих значения true и false), однако в случае компиляции программы с такими переменными с помощью устаревшего компилятора будут возникать ошибки.

Новый тип данных объявлен как _Bool, хотя в случае подключения модуля <stdbool.h> тип _Bool будет эквивалентен типу bool.

  1. #include <stdio.h>

  2. #include <stdbool.h>

  3.  

  4. int main()

  5. {

  6.     bool run = false;

  7.  

  8.     if (run == true)

  9.         printf("Работает...\n");
  10.     else

  11.         printf("Не работает. \n");
  12.  

  13.     return 0;

  14. }

Примечание: для использования типа bool программа должна компилироваться с параметром -std=c99.

Локальные и глобальные переменные

Если переменная объявлена в рамках функции, она может использоваться исключительно в рамках этой функции. Любая попытка использования ее извне, например, из другой функции, приведет к выводу сообщения об ошибке. Такая переменная называется локальной.

Глобальные переменные объявляются вне функций и могут использоваться из любого фрагмента кода программы. Например, значение такой переменной может изменяться из нескольких функций.

Статические переменные

Статические переменные объявляются как локальные переменные. Такая переменная инициализируется в момент первого вызова функции. После выхода из функции значение статической переменной сохраняется в памяти и не изменяется до следующего вызова этой функции.

Статическая переменная объявляется с помощью слова static, располагающегося перед описанием типа переменной.

static int age;

Пример:

  1. #include <stdio.h>

  2.  

  3. {

  4.     int var = 1;

  5.     static int svar = 1;

  6.  

  7.     printf("var = %d, static = %d\n", var, svar);
  8.     var++;

  9.     svar++;

  10. }

  11.  

  12. int main()

  13. {

  14.     int i;

  15.     for (i = 0; i < 5; i++)

  16.     {

  17.     }

  18.  

  19.     return 0;

  20. }

Загрузить файл исходного кода

Ключевые слова

Язык C предполагает использование набора зарезервированных ключевых слов, которые не могут использоваться в качестве имен переменных. Этими ключевыми словами являются:

  • auto
  • break
  • case
  • char
  • continue
  • default
  • do
  • double
  • else
  • enum
  • extern
  • float
  • for
  • goto
  • if
  • int
  • long
  • register
  • return
  • short
  • sizeof
  • static
  • struct
  • switch
  • typedef
  • union
  • unsigned
  • void
  • while

Имена переменных должны начинаться с буквенного символа или символа подчеркивания и могут содержать цифры. 31, что составляет от –2 147 483 648 до 2 147 483 647 или около -2 млрд до +2 млрд. Диапазон такой же, но он сдвинут на числовой линии.

25

biscuit

29 Авг 2019 в 08:21

5 ответов

Лучший ответ

п

3

000
001
010
011
100
101
110
111

п

  • Он может представлять целые числа от 0 до 2 n -1 включительно
    (потому что (2 n -1) — (0) + 1 = 2 п разные значения).

    Например,

    000   0
    001   1
    010   2
    011   3
    100   4
    101   5
    110   6
    111   7
    
  • Он может представлять целые числа от -2 n-1 до 2 n-1 -1 включительно
    (потому что (2 n-1 ) > -1) — (-2 n-1 ) + 1 = 2 n различных значений).

    Например,

    100  -4
    101  -3
    110  -2
    111  -1
    000   0
    001   1
    010   2
    011   3
    

[ 1 ]


  1. На своем дополнении есть два способа записи нуля (0000. ..0000 2 и 1000 … 0000 2 ), поэтому диапазон составляет всего от -2 n-1 -1 до 2 п — 1 -1 . Я думаю, что все современные машины являются дополнением до двух.

23

ikegami
24 Фев 2020 в 13:47

Для целого типа без знака значение -1 равно вне диапазона и не может быть представлено в переменной этого типа. Если вы попытаетесь присвоить -1 unsigned int, конверсия произойдет в соответствии с правилами стандарта C.

Преобразование значения со знаком в целочисленный тип без знака указано в разделе 6.3.1.3p2 документа стандарт C:

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

60) Правила описывают арифметику математического значения, а не значения данного типа выражения

Предполагая, как в вашем примере, что unsigned int имеет диапазон значений от 0 до 4 294 967 295, значение -1 преобразуется путем сложения -1 + 4 294 967 296 = 4 294 967 295. Обратите внимание, что это преобразование происходит независимо от того, как отрицательные числа представлены в данной системе. То же самое для комплимента двоих, комплимента своих или знака и величины.

Обратите внимание, что это означает, что представление преобразованного значения не должно совпадать с представлением -1.

Используя 4-битный тип в качестве примера, преобразование значения -1 в тип без знака приводит к значению 15. Представление этих чисел выглядит следующим образом:

                sign-and magnitude    ones' complement   two's complement
  -1   (signed)               1001                1110               1111
  15 (unsigned)               1111                1111               1111

Хотя в случае дополнения до двух результат преобразования остается тем же представлением, в других двух случаях он изменяется. Для дополнения представление -1 такое же, как для 14, а для знака и величины представление -1 такое же, как для 9.

Таким образом, то, что другие ответы описали относительно дополнения к двум, скорее всего, как эти реализации делают это (то есть переосмысливают представление -1 как значение без знака), однако с точки зрения языка C как абстрактной машины то, что я описал, является единственным правильный способ выполнения этого преобразования.

3

dbush
29 Авг 2019 в 18:53

В дополнение к превосходному объяснению @ Yunnosch о числах без знака, почти все современные компьютеры используют «дополнение двух» для представления двоичных чисел со знаком. В дополнение к двум наиболее значимый бит используется в качестве «знакового бита», а биты являются дополнением к абсолютному значению числа +1. Так, для примера с 3 битами, в то время как диапазон значений без знака равен от 0 до 7, диапазон для подписанных значений от -4 до 3:

100 : -4
101 : -3
110 : -2
111 : -1
000 :  0
001 :  1
010 :  2
011 :  3

Обратите внимание, что для чисел со знаком диапазон отрицательных чисел на единицу больше, чем диапазон положительных чисел. Это потому, что в теории чисел 0 не является ни положительным, ни отрицательным, в двоичном представлении 0 должен быть либо отрицательным, либо положительным. Поскольку он сбрасывает наиболее значимый бит, 0 является частью области положительных чисел, поэтому остается доступным одно менее положительное число.

5

daShier
29 Авг 2019 в 06:08

Рассмотрим значения, которые вы можете получить с 2 битами:

00 : 0
01 : 1
10 : 2
11 : 3

Их 4, 2 в степени 2. 2-1
или 2 2 -1

Добавьте немного, и вы получите вдвое больше, добавив

100 : 4
101 : 5
110 : 6
111 : 7

Всего 8, но наибольшее 7.

Таким образом, «-1» потому что всегда первое из 2 n используется для 0,
2-е используется для 1, 3-е используется для 2.
В конце (2 n ) -й один недоступен для 2 n , он уже используется для 2 n -1.

47

Yunnosch
29 Авг 2019 в 06:30

Где вы нашли этот неверный абзац? Похоже, что это дополнение к 2, но -1 находится не в том месте.

Для реализаций C, использующих целые числа со знаком или дополнением или знаком / величиной, диапазон симметричен относительно нуля (с 2-битными комбинациями, которые оба представляют 0, поэтому положительный диапазон и отрицательный диапазон имеют одинаковый размер).n.

Если только установить бит знака, мы получим наиболее отрицательное число : -INT_MIN переполнение со знаком (неопределенное поведение), поскольку оно не может быть представлено как {{X1 } } ; это требует более широкого целого числа. Или с упаковкой, -INT_MIN = INT_MIN. Это «аномалия комплемента 2». https://en.wikipedia.org/wiki/Two%27s_complement#Most_negative_number

Вы можете избежать расширения, если выполняете операцию с абсолютным значением: например,
unsigned abs = i >= 0 ? i : -(unsigned)i;

(Преобразование отрицательного значения в unsigned в C имеет четко определенное поведение уменьшения по модулю до тех пор, пока оно не окажется в представимом диапазоне. В C это не зависит от кодировки со знаком-целым числом; важно значение value . Так что (uint8_t)-1 всегда 255. Для дополнения 2 он просто копирует битовый шаблон. Для знака / величины или дополнения реализация C должна была бы выполнить некоторую математику для приведения от подписи к подписи. Обратите внимание, что я сделал это до отрицания, что означает 0 - i с обычным беззнаковым переносом.)

2

Peter Cordes
30 Авг 2019 в 00:09

Введение в целочисленные типы — Целочисленные типы, кортежи, шаблонные функции

[БЕЗ_ЗВУКА] Здравствуйте. Мы начинаем блок, посвященный рассмотрению целочисленных типов языка C++. Вы уже знаете один целочисленный тип — это тип int, и мы им везде пользовались. Но все-таки мы не будем просто так ради общего образования рассматривать целочисленные типы языка C++ — мы начнем с проблемы. Для этого вспомним задачу «Средняя температура» из первого курса. Что там нужно было сделать? Нам был дан набор наблюдений за температурой, скажем, в виде вектора (назовем его t), и у нас были какие-то значения температуры: например, 8, 7 и 3. Нужно было найти среднее арифметическое значение температуры за все дни и затем вывести номера дней, в которые значение температуры было больше, чем среднее арифметическое. В частности, нам нужно было уметь вычислять среднее арифметическое. Как это можно было сделать? Можно было, вот, собственно, имея данный вектор с наблюдениями… Возможно, мы его читали из исходного файла, здесь у меня он задан просто явно. Надо было завести переменную с суммой и затем циклом for проитерироваться по вектору t, тем самым вычислить сумму — суммарное значение температуры, и затем поделить эту сумму на размер вектора. Все кажется абсолютно логичным. Сохранить в переменную со средним значением и вывести после этого. Вот был один из способов решить эту задачу. Давайте проверим, что он работает. Какая будет средняя температура? 8 + 7 + 3 — это 18. Разделить на 3, получается 6. Должно получиться 6. Запускаем код и видим действительно 6, код работает. Но если вспомнить, то в той задаче было ограничение: вам гарантировалось, что все значения температуры положительные, ну или не отрицательные. Почему так? Потому что если в таком решении у вас в исходном векторе будут отрицательные значения температуры, например, −8, −7 и 3, код работать не будет. Почему? Потому что… казалось бы, −8 − 7 + 3 — это −12. −12 / 3 — должно получиться −4. Давайте проверим, что же получится. Компилируем код, запускаем и видим что-то очень странное. Мы видим 1431655761. Это не −4. Что же произошло? На самом деле мы от незнания неаккуратно использовали другой целочисленный тип языка C++. Он возникает у нас вот здесь, это другой тип, t.size() — это специальный тип, который не умеет хранить отрицательные числа. Понятно, что размер контейнера отрицательным быть не может, и это так называемый беззнаковый тип, мы это не учли. Далее мы эту проблему рассмотрим, в следующих видео. Пока что. Какая еще бывает проблема с целочисленными типами? Очень простой пример: у вас есть переменная типа int, в нее умещается, например, два миллиарда — 2 и девять нулей. Заметьте, в C++ можно для лучшей читаемости разбивать разряды в числе одинарной кавычкой. И эта переменная x успешно хранится в типе int, мы можем ее вывести, а потом можем захотеть умножить ее на 2, например. И давайте снова посмотрим, что получится. Итак, запускаем код и видим, что у нас изначально x было 2 миллиарда, а в итоге получилось каким-то большим отрицательным числом. 4 миллиарда в переменную типа int не поместилось. Соответственно, нам нужно обсудить следующие особенности целочисленных типов языка C++. Первая особенность: в языке C++ память для целочисленных типов ограничена. Язык C++ не будет за вас выделять больше памяти для целочисленного типа, потому что он позволяет вам не платить за то, что вы не используете. Если вам не нужны целые числа размером больше 2 миллиардов, язык C++ для вас выделит вот ровно столько памяти, сколько достаточно для хранения числа размером 2 миллиарда. Соответственно, у целочисленных типов языка C++ ограниченный диапазон значений. И нам нужно это изучить в первую очередь. Во-вторых, вторая проблема, с которой мы могли столкнуться в задаче «Средняя температура», если бы там допускались отрицательные значения температуры, это то, что некоторые целочисленные типы языка C++ беззнаковые. Почему? Потому что иногда вам не нужно уметь хранить в целочисленной переменной отрицательные числа. Соответственно, опять, вы не должны платить за то, что вы не используете, и вы можете взять себе для этих целей целочисленный тип, в котором нельзя хранить отрицательные значения. Тем самым вы, наверное, сможете хранить больше положительных значений. Итак, давайте разбираться, какие у нас бывают целочисленные типы. Стандартный целочисленный тип — это тип int. Именно по этой причине мы его использовали свободно в первом курсе. В каком смысле стандартный? Во-первых, если вы напишете auto x = 1, то переменная x получит тип int. То есть все числа, которые вы прямо вот в виде цифр пишете в вашем коде, имеют тип int, если, конечно, они достаточно маленькие. Этот тип — почему еще он стандартный? Потому что он наиболее эффективен. Он наиболее эффективен на вашем компьютере, на том компьютере, на котором вы компилируете вашу программу. Он ровно такой, чтобы операции с ним напрямую транслировались в инструкции процессора. И именно поэтому он не имеет какой-то конкретный размер, вам не гарантируют, что он всегда будет иметь какой-то конкретный размер, но на всех современных архитектурах он почти всегда имеет размер 4 байта или 32 бита, и соответственно, диапазон его значений — от −2³¹ до (2³¹ − 1), это примерно 2 миллиарда. Но нужно помнить, что в конечном счете размер этого типа зависит от архитектуры процессора конкретной. Более того, у типа int есть его беззнаковый аналог — тип unsigned int. Вот ровно так, через пробел, нужно записывать название этого типа; или просто unsigned, без int. Этот тип имеет точно такой же размер, как и int, он тоже наиболее эффективен на вашей архитектуре, но он беззнаковый, соответственно, он вмещает числа от 0 до (2³² − 1). Как видите, примерно в два раза больше положительных чисел вмещается за счет того, что мы не храним отрицательные числа. Есть еще один беззнаковый тип — это тип size_t. Это ровно тот тип, который в методе size возвращают контейнеры языка C++, например, вектор. Его размер зависит от разрядности вашей операционной системы. То есть тип size_t спроектирован именно так, чтобы умещать те размеры контейнера, которые бывают. Если у вас 32-битная система, скорее всего, size_t будет иметь размер 32 бита, если 64-битная, то 64 бита. Кроме того. Это были так называемые классические типы языка C++. Почему классические? Потому что они из языка C к нам пришли. А еще есть другие типы, в которых явно известен заранее их размер. Если вы подключите модуль cstdint, то вы сможете использовать тип int32_t. Это знаковый тип размером ровно 32 бита. То есть как int, но у него гарантированный размер. Есть у него беззнаковый аналог, это тип uint32_t, и также есть, соответственно, типы размеров 8 бит, 16 бит и 64 бита. int8_t, uint8_t, int16_t, uint16_t и int64_t, uint64_t. Итак, давайте резюмируем, какие типы мы узнали. У нас есть тип int, который обычно имеет размер 4 байта, есть тип unsigned int, который обычно имеет тот же размер, но он беззнаковый, и есть тип size_t, который имеет размер 4 или 8 байт. Кроме того, есть типы с известным размером: int8_t, int16_t, int32_t, int64_t — знаковые типы, и их беззнаковые аналоги с префиксом u. Как же нам выбрать разрядность типа? Как же нам выбрать тот тип, который мы будем использовать? Мы пока не говорим про знаковость и беззнаковость, это мы обсудим позже. Как же нам выбрать размер типа? Если вам достаточно стандартного типа int, используйте его, потому что он стандартный. Если вам недостаточно его размерности, то есть вы хотите хранить числа порядка триллиона, порядка 10¹², например, то вам нужно использовать тип int64_t. Если вы хотите единообразия и вас пугает то, что никто не гарантирует, что тип int будет иметь размер обязательно 4 байта, используйте тип int32_t, хотя это, конечно, некоторая экзотика, разве что для единообразия можно его использовать. И если вы хотите экономить память на целочисленных типах, то вы можете использовать типы меньшей размерности, такие как int8_t, int16_t. Что делать, если вы забыли размер конкретного типа данных? Необязательно для этого идти в документацию, для этого достаточно взять компилятор и… Например, вы забыли, какой размер имеет тип int16_t. Как узнать его размер? Вы можете написать cout и вывести sizeof(int16_t). Так вы узнаете размер в байтах для этого типа. Правда, это и так понятно, 16 / 8 — это 2: 16 бит — 2 байта. Или вы точно так же можете узнать размер типа int. Вот я говорил, что не гарантируется, что у него размер 4 байта, давайте посмотрим, на моем компьютере какой у него размер. Запустим этот код. 4 байта, действительно, почти всегда 4 байта. Кроме того, можно sizeof еще вызывать не только от типа, но и от переменной конкретного типа. Если бы у вас была переменная int x, скажем, вы бы могли вызвать sizeof(x). Ну да ладно, это не очень удобно. Итак. Кроме того, вы можете узнать минимальное и максимальное значение типа, подключив модуль limits — ограничения. И теперь вы можете узнать минимальное и максимальное значение типа int. Как это сделать? Нужно написать: numeric_limits, в угловых скобках название того типа, который вас интересует, и дальше через два двоеточия — что вас об этом типе интересует. Я вызываю функцию min: меня интересует, собственно, минимальное значение этого типа, ну и давайте для полноты картины выведем еще и максимальное значение этого типа: numeric_limits&lt;int&gt;::max. Давайте посмотрим, какое же минимальное и максимальное значение типа int. На самом деле мы это уже знаем, это −2³¹ и (2³¹ − 1). Давайте посмотрим на эти числа. Я забыл круглые скобки. max — это функция, конечно, надо ставить круглые скобки, даже если нет аргументов. Запускаю код и вижу, что тип int имеет размер 4 байта, его минимальное значение — это −2147483648, максимальное значение по модулю такое же, но на единицу меньше, примерно два миллиарда. Кроме того, этот же прием можно использовать, если вы вдруг в чужом коде увидели какой-то целочисленный тип, про который мы вам не рассказали. Например, мы вам не рассказали про тип long int, потому что вместо него успешно можно использовать типы из модуля cstdint. Давайте посмотрим на тип long int: какой у него размер и минимальное и максимальное значение. Размер обычно 4 или 8 байт. Давайте скомпилируем код, запустим, и действительно, он в данном случае равен типу int, но это не всегда так. Итак, что мы узнали в этом видео? Мы узнали, что в языке C++ больше одного целочисленного типа, там есть не только тип int. И чем эти целочисленные типы отличаются? Во-первых, они отличаются размером, той памятью, которую они занимают, и, соответственно, диапазоном значений. Во-вторых, они отличаются знаковостью или беззнаковостью. Некоторые типы не хранят осознанно отрицательные числа. Мы узнали, что тип int — это стандартный целочисленный тип, у него есть беззнаковый аналог unsigned int, что есть тип size_t — это тип, который возвращают методы size у контейнеров, тип для хранения размеров. Мы узнали про типы, у которых конкретный фиксированный размер, научились узнавать по целочисленному типу его размер и его минимальное и максимальное значение. А дальше мы как раз научимся решать ту проблему, с которой мы начали, — проблему с беззнаковыми типами. Кроме того, также поговорим про переполнение.

Объявление переменных в языке программирования C-51*

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

В языке программирования C51 любая переменная должна быть объявлена до первого использования этой переменной. Как
уже говорилось ранее, этот язык программирования предназначен для написания программ для микроконтроллеров
семейства MCS-51, поэтому в составе языка должна отображаться внутренняя структура этого семейства микроконтроллеров. Эти
особенности отражены во введении новых типов данных. В остальном язык программирования C-51 не отличается от
стандарта ANSI.

Объявление переменной в языке программирования C51 представляется в следующем виде:

   [спецификатор класса памяти]  спецификатор типа  [спецификатор типа памяти] описатель [=инициатор] [,описатель [= инициатор] ]...

Описатель — идентификатор простой переменной либо более сложная конструкция с квадратными скобками, круглыми скобками
или звездочкой (набором звездочек).

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

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

Спецификатор класса памяти — определяется одним из четырех ключевых слов языка С: auto, bit, extern, register, sbit, sfr, sfr16 static auto, extern, register, static, и указывает,
каким образом будет распределяться память под объявляемую переменную, с одной стороны, а с другой, область видимости этой
переменной, т.е., из каких частей программы можно к ней обратиться.

Спецификатор типа памяти — определяется одним из шести ключевых слов языка С-51: code, data, idata, bdata, xdata, pdata и указывает,
в какой области памяти микроконтроллера будет размещена переменная.

Категории типов данных

Ключевые слова для определения основных типов данных

     Целые типы данных:     Типы данных с плавающей запятой:
       bit                            float
       sbit
       char
       int
       short
       long
       signed
       unsigned
       sfr
       sfr16

Переменная любого типа может быть объявлена как неизменяемая. Это достигается добавлением ключевого слова const к спецификатору типа. Объекты с типом const
представляют собой данные, используемые только для чтения, т.е. этой переменной не может быть присвоено новое значение. Отметим, что если после слова const
отсутствует спецификатор типа, то подразумевается спецификатор типа int. Если ключевое слово const стоит перед объявлением составных типов (массив, структура, смесь,
перечисление), то это приводит к тому, что каждый элемент также должен являться немодифицируемым, т.е. значение ему может быть присвоено только один раз.

Примеры использования ключевого слова const:

      const float A=2.128E-2;
      const B=286; //подразумевается const int B=286

Отметим, что переменные с спецификатором класса памяти размещаются во внутреннем ОЗУ. Неизменяемость контролируется только на этапе трансляции. Для размещения
переменной в ПЗУ лучше воспользоваться спецификатором типа памяти code

Целые типы данных

Для определения данных целого типа используются различные ключевые слова, которые определяют диапазон значений и размер области памяти, выделяемой под
переменные (табл. 6).

Таблица 6

ТипРазмер памяти в битахРазмер памяти в байтахДиапазон значений
bit1 от 0 до 1
char81от -128 до 127
unsigned shar81oт 0 до 255
int, short162от -32768 до 32767
long324от -2 147 483 648 до 2 147 483 647
unsigned int, unsigned short162от 0 до 65535
unsigned long324от 0 до 4 294 967 295
sbit1 0 или 1
sfr81oт 0 до 255
sfr16162от 0 до 65535

Отметим, что ключевые слова signed и unsigned необязательны. Они указывают, как интерпретируется нулевой бит объявляемой переменной, т.е., если указано ключевое
слово unsigned, то нулевой бит интерпретируется как часть числа, в противном случае нулевой бит интерпретируется как знаковый. В случае отсутствия ключевого слова
unsigned целая переменная считается знаковой. В том случае, если спецификатор типа состоит из ключевого типа signed или unsigned и далее следует
идентификатор переменной, то она будет рассматриваться как переменная типа int. Например:

unsigned int n;  //Беззнаковое шестнадцатиразрядное число n
unsigned int b;
int c;           //подразумевается  signed   int c
unsigned d;      //подразумевается  unsigned int d
signed f;        //подразумевается  signed   int f

Отметим, что модификатор типа char используется для представления одиночного символа или для объявления строковых литералов. Значением объекта типа char
является код (размером 1 байт), соответствующий представляемому символу.

Отметим также, что восьмеричные и шестнадцатеричные константы также могут иметь модификатор unsigned. Это достигается указанием суффикса u или U после константы,
константа без этого префикса считается знаковой.

Например:

0xA8C   //int  signed
01786l  //long signed
0xF7u   //int  unsigned

Числа с плавающей запятой

Для переменных, представляющих число с плавающей запятой используется модификатор
типа float. Модификатор double тоже допустим в языке программирования C51, но он не приводит к увеличению точности
результата.

Величина с модификатором типа float занимает 4 байта. Из них 1 бит отводится для знака, 8 бит для избыточной
экспоненты и 23 бита для мантиссы. Отметим, что старший бит мантиссы всегда равен 1, поэтому он не заполняется,
в связи с этим диапазон значений переменной с плавающей точкой равен от ±1.175494E-38 до ±3.402823E+38.

Пример объявления переменной с плавающей запятой:

float f, a, b;

Переменные перечислимого типа

Переменная, которая может принимать значение из некоторого списка значений, называется переменной перечислимого типа или перечислением.

Использование такого вида переменной эквивалентно применению целой знаковой переменной char или int. Это означает, что для переменной перечислимого вида
будет выделен один или два байта в зависимости от максимального значения используемых этой переменной констант. В отличие от переменных целого типа, переменные
перечислимого типа позволяют вместо безликих чисел использовать имена констант, которые более понятны и легче запоминаются человеком.

Например, вместо использования чисел 1,2,3,4,5,6,7 можно использовать названия дней недели: Poned, Vtorn, Sreda, Chetv, Pjatn, Subb, Voskr. При этом
каждой константе будет соответствовать свое конкретное число. Однако использование имен констант приведет к более понятной программе. Более того,
транслятор сам позволяет отслеживать правильность использования констант и при попытке использования константы, не входящей в объявленный заранее список,
выдает сообщение об ошибке.

Переменные enum типа могут использоваться в индексных выражениях и как операнды в арифметических операциях и в операциях отношения. Например:

if(rab_ned == SUB) dejstvie = rabota [rab_ned];

При объявлении перечисления определяется тип переменной перечисления и определяется список именованных констант, называемый списком перечисления.
Значением каждого имени этого списка является целое число. Объявление перечислимой переменной начинается с ключевого слова enum и может быть представлено
в двух форматах:

  1. enum [имя типа перечисления] {список констант} имя1 [,имя2 …];
  2. enum имя перечисления описатель [,описатель..];

В первом формате имена и значения констант задаются в списке констант. Необязательное имя типа объявляемой переменной — это идентификатор, который
представляет собой тип переменной, соответствующий списку констант. За списком констант записывается имя одной или нескольких переменных.

Список констант содержит одну или несколько конструкций вида:

идентификатор [= константное выражение]

Каждый идентификатор — это имя константы. Все идентификаторы в списке enum должны быть уникальными. В случае если константе явным
образом не задается число, то первому идентификатору присваивается значение 0, следующему идентификатору — значение 1 и т.д.

Пример объявления переменной rab_ned и типа переменных, совместимых с этой переменной, week выглядит следующим образом:

enum week {SUB = 0, /* константе SUB присвоено значение 0 */
           VOS = 0, /* константе VOS присвоено значение 0 */
           POND,    /* константе POND присвоено значение 1 */
           VTOR,    /* константе VTOR присвоено значение 2 */
           SRED,    /* константе SRED присвоено значение 3 */
           HETV,    /* константе HETV присвоено значение 4 */
           PJAT     /* константе PJAT присвоено значение 5 */
          } rab_ned;

Идентификатор, связанный с константным выражением, принимает значение, задаваемое этим константным выражением. Результат вычисления константного
выражения должен иметь тип int и может быть как положительным, так и отрицательным. Следующему идентификатору в списке, если этот идентификатор не имеет
своего константного выражения, присваивается значение, равное константному выражению предыдущего идентификатора плюс 1. Использование констант должно
подчиняться следующим правилам:

  1. Объявляемая переменная может содержать повторяющиеся значения констант.
  2. Идентификаторы в списке констант должны быть отличны от всех других идентификаторов в той же области видимости, включая имена обычных
    переменных и идентификаторы из других списков констант.
  3. Имена типов перечислений должны быть отличны от других имен типов перечислений, структур и смесей в этой же области видимости.
  4. Значение может следовать за последним элементом списка перечисления.

Во втором формате для объявления переменной перечислимого типа используется уже готовый тип переменной уже объявленный ранее. Например:

enum week rab1;

К переменной перечислимого типа можно обращаться при помощи указателей. При этом необходимо заранее определить тип переменной, на которую будет ссылаться
указатель. Это может быть сделано, как описывалось выше или при помощи оператора typedef. Например:

Typedef enum {SUB = 0, /* константе SUB присвоено значение 0 */
              VOS = 0, /* константе VOS присвоено значение 0 */
              POND,    /* константе POND присвоено значение 1 */
              VTOR,    /* константе VTOR присвоено значение 2 */
              SRED,    /* константе SRED присвоено значение 3 */
              HETV,    /* константе HETV присвоено значение 4 */
              PJAT     /* константе PJAT присвоено значение 5 */
             } week;

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


* Для тех читателей
что вышли на эту страницу по поиску прошу
обратить внимание, что здесь
рассматривается не стандартный язык
программирования С, а язык, адаптированный к
микроконтроллерам серии MCS-51.
Имеются отличия!

[Назад] [Содержание]
[Вперёд]

int, short, long, long long range

Относительно подписанных и неподписанных типов: целые числа int, short и long все по умолчанию для подписанных типов. Чтобы получить тип без знака, тип должен быть указан как без знака, например без знака long. Тип unsigned int может быть сокращен как unsigned, то есть unsigned без какого-либо другого спецификатора типа означает unsigned int.

Один байт представляет восемь битов, то есть: 1 байт = 8 бит;

«C и указатели» писал: long и int: Стандарт только предусматривает, что long не меньше длины int, а int не меньше длины short.
 

Имя типа Количество байтов Диапазон значений
signed char        1         -2^7 ~ 2^7-1    -128~+127

short int          2         -2^14 ~ 2^14-1  -32768~+32767

int                4         -2^31 ~ 2^31-1  -2147483648~+2147483647
unsigned int       4         0 ~ 2^32-1      0 ~ 4294967295

 long int 4 -2 ^ 31 ~ 2 ^ 31-1 -2147483648 ~ + 2141483647 (аналогично int)
unsigned long      4         0 ~ 2^32-1      0~4294967295

long long int      8         -2^63 ~ 2^63-1  -9223372036854775808~+9223372036854775807
unsigned long long 8         0 ~ 2^64-1      18446744073709551615


 Максимальное значение __int64: 9223372036854775807
 Минимальное значение __int64: -9223372036854775808
 Максимальное значение без знака __int64: 18446744073709551615

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

PS: верхний или нижний предел диапазона значений может быть реализован с помощью оператора сдвига влево (<<). Например:

Диапазон значений int может быть выражен как :-( 1 << 31) ~ ((1 << 31) -1)

Сдвиг на один бит влево эквивалентен умножению 2, левый оператор сдвига имеет более низкий приоритет, чем сложение и вычитание! !
 

диапазонов типов данных | Документы Microsoft

  • 2 минуты на чтение

В этой статье

32-разрядные и 64-разрядные компиляторы Microsoft C ++ распознают типы, указанные в таблице далее в этой статье.

  • int ( без знака int )

  • __int8 ( без знака __int8 )

  • __int16 ( без знака __int16 )

  • __int32 ( без знака __int32 )

  • __int64 ( без знака __int64 )

  • короткое ( беззнаковое короткое )

  • длинный ( беззнаковый длинный )

  • длинный длинный ( беззнаковый длинный длинный )

Если его имя начинается с двух знаков подчеркивания ( __ ), тип данных нестандартный.

Диапазоны, указанные в следующей таблице, являются включительно.

Тип Название байта Другие имена Диапазон значений
внутренний 4 подписан -2 147 483 648 до 2 147 483 647
целое без знака 4 без знака 0 до 4 294 967 295
__int8 1 символ -128 до 127
без знака __int8 1 символ без знака от 0 до 255
__int16 2 короткий , короткий внутренний , подписанный короткий внутренний -32 768 до 32 767
без знака __int16 2 беззнаковый короткий , беззнаковый короткий int 0 до 65 535
__int32 4 подпись , подпись , внутр -2 147 483 648 до 2 147 483 647
без знака __int32 4 без знака , без знака int 0 до 4 294 967 295
__int64 8 длинный длинный , подписанный длинный длинный -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807
без знака __int64 8 беззнаковый длинный длинный 0 до 18 446 744 073 709 551 615
болт 1 нет ложно или истинно
символ 1 нет от -128 до 127 по умолчанию

от 0 до 255 при компиляции с использованием / J

символ со знаком 1 нет -128 до 127
символ без знака 1 нет от 0 до 255
короткий 2 короткое внутреннее , подписанное короткое внутреннее -32 768 до 32 767
короткое без знака 2 беззнаковое короткое int 0 до 65 535
длинный 4 длинный int , подписанный длинный int -2 147 483 648 до 2 147 483 647
длинное без знака 4 беззнаковое длинное целое 0 до 4 294 967 295
длинный длинный 8 нет (но эквивалентно __int64 ) -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807
беззнаковый длинный длинный 8 нет (но эквивалентно беззнаковый __int64 ) 0 до 18 446 744 073 709 551 615
Перечисление варьируется нет
поплавок 4 нет 3.4E +/- 38 (7 цифр)
двойной 8 нет 1.7E +/- 308 (15 цифр)
длинный двойной как двойной нет То же, что двойной
wchar_t 2 __wchar_t 0 до 65 535

В зависимости от того, как она используется, переменная __wchar_t обозначает тип расширенных символов или многобайтовых символов.Используйте префикс L перед символьной или строковой константой для обозначения константы типа расширенных символов.

со знаком и без знака — это модификаторы, которые можно использовать с любым целым типом, кроме bool . Обратите внимание, что char , signed char и unsigned char — это три разных типа для таких механизмов, как перегрузка и шаблоны.

Типы int и unsigned int имеют размер четыре байта. Однако переносимый код не должен зависеть от размера int , потому что стандарт языка допускает, чтобы это зависело от реализации.

C / C ++ в Visual Studio также поддерживает целочисленные типы размера. Для получения дополнительной информации см. __int8, __int16, __int32, __int64 и целочисленные пределы.

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

Диапазон перечисляемых типов зависит от языкового контекста и указанных флагов компилятора. Для получения дополнительной информации см. Объявления и перечисления перечислений C.

См. Также

Ключевые слова
Встроенные типы

c — Определение диапазона типа данных

Тип данных — это абстракция, которая применяется к фрагменту памяти, чтобы видеть этот фрагмент памяти как область, которая может представлять значение.

Например, один байт состоит из 8 бит памяти.8 или 256 комбинаций различных значений, которые мы можем представить из 8 бит.

Здесь вступает в игру концепция диапазона — как нам распределить эти 256 комбинаций значений в пригодный для использования диапазон?

Один из способов — принять первую из 256 комбинаций как 0 , а последнюю комбинацию как 255 :

  байт: 0 0 0 0 0 0 0 0 <- Представляет «0»
byte: 0 0 0 0 0 0 0 1 <- Представляет "1"

        .. скоро ..

byte: 1 1 1 1 1 1 1 0 <- Представляет "254"
byte: 1 1 1 1 1 1 1 1 <- Представляет "255"
  

Для этого типа данных диапазон составляет от 0 до 255 .Этот тип обычно называется байтом без знака , поскольку значения, которые он может представлять, составляют без знака , поскольку он не имеет знака. По сути, это обрабатывается так, как если бы все были положительными числами.

С другой стороны, поскольку у нас 256 комбинаций, что, если мы назначим половину из них как положительные числа, а другая половина - отрицательные числа? Итак, мы присваиваем байтовому представлению положительное или отрицательное значение:

  байт: 0 1 1 1 1 1 1 1 <- Представляет "127"
byte: 0 1 1 1 1 1 1 0 <- Представляет «126»

        .. скоро ..

byte: 0 0 0 0 0 0 0 1 <- Представляет "0"
byte: 0 0 0 0 0 0 0 0 <- Представляет "0"
byte: 1 1 1 1 1 1 1 1 <- Представляет «-1»

        .. скоро ..

байт: 1 0 0 0 0 0 0 1 <- Представляет "-127"
byte: 1 0 0 0 0 0 0 0 <- Представляет "-128"
  

Вышеупомянутое представление называется системой «дополнения до двух», а приведенная выше таблица была адаптирована из статьи Википедии о дополнении до двух.

С помощью этого типа представления в тех же 8 битах мы могли бы определить способ представления диапазона чисел от -128 до 127 .Это представление обычно называется байтом со знаком , , потому что это тип байт , который может иметь как положительные, так и отрицательные представления числа.

При сравнении байта без знака и байта со знаком их диапазоны различны:

  байт без знака: 0-255
подписанный байт: -128 - 127
  

Однако они оба имеют 256 возможных комбинаций значений, которые они могут представлять. Они отличаются только диапазоном и значений, которые они могут представлять.Это диапазон типа данных.

Точно так же это может быть расширено до типов int , long , float , double . Количество битов, которые назначаются каждому типу данных, различается. Например:

  int: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <- 16 бит
длинный: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ <- 32 бита
  

Примечание. Фактическое количество битов для каждого типа, например int и long , может зависеть от реализации и архитектуры, поэтому приведенная выше диаграмма не обязательно соответствует действительности.16 или 65536 комбинаций значений, которые он может представлять. Опять же, как и байт , диапазон значений может быть полностью положительным или разделен на положительные и отрицательные:

  целое без знака: 0 - 65535
подписанное целое число: -32768 - 32767
  

(опять же, int не обязательно должно быть 16-битным.)

Типы с плавающей запятой, такие как float и double , также представлены битами в памяти, но их представление данных отличается от целочисленных типов данных, таких как byte и int тем, что они будут хранить значение в памяти как двоичные дроби и показатели.Типы с плавающей запятой также имеют понятие диапазонов.

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

Диапазоны данных типа данных возникают из комбинаций значений, которые могут быть представлены из объема памяти, назначенного для одной единицы одного типа данных, и того, как эти возможные комбинации присваиваются фактическим значениям, которые они представляют.

Целочисленные типы C

Резюме : в этом руководстве вы изучите различные целые типы C , включая целые числа со знаком и без знака.

C целочисленные типы со знаком

C предоставляет вам пять целочисленных типов со знаком. У каждого целочисленного типа есть несколько синонимов.

В следующей таблице показаны первые пять целочисленных типов с соответствующими синонимами:

подписанный int
Целочисленные типы Синонимы Примечания
знаковый символ int

короткий короткий int, подписанный короткий, подписанный короткий int
длинный длинный int, подписанный длинный, подписанный длинный int
длинный длинный длинный int, signed long long, signed long long int Доступно с C99

Целочисленные типы без знака C

Для каждого целого числа со знаком C также предоставляет соответствующий целочисленный тип без знака, который имеет тот же размер памяти, что и целочисленный тип со знаком .

В следующей таблице показан беззнаковый целочисленный тип:

Целочисленные типы со знаком Целочисленные типы без знака
char unsigned char
int

unsigned short
long unsigned long
long long unsigned long long

C диапазоны значений целых типов

C точно определяет минимальный размер памяти для каждого целочисленного типа e.g., short занимает не менее двух байтов, long занимает не менее 4 байтов. Независимо от реализации C, размер целочисленных типов должен соответствовать следующему порядку:

sizeof (short) & lt; sizeof (int) & lt; sizeof (длинный) & lt; sizeof (long long)

В следующей таблице приведены общие размеры целочисленных типов в C:

901 35 2 байта

903

Тип Размер памяти Минимальное значение Максимальное значение
char 1 байт-128 127
беззнаковый символ 1 байт 0 255
со знаком
со знаком

127
int 2 байта или 4 байта -32,768 или -2,147,483,648 32,767 или 2,147,483,647
int без знака 65131
2 байта или 4 байта

короткий 2 байта-32,768 32,767
беззнаковый короткий 0 65,535
длинный 4 байта -2 147 483 648 2 147 483 647
беззнаковый длинный 4,25 4,25 4,2 95 8 байт -9,223,372,036, 854,775,808 9,223,372,036, 854,775,807
длинное без знака длинное 8 байтов 0 181460744673 целочисленное значение 0 18,446,744,673 лимитов.h заголовочный файл . Этот файл заголовка содержит макросы, которые определяют минимальные и максимальные значения каждого целого типа, например, INT_MIN , INT_MAX для минимального и максимального размера целого числа.

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

Чтобы получить размер целочисленного типа или любого другого типа, вы используете оператор sizeof () . Следующая программа демонстрирует, как использовать оператор sizeof () для получения размеров различных целочисленных типов в вашей системе.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/ *

* Файл: main.c

* Автор: zentut.com

* Назначение: отображение размеров целочисленных типов

* /

#include < stdio.h>

int main ()

{

printf ("sizeof (короткий) =% d байтов \ n", sizeof (короткий));

printf ("sizeof (int) =% d bytes \ n", sizeof (int));

printf ("sizeof (unsigned int) =% d bytes \ n", sizeof (unsigned int));

printf ("sizeof (long) =% d bytes \ n", sizeof (long));

возврат 0;

}

В нашей системе выводится следующий результат:

sizeof (short) = 2 байта

sizeof (int) = 4 байта

sizeof (unsigned int) = 4 байта

sizeof (long) = 4 байта

В этом руководстве мы представили различные целочисленные типы C и показали, как использовать оператор sizeof () для получения размеров целочисленных типов в вашей системе.

  • Было ли это руководство полезным?
  • Да Нет

INT08-C. Убедитесь, что все целые значения находятся в диапазоне - стандарт кодирования SEI CERT C

Целочисленные операции должны приводить к целочисленному значению в диапазоне целочисленного типа (т. Е. Результирующее значение совпадает с результатом, полученным с помощью целых чисел с неограниченным диапазоном). Часто диапазон более ограничен в зависимости от использования целочисленного значения, например, в качестве индекса.Целочисленные значения можно проверить с помощью обзора кода или статического анализа.

Целочисленное переполнение - это неопределенное поведение, поэтому скомпилированная программа может делать что угодно, в том числе переходить в «Игру жизни». Кроме того, компилятор может выполнять оптимизацию, предполагающую, что переполнение никогда не произойдет, что может легко привести к неожиданным результатам. Компиляторы могут оптимизировать if, если операторы проверяют, произошло ли переполнение. См. MSC15-C. Не полагайтесь на пример неопределенного поведения.

Проверяемые операции в пределах диапазона часто предпочтительнее обработки значений вне диапазона как состояния ошибки, поскольку обработка этих ошибок неоднократно показывала, что вызывает проблемы отказа в обслуживании в реальных приложениях. Типичным примером является отказ пусковой установки Ariane 5, который произошел из-за неправильно обработанной ошибки преобразования, которая привела к выключению процессора [Lions 1996].

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

Алгоритмы насыщения и modwrap, а также метод использования ограниченного диапазона, определенные в следующих подразделах, дают целочисленные результаты, которые всегда находятся в пределах определенного диапазона. Этот диапазон находится между целыми числами MIN и MAX (включительно), где MIN и MAX - два представимых целых числа с MIN .

Семантика насыщения

Для семантики насыщения предположим, что математическим результатом вычисления является результат .Фактически возвращаемое пользователю значение указано в следующей таблице:

Диапазон математического результата

Возвращенный результат

MAX <результат

MAX

MIN <= результат <= MAX

результат

результат

MIN

Semant

ics Mod Семантика modwrap (также называемая арифметикой

по модулю ), целочисленные значения «оборачиваются»."То есть добавление 1 к MAX дает MIN . Это определенное поведение для целых чисел без знака в Стандарте C, подпункт 6.2.5, параграф 9. Это часто также поведение целых чисел со знаком. Однако, во многих приложениях более разумно использовать семантику насыщения вместо семантики modwrap. Например, при вычислении размера (с использованием целых чисел без знака) часто лучше, чтобы размер оставался на максимальном значении в случае переполнения, а не чем внезапно стать очень маленькой ценностью.

Использование ограниченного диапазона

Другой способ избежать переполнения целых чисел - использовать только половину диапазона целых чисел со знаком. Например, при использовании int используйте только диапазон [ INT_MIN / 2 , INT_MAX / 2 ]. В течение некоторого времени эта практика была уловкой в ​​Фортране, и теперь, когда оптимизирующие компиляторы C стали более сложными, она может оказаться полезной в C.

Рассмотрите возможность вычитания. Если пользователь вводит выражение a - b , где a и b находятся в диапазоне [INT_MIN / 2, INT_MAX / 2] , результат будет в диапазоне (INT_MIN, INT_MAX] для типичной машины с дополнением до двух.

Теперь, если пользователь набирает a , часто происходит неявное вычитание. На машине без кодов условий компилятор может просто выдать команду вычитания и проверить, является ли результат отрицательным. Такое поведение разрешено, потому что компилятору разрешено предполагать, что переполнения нет. Если все явно созданные пользователем значения сохраняются в диапазоне [INT_MIN / 2, INT_MAX / 2] , то сравнения всегда будут работать, даже если компилятор выполнит эту оптимизацию на таком оборудовании.

Пример несоответствующего кода

В этом несоответствующем примере i + 1 будет переполняться на 16-битной машине. Стандарт C допускает переполнение целых чисел со знаком и получение неверных результатов. Компиляторы могут воспользоваться этим для создания более быстрого кода, предполагая, что переполнения не произойдет. В результате оператор if , предназначенный для перехвата переполнения, может быть оптимизирован.

 int i = / * Выражение, возвращающее значение 32767 * /;
/ *... * /
если (я + 1 <= я) {
  / * Обработка переполнения * /
}
/ * Выражение, включающее i + 1 * /
 

Совместимое решение

Использование длинного вместо int гарантированно соответствует вычисленному значению:

 long i = / * Выражение, возвращающее значение 32767 * /;
/ * ... * /
/ * Тест не требуется; i известно, что он не переполняется * /
/ * Выражение, включающее i + 1 * /
 

Оценка рисков

Целочисленные значения вне допустимого диапазона могут привести к чтению или записи в произвольные области памяти и выполнению произвольного кода.

Рекомендация

Серьезность

Вероятность

Стоимость восстановления

9013 9013 Уровень приоритета

Средний

Вероятный

Высокий

P4

L3

9

Инструмент автоматического обнаружения

Версия

Checker

Описание

Astrée

20.10

целочисленное переполнение Полностью проверено
Axivion Bauhaus Suite CertC-INT08
9014IZ

0

9147 914IZ

40. ADDOFLOW

ALLOC.SIZE.IOFLOW

ALLOC.SIZE.MULOFLOW

ALLOC.SIZE.SUBUFLOW

MISC.MEM.SIZE.ADDOFLOW

MISC.MEM.SIZE.BAD

MISC.MEM.SIZE.MULOFLOW

MISC.MEM.SIZE.SUBUFLOW

Добавление переполнения размера выделения

Целочисленное переполнение размера выделения

Переполнение перемножения Размер

Вычитание недостающего размера размера распределения

Добавление переполнения размера

Аргумент необоснованного размера

Переполнение умножения размера

Вычитание недостающего размера размера

Компас / ROSE

Может обнаруживать нарушения этой рекомендации путем отметки любого выражения сравнения, включающего добавление, которое потенциально может переполниться.Например, вместо сравнения a + b (где b и c - константы времени компиляции) и b> c , код должен сравнивать a . (Предполагается, что a , b , c являются беззнаковыми int s. Обычно b является малым значением, а c является верхней границей, например INT_MAX .)

Helix QAC

C2800, C2801, C2802, C2803, C2910, C2911, C2912, C2913

Набор инструментов LDRA

9.7.1

488 S, 493 S, 493 S

Частично реализовано

Тест Parasoft C / C ++ CERT_C-INT08-a

целое число Avoid15

PC-lint Plus

648, 650, 679, 680, 776,
952, 2704

Частично поддерживается

Polyspace Bug Finder 914.INT08-C Проверяет целочисленное переполнение или целочисленное переполнение константы (рекомендация полностью покрыта)
PRQA QA-C

9.7

2800,2801,2802,2803,

2910, 2911,2912,2913

Частично реализовано

Связанные уязвимости

Найдите уязвимости, возникшие в результате нарушения этого правила, на веб-сайте CERT.

Библиография


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

Home / Руководство пользователя компилятора

10.2 Основные типы данных в ARM C и C ++

Описывает основные типы данных, реализованные в ARM C и C ++:

Размер и выравнивание основных типов данных

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

Таблица 10-2 Размер и соответствие типов данных

Тип Размер в битах Естественное выравнивание в байтах Диапазон значений
симв. 8 1 (с выравниванием по байтам)

По умолчанию от 0 до 255 (без знака).

от –128 до 127 (со знаком) при компиляции с --signed_chars .

символ со знаком 8 1 (с выравниванием по байтам)

–128 до 127

символ без знака 8 1 (с выравниванием по байтам)

0 до 255

(подпись) короткая 16 2 (с выравниванием по полуслову) –32 768 до 32 767
короткое без знака 16 2 (с выравниванием по полуслову) 0 до 65 535
(подпись) внутр 32 4 (с выравниванием по словам) –2 147 483 648 до 2 147 483 647
Целое без знака 32 4 (с выравниванием по словам) 0 до 4 294 967 295
(подпись) длинный 32 4 (с выравниванием по словам) –2 147 483 648 до 2 147 483 647
длинное без знака 32 4 (с выравниванием по словам) 0 до 4 294 967 295
(со знаком) длинный длинный 64 8 (с выравниванием по двойному слову) –9,223,372,036,854,775,808 до 9,223,372,036,854,775,807
беззнаковый длинный длинный 64 8 (с выравниванием по двойному слову) 0 до 18 446 744 073 709 551 615
поплавок 32 4 (с выравниванием по словам) 1.175494351e-38 до 3.40282347e + 38 (нормализованные значения)
двойной 64 8 (с выравниванием по двойному слову) 2,22507385850720138e-308 до 1,79769313486231571e + 308 (нормализованные значения)
длинный двойной 64 8 (с выравниванием по двойному слову) 2.22507385850720138e-308 к 1.79769313486231571e + 308 (нормированные значения)
wchar_t

16

32

2 (с выравниванием по полуслову)

4 (с выравниванием по словам)

По умолчанию от 0 до 65 535.

0 до 4,294,967,295 при компиляции с --wchar32 .

Все указатели 32 4 (с выравниванием по словам) Не применимо.
bool (только C ++) 8 1 (с выравниванием по байтам) ложь или правда
_ Болт (только C a ) 8 1 (с выравниванием по байтам) ложь или правда

Выравнивание типов зависит от контекста:

  • Локальные переменные обычно хранятся в регистрах, но
    когда локальные переменные попадают в стек, они всегда выравниваются по словам.Например, в переменной локальной char с разлитой строкой есть
    выравнивание 4.

  • Естественное выравнивание упакованного типа 1.

Целое число

Целые числа представлены в виде дополнения до двух. Низкий
слово длинного длинного находится по младшему адресу в
в режиме прямого порядка байтов и по старшему адресу в режиме прямого порядка байтов.

Поплавок

Величины с плавающей запятой хранятся в формате IEEE:

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

Массивы и указатели

Следующие утверждения применяются ко всем указателям на объекты
в C и C ++, кроме указателей на члены:

  • Соседние байты имеют адреса
    которые отличаются на один.

  • Макрос NULL расширяется до
    значение 0.

  • Приведение между целыми числами и указателями приводит к
    без изменения представительства.

  • Компилятор предупреждает о приведениях между указателями на
    функции и указатели на данные.

  • Тип size_t определяется как без знака
    int
    .

  • Определен тип ptrdiff_t
    как подписано int .

Примитивные типы данных (Руководства по Java ™> Изучение языка Java> Основы языка)

Язык программирования Java является статически типизированным, что означает, что все переменные должны быть сначала объявлены, прежде чем их можно будет использовать. Это включает в себя указание типа и имени переменной, как вы уже видели:

Это сообщает вашей программе, что поле с именем «шестерня» существует, содержит числовые данные и имеет начальное значение «1».Тип данных переменной определяет значения, которые она может содержать, а также операции, которые могут выполняться с ней. Помимо int , язык программирования Java поддерживает еще семь других примитивных типов данных . Примитивный тип предопределен языком и назван зарезервированным ключевым словом. Примитивные значения не разделяют состояние с другими примитивными значениями. Язык программирования Java поддерживает следующие восемь примитивных типов данных:

  • байт : Тип данных байт является 8-битовым целым числом с дополнением до двух со знаком.Он имеет минимальное значение -128 и максимальное значение 127 (включительно). Тип данных байт может быть полезен для экономии памяти в больших объемах.
    массивы, где действительно важна экономия памяти. Их также можно использовать вместо int , где их ограничения помогают прояснить ваш код; тот факт, что диапазон переменной ограничен, может служить формой документации.

  • short : Тип данных short представляет собой 16-битовое целое число со знаком в дополнительном коде.Он имеет минимальное значение -32 768 и максимальное значение 32 767 (включительно). Как и в случае с байтом , применяются те же правила: вы можете использовать короткий для экономии памяти в больших массивах в ситуациях, когда экономия памяти действительно имеет значение.

  • int : по умолчанию тип данных int представляет собой 32-битное знаковое целое число с дополнением до двух, которое имеет минимальное значение -2 31 и максимальное значение 2 31 -1. В Java SE 8 и более поздних версиях вы можете использовать тип данных int для представления 32-битного целого числа без знака, которое имеет минимальное значение 0 и максимальное значение 2 32 -1.Используйте класс Integer, чтобы использовать тип данных int как целое число без знака. См. Раздел «Числовые классы» для получения дополнительной информации. Статические методы, такие как compareUnsigned , diverUnsigned и т. Д., Были добавлены в
    Integer Класс для поддержки арифметических операций с целыми числами без знака.

  • long : Тип данных long - это 64-битное целое число с дополнением до двух. Длинное число со знаком имеет минимальное значение -2 63 и максимальное значение 2 63 -1.В Java SE 8 и более поздних версиях вы можете использовать тип данных long для представления беззнакового 64-битного типа long, который имеет минимальное значение 0 и максимальное значение 2 64 -1. Используйте этот тип данных, когда вам нужен более широкий диапазон значений, чем те, которые предоставляет int . В
    Long Класс также содержит такие методы, как compareUnsigned , DivideUnsigned и т. Д. Для поддержки арифметических операций для длинных значений без знака.

  • с плавающей запятой : Тип данных с плавающей запятой представляет собой 32-битное число с плавающей запятой одинарной точности IEEE 754.Диапазон его значений выходит за рамки данного обсуждения, но указан в
    Раздел «Типы, форматы и значения с плавающей запятой» Спецификации языка Java. Как и в случае с рекомендациями для байт и короткого , используйте с плавающей запятой (вместо double ), если вам нужно сохранить память в больших массивах чисел с плавающей запятой. Этот тип данных никогда не следует использовать для точных значений, таких как валюта. Для этого вам нужно будет использовать
    java.math.BigDecimal вместо этого.Numbers and Strings охватывает BigDecimal, и другие полезные классы, предоставляемые платформой Java.

  • double : тип данных double является 64-битным IEEE 754 с плавающей запятой двойной точности. Диапазон его значений выходит за рамки данного обсуждения, но указан в
    Раздел «Типы, форматы и значения с плавающей запятой» Спецификации языка Java. Для десятичных значений этот тип данных обычно выбирается по умолчанию. Как упоминалось выше, этот тип данных никогда не следует использовать для точных значений, таких как валюта.

  • boolean : Тип данных boolean имеет только два возможных значения: true и false . Используйте этот тип данных для простых флагов, которые отслеживают истинные / ложные условия. Этот тип данных представляет один бит информации, но его «размер» не совсем точно определен.

  • char : Тип данных char - это один 16-битный символ Unicode. Он имеет минимальное значение '\ u0000' (или 0) и максимальное значение '\ uffff' (или 65 535 включительно).

В дополнение к восьми примитивным типам данных, перечисленным выше, язык программирования Java также обеспечивает специальную поддержку символьных строк через
java.lang.String класс. Заключение строки символов в двойные кавычки автоматически создаст новый объект String ; например, String s = "это строка"; . String Объекты являются неизменяемыми , что означает, что после создания их значения не могут быть изменены. Класс String технически не является примитивным типом данных, но, учитывая особую поддержку, предоставляемую ему языком, вы, вероятно, будете думать о нем как о таковом.Вы узнаете больше о классе String в
Простые объекты данных

Значения по умолчанию

Не всегда необходимо присваивать значение при объявлении поля. Поля, которые объявлены, но не инициализированы, будут установлены компилятором в разумные значения по умолчанию. Вообще говоря, это значение по умолчанию будет равно нулю или null , в зависимости от типа данных. Однако использование таких значений по умолчанию обычно считается плохим стилем программирования.

В следующей таблице приведены значения по умолчанию для указанных выше типов данных.

Тип данных Значение по умолчанию (для полей)
байт 0
короткий 0
внутренний 0
длинный 0L
поплавок 0,0f
двойной 0,0d
символ '\ u0000'
Строка (или любой объект) null
логический ложь

Локальные переменные немного отличаются; компилятор никогда не присваивает значение по умолчанию неинициализированной локальной переменной.Если вы не можете инициализировать свою локальную переменную там, где она объявлена, не забудьте присвоить ей значение, прежде чем пытаться использовать ее. Доступ к неинициализированной локальной переменной приведет к ошибке времени компиляции.

Литералы

Вы могли заметить, что ключевое слово new не используется при инициализации переменной примитивного типа. Примитивные типы - это особые типы данных, встроенные в язык; они не объекты, созданные из класса. Литерал - это представление исходного кода фиксированного значения; литералы представлены непосредственно в вашем коде, не требуя вычислений.Как показано ниже, можно присвоить литерал переменной примитивного типа:

логический результат = истина;
char capitalC = 'C';
байт b = 100;
короткий s = 10000;
int i = 100000;
 
Целочисленные литералы

Целочисленный литерал имеет тип длиной , если он заканчивается буквой L или l ; в противном случае это тип int . Рекомендуется использовать прописную букву L , поскольку строчную букву l трудно отличить от цифры 1 .

Значения целочисленных типов byte , short , int и long могут быть созданы из int литералов. Значения типа long , выходящие за пределы диапазона int , могут быть созданы из long литералов. Целочисленные литералы можно выразить с помощью этих систем счисления:

  • Десятичный: Основание 10, цифры которого состоят из чисел от 0 до 9; это система счисления, которую вы используете каждый день
  • Шестнадцатеричный: основание 16, цифры которого состоят из цифр от 0 до 9 и букв от A до F
  • Двоичный: База 2, цифры которого состоят из чисел 0 и 1 (вы можете создавать двоичные литералы в Java SE 7 и более поздних версиях)

Для универсального программирования десятичная система, вероятно, будет единственной системой счисления, которую вы когда-либо будете использовать.Однако, если вам нужно использовать другую систему счисления, следующий пример показывает правильный синтаксис. Префикс 0x указывает на шестнадцатеричный, а 0b указывает на двоичный:

// Число 26 в десятичном формате
int decVal = 26;
// Число 26 в шестнадцатеричном формате
int hexVal = 0x1a;
// Число 26 в двоичном формате
int binVal = 0b11010;
 
Литералы с плавающей запятой

Литерал с плавающей запятой имеет тип float , если он заканчивается буквой F или f ; в противном случае его тип - double и может оканчиваться буквой D или d .

Типы с плавающей запятой ( float и double ) также могут быть выражены с помощью E или e (для экспоненциальной записи), F или f (32-битный литерал с плавающей запятой) и D или d (64-битный двойной литерал; это является значением по умолчанию и по соглашению опускается).

двойной d1 = 123,4;
// то же значение, что и d1, но в экспоненциальном представлении
двойной d2 = 1.234e2;
float f1 = 123.4f;
 
Символьные и строковые литералы

Литералы типов char и String могут содержать любые символы Unicode (UTF-16).Если ваш редактор и файловая система позволяют это, вы можете использовать такие символы непосредственно в своем коде. Если нет, вы можете использовать «escape-последовательность Unicode», например '\ u0108' (заглавная C с циркумфлексом) или «S = Se \ u00F1or» (Sí Señor на испанском языке). Всегда используйте «одинарные кавычки» для символьных литералов и «двойные кавычки» для строковых литералов . Управляющие последовательности Unicode могут использоваться в другом месте программы (например, в именах полей), а не только в литералах char или String .

Язык программирования Java также поддерживает несколько специальных escape-последовательностей для литералов char и String : \ b (backspace), \ t (tab), \ n (перевод строки), \ f (подача страницы), \ r (возврат каретки), \ " (двойная кавычка), \ ' (одинарная кавычка) и \ (обратная косая черта).

Существует также специальный литерал null , который можно использовать как значение для любого ссылочного типа. null может быть присвоено любой переменной, кроме переменных примитивных типов. Вы мало что можете сделать со значением null , кроме проверки его наличия. Следовательно, null часто используется в программах в качестве маркера, чтобы указать, что какой-то объект недоступен.

Наконец, существует также особый вид литерала, называемый литералом класса , образованный путем взятия имени типа и добавления « .class» ; например String.class .Это относится к объекту (типа , класс ), который представляет сам тип.

Использование символов подчеркивания в числовых литералах

В Java SE 7 и более поздних версиях любое количество символов подчеркивания ( _ ) может находиться где угодно между цифрами в числовом литерале. Эта функция позволяет вам, например. для разделения групп цифр в числовых литералах, что может улучшить читаемость вашего кода.

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

В следующем примере показаны другие способы использования символа подчеркивания в числовых литералах:

long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F;
длинный hexBytes = 0xFF_EC_DE_5E;
длинные шестнадцатеричные слова = 0xCAFE_BABE;
длинный maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
длинные байты = 0b11010010_01101001_10010100_10010010;
 

Знаки подчеркивания можно ставить только между цифрами; нельзя ставить подчеркивания в следующих местах:

  • В начале или конце числа
  • Рядом с десятичной запятой в литерале с плавающей запятой
  • До F или L суффикс
  • В позициях, где ожидается строка цифр

Следующие примеры демонстрируют допустимые и недопустимые размещения подчеркивания (выделенные) в числовых литералах:

//  Недействительно: нельзя ставить символы подчеркивания 
//  рядом с десятичной точкой 
поплавок pi1 = 3_.1415F;
//  Недействительно: нельзя ставить символы подчеркивания 
//  рядом с десятичной точкой 
float pi2 = 3._1415F;
//  Недействительно: нельзя ставить символы подчеркивания 
//  перед суффиксом L 
long socialSecurityNumber1 = 999_99_9999_L;

// ОК (десятичный литерал)
интервал x1 = 5_2;
//  Недействительно: нельзя ставить символы подчеркивания 
//  В конце литерала 
int x2 = 52_;
// ОК (десятичный литерал)
int x3 = 5_______2;

//  Недействительно: нельзя ставить символы подчеркивания 
//  в префиксе системы счисления 0x 
int x4 = 0_x52;
//  Недействительно: нельзя ставить символы подчеркивания 
//  в начале числа 
int x5 = 0x_52;
// ОК (шестнадцатеричный литерал)
int x6 = 0x5_2;
//  Недействительно: нельзя ставить символы подчеркивания 
//  в конце числа 
int x7 = 0x52_;
 

4.5 - Целые числа без знака и почему их следует избегать

Алекс, 23 апреля 2019 г. | последнее изменение nascardriver 20 февраля 2021 г.

Целые числа без знака

На предыдущем уроке (4.4 - Целые числа со знаком) мы рассмотрели целые числа со знаком, которые представляют собой набор типов, которые могут содержать положительные и отрицательные целые числа, включая 0.

C ++ также поддерживает целые числа без знака. Беззнаковые целые числа - это целые числа, которые могут содержать только неотрицательные целые числа.

Определение целых чисел без знака

Чтобы определить целое число без знака, мы используем ключевое слово без знака .По соглашению это помещается перед типом:

беззнаковый короткий нам;

беззнаковый int ui;

беззнаковая длинная ul;

беззнаковый длинный длинный ull;

Целочисленный диапазон без знака

1-байтовое целое число без знака имеет диапазон от 0 до 255. Сравните это с диапазоном 1-байтового целого числа со знаком от -128 до 127. Оба могут хранить 256 различных значений, но целые числа со знаком используют половину своего диапазона для отрицательных чисел, тогда как целые числа без знака могут хранить положительные числа, которые в два раза больше.

Вот таблица, показывающая диапазон для целых чисел без знака:

Размер / тип Диапазон
1 байт без знака от 0 до 255
2 байта без знака 0 до 65 535
4 байта без знака 0 до 4 294 967 295
8 байт без знака 0 до 18 446 744 073 709 551 615

n-битовая переменная без знака имеет диапазон от 0 до (2 n ) -1.

Если отрицательные числа не требуются, целые числа без знака хорошо подходят для сетей и систем с небольшим объемом памяти, поскольку целые числа без знака могут хранить больше положительных чисел, не занимая дополнительной памяти.

Запоминание условий подписанный и неподписанный

Новые программисты иногда путают подписанные и неподписанные. Вот простой способ запомнить разницу: чтобы отличить отрицательные числа от положительных, мы используем отрицательный знак. Если знак не указан, мы предполагаем, что число положительное.Следовательно, целое число со знаком (целое число со знаком) может отличать положительное от отрицательного. Целое число без знака (целое число без знака) предполагает, что все значения положительны.

Целочисленное без знака переполнение

Что произойдет, если мы попытаемся сохранить число 280 (для представления которого требуется 9 бит) в 1-байтовом (8-битном) целом числе без знака? Ответ - переполнение.

Как ни странно, в стандарте C ++ прямо говорится, что «вычисление с использованием беззнаковых операндов никогда не может переполниться».Это противоречит общему мнению программистов о том, что целочисленное переполнение охватывает как подписанные, так и неподписанные варианты использования. Учитывая, что большинство программистов рассмотрело бы это переполнение, мы будем называть его переполнением, несмотря на утверждения C ++ об обратном.

Если значение без знака выходит за пределы допустимого диапазона, оно делится на единицу, превышающую наибольшее число типа, и сохраняется только остаток.

Число 280 слишком велико для нашего 1-байтового диапазона от 0 до 255. На 1 больше наибольшего числа типа 256.Следовательно, мы делим 280 на 256, получая 1 остаток 24. Остаток 24 - это то, что сохраняется.

Вот еще один способ думать о том же. Любое число, превышающее наибольшее число, представленное типом, просто «оборачивается» (иногда это называется «обертывание по модулю» или, что более неясно, «насыщенность»). 255 находится в диапазоне 1-байтового целого числа, поэтому 255 подойдет. 256, однако, находится за пределами диапазона, поэтому он переходит к значению 0. 257 переходит к значению 1. 280 переходит к значению 24.

Давайте посмотрим на это, используя 2-байтовые целые числа:

#include

int main ()

{

беззнаковый короткий x {65535}; // максимальное возможное 16-битное беззнаковое значение

std :: cout << "x was:" << x << '\ n';

х = 65536; // 65536 находится за пределами нашего диапазона, поэтому мы получаем обтекание

std :: cout << "x is now:" << x << '\ n';

х = 65537; // 65537 находится за пределами нашего диапазона, поэтому мы получаем обтекание

std :: cout << "x is now:" << x << '\ n';

возврат 0;

}

Как вы думаете, каким будет результат этой программы?

 x было: 65535
x сейчас: 0
x сейчас: 1
 

Также можно обернуть в другую сторону.0 можно представить в виде 2-байтового целого числа, так что это нормально. -1 не является представимым, поэтому он завершается до вершины диапазона, производя значение 65535. -2 оборачивается до 65534. И так далее.

#include

int main ()

{

беззнаковый короткий x {0}; // наименьшее возможное 2-байтовое значение без знака

std :: cout << "x was:" << x << '\ n';

х = -1; // -1 вне нашего диапазона, поэтому мы получаем обтекание

std :: cout << "x is now:" << x << '\ n';

х = -2; // -2 вне нашего диапазона, поэтому мы получаем обтекание

std :: cout << "x is now:" << x << '\ n';

возврат 0;

}

 x было: 0
x сейчас: 65535
x сейчас: 65534
 

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

Многие заметные ошибки в истории видеоигр произошли из-за поведения циклического перехода с беззнаковыми целыми числами. В аркадной игре Donkey Kong невозможно пройти уровень выше 22 из-за ошибки переполнения, из-за которой у пользователя недостаточно бонусного времени для завершения уровня.

В компьютерной игре Civilization Ганди был известен тем, что часто был первым, кто применил ядерное оружие, что, похоже, противоречит его ожидаемой пассивности.Игроки полагали, что это результат агрессии Ганди, изначально установленной на 1, но если он выберет демократическое правительство, он получит модификатор -2. Это приведет к тому, что его агрессия превысит 255, что сделает его максимально агрессивным! Однако совсем недавно Сид Мейер (автор игры) пояснил, что на самом деле это не так.

Споры по поводу чисел без знака

Многие разработчики (и некоторые крупные компании, такие как Google) считают, что разработчикам следует избегать целых чисел без знака.

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

Сначала рассмотрим вычитание двух беззнаковых чисел, таких как 3 и 5. 3 минус 5 равно -2, но -2 не может быть представлено как беззнаковое число.

#include

int main ()

{

unsigned int x {3};

целое число без знака y {5};

std :: cout << x - y << '\ n';

возврат 0;

}

На машине автора эта, казалось бы, невинно выглядящая программа дает результат:

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

Во-вторых, при смешивании целых чисел со знаком и без знака может возникнуть непредвиденное поведение. В приведенном выше примере, даже если один из операндов (x или y) подписан, другой операнд (беззнаковый) приведет к тому, что подписанный будет преобразован в целое число без знака, и будет получено такое же поведение!

Рассмотрим следующий фрагмент:

void doSomething (unsigned int x)

{

// Выполнить код x раз

std :: cout << "x is" << x << '\ n';

}

int main ()

{

doSomething (-1);

возврат 0;

}

Автор doSomething () ожидал, что кто-то вызовет эту функцию только с положительными числами.Но звонящий проходит по номеру -1 . Что происходит в этом случае?

Аргумент со знаком -1 неявно преобразуется в беззнаковый параметр. -1 не входит в диапазон беззнакового числа, поэтому он завершается до некоторого большого числа (вероятно, 4294967295). Тогда ваша программа становится баллистической. Хуже того, нет хорошего способа предотвратить это состояние. C ++ может свободно преобразовывать числа со знаком и без знака, но не проверяет диапазон, чтобы убедиться, что вы не переполняете свой тип.

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

Некоторые современные языки программирования (например, Java) и фреймворки (например, .NET) либо не включают беззнаковые типы, либо ограничивают их использование.

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

Избегайте использования чисел без знака, за исключением особых случаев или когда это необходимо.

Не избегайте отрицательных чисел, используя беззнаковые типы. Если вам нужен больший диапазон, чем предлагает число со знаком, используйте одно из целых чисел гарантированной ширины, показанных в следующем уроке (4.6 - Целые числа фиксированной ширины и size_t).

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

Так где же разумно использовать беззнаковые числа?

В C ++ все еще есть несколько случаев, когда можно (или необходимо) использовать беззнаковые числа.

Во-первых, беззнаковые числа предпочтительнее при работе с битами (рассматривается в главе O (это заглавная буква «о», а не «0»).

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

Также обратите внимание, что если вы разрабатываете для встроенной системы (например, Arduino) или какого-либо другого контекста с ограничением процессора / памяти, использование чисел без знака более распространено и приемлемо (а в некоторых случаях неизбежно) по соображениям производительности.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *