ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

Некоторые особенности типа int в C++

Операционная система: «Windows 7 Профессиональная» (SP1), 64-разрядная. Компилятор среды «Visual Studio Community 2017».

int — основной в языке тип целого числа со знаком. signed int — синоним int.

В 8-разрядных и 16-разрядных системах int занимал 2 байта, в современных 32- и 64-разрядных системах занимает обычно 4 байта. Предполагая, что в байте 8 бит (существуют системы, в которых в байте другое количество бит), получаем, что 4 байта — это 32 бита.

Если в программе мы работаем только с положительными целыми числами, то можно использовать тип unsigned int (целое без знака). При этом в 4 байта можно поместить 2^n (два в степени n) значений, где n = 32 (количество разрядов или битов). Из-за того, что в диапазон представляемых значений необходимо включить число 0 (ноль), диапазон представляемых типом unsigned int значений будет не 0..2^n, как можно было бы подумать, а

0..(2^n - 1)
или 0..(2^32 - 1)
или 0..(4'294'967'296 - 1)
или 0..4'294'967'295

(границы входят в диапазон представляемых значений)

Теперь рассмотрим случай, когда в программе мы работаем и с положительными целыми числами, и с отрицательными целыми числами. Для этого можно использовать тип int (или синоним signed int, как уже упоминалось выше). Теперь в 4 байта необходимо помещать и отрицательные, и положительные целые числа.

Самый старший бит (тот, который находится слева) отводится для хранения знака числа (0 — число положительное, 1 — число отрицательное). Значит, у нас остается для представления чисел 31 бит: 31 бит для отрицательных чисел и 31 бит для положительных чисел. То есть мы можем представить с помощью типа int 2^(n-1) отрицательных чисел и 2^(n-1) положительных чисел (n = 32, как мы помним). Однако, как и для типа unsigned int, для типа int требуется куда-то включить число 0 (ноль). Его включают в положительные числа, хотя оно, как мы помним из математики, не является ни положительным, ни отрицательным. Исходя из этого, диапазон представляемых типом int отрицательных чисел будет -2^(n-1)..-1, а диапазон представляемых типом int положительных чисел будет 0..2^(n-1)-1, то есть

-2^(n-1)..-1 и 0..2^(n-1)-1
или -2^(n-1)..2^(n-1)-1
или -2^(32-1)..2^(32-1)-1
или -2^31..2^31-1
или -2'147'483'648..2'147'483'648-1
или -2'147'483'648..2'147'483'647

(границы входят в диапазон представляемых значений)

Вот почему компилятор позволяет такой диапазон представляемых типом int значений, несмотря на то, что в стандарте языка Си установлен диапазон значений, определяемый по формуле -(2^(n-1)-1)..2^(n-1)-1, то есть

-2'147'483'647..2'147'483'647 (согласно стандарта)
-2'147'483'648..2'147'483'647 (фактическая реализация)

Кстати, в тексте программ позволяется использовать апострофы в качестве разделителей прямо в числовых литералах, что, по-моему, очень удобно. Например:
int a = 2147483647;
int b = 2'147'483'647;

Перейдем к интересным неочевидным штукам. Например, в результате выражения
int b = 2'147'483'648;
переменной b присваивается не указанное значение, а это же значение, только со знаком минус: -2'147'483'648. Это происходит потому, что значение справа от знака присваивания считается компилятором значением типа unsigned long, а не значением типа int (как должно быть по умолчанию для числового литерала), так как оно выходит за рамки диапазона представляемых типом int значений. Далее компилятор пытается записать в переменную типа int значение, которое туда не может поместиться, и бит значения числа записывается в самый старший бит, который предназначен для хранения знака числа, а не битов значения числа.

Еще один казус. Попытка использовать числовой литерал с наименьшим возможным для типа int значением -2'147'483'648 приведет к ошибке C4146:
int b = -2'147'483'648;

Почему это происходит? Потому что компилятор не включает знак минуса в понятие числового литерала, то есть сначала рассматривается значение 2'147'483'648, которое не может быть значением типа int, как уже указывалось выше, и считается значением типа unsigned long (целое без знака). После этого компилятор пытается применить унарную операцию отрицания к целому без знака, которое не может быть отрицательным, что логично является ошибкой.

Что делать? Рекомендуется применять вместо значения -2'147'483'648 встроенную константу INT_MIN с этим значением, определенную в заголовочном файле limits.h, либо писать вот так:
int b = -2'147'483'647 - 1;
Tags: Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 7 comments