Целое со знаком в более широкое целое со знаком.Меньшее значение расширяется со знаком, например приведение (char)0x7f к int дает 0x0000007f, но (char)0x80 становится равно 0xffffff80.
Целое со знаком в целое без знака того же размера.Комбинация битов сохраняется, значение может измениться или остаться неизменным. Так, (char)0xff (-1) после приведения к типу unsigned char становится равно 0xff, но ясно, что–1 и 255 – это не одно и то же.
Целое со знаком в более широкое целое без знака.Здесь сочетаются два предыдущих правила. Сначала производится расширение со знаком до знакового типа нужного размера, а затем приведение с сохранением комбинации битов. Это означает, что положительные числа ведут себя ожидаемым образом, а отрицательные могут дать неожиданный результат. Например, (char) -1 (0xff) после приведения к типу unsigned long становится равно 4 294 967 295 (0xffffffff).
Целое без знака в более широкое целое без знака.Это простейший случай: новое число дополняется нулями, чего вы обычно и ожидаете. Следовательно, (unsigned char)0xff после приведения к типу unsigned long становится равно
0x000000ff.
Целое без знака в целое со знаком того же размера.Так же как при приведении целого со знаком к целому без знака, комбинация битов сохраняется, а значение может измениться в зависимости от того, был ли старший (знаковый) бит равен 1 или 0.
Целое без знака в более широкое целое со знаком.Так же как при приведении целого без знака к более широкому целому без знака, значение сначала дополняется нулями до нужного беззнакового типа, а затем приводится к знаковому типу. Значение не изменяется, так что никаких сюрпризов в этом случае не бывает.
Понижающее приведение.Если в исходном числе хотя бы один из старших битов был отличен от нуля, то мы имеем усечение, что вполне может привести к печальным последствиям. Возможно, что число без знака станет отрицательным или произойдет потеря информации. Если речь не идет о битовых масках, всегда проверяйте, не было ли усечения.
Преобразования при вызове операторов
Большинство программистов не подозревают, что одного лишь обращения к оператору достаточно для изменения типа результата. Обычно ничего страшного не происходит, но граничные случаи могут вас неприятно удивить. Вот код на С++, иллюстрирующий проблему:
...
template
void WhatIsIt(T value)
{
if((T)-1 < 0)
printf("Со знаком");
else
printf("Без знака");
printf(" – %d бит\n", sizeof(T)*8);
}
Для простоты оставим в стороне случай смешанных операций над целыми и числами с плавающей точкой. Правила формулируются так:
□ если хотя бы один операнд имеет тип unsigned long, то оба операнда приводятся к типу unsigned long. Строго говоря, long и int – это два разных типа, но на современных машинах тот и другой имеют длину 32 бита, поэтому компилятор считает их эквивалентными;
□ во всех остальных случаях, когда длина операнда составляет 32 бита или меньше, операнды расширяются до типа int, и результатом является значение типа int.
Как правило, ничего неожиданного при этом не происходит, и неявное приведение в результате применения операторов может даже помочь избежать некоторых переполнений. Но бывают и сюрпризы. Во–первых, в системах, где имеется тип 64–разрядного целого, было бы логично ожидать, что коль скоро unsigned short и signed short приводятся к int, а операторное приведение не нарушает корректность результата (по крайней мере, если вы потом не выполняете понижающего приведения до 16 битов), то unsigned int и signed int будут приводиться к 64–разрядному типу (_int64). Если вы думаете, что все так и работает, то вынуждены вас разочаровать – по крайней мере, до той поры, когда стандарт C/C++ не станет трактовать 64–разрядные целые так же, как остальные.
Вторая неожиданность заключается в том, что поведение изменяется еще и в зависимости от оператора. Все арифметические операторы (+, – ,*,/,%) подчиняются приведенным выше правилам. Но им же подчиняются и поразрядные бинарные операторы (&,|,А); поэтому (unsigned short) | (unsigned short) дает int! Те же правила в языке С распространяются на булевские операторы (&&,|| и !), тогда как в С++ возвращается значение встроенного типа bool. Дополнительную путаницу вносит тот факт, что одни унарные операторы модифицируют тип, а другие–нет. Оператор дополнения до единицы (~) изменяет тип результата, поэтому -((unsigned short)0) дает int, тогда как операторы префиксного и постфиксного инкремента и декремента (++, – ) типа не меняют.
Читать дальше
Конец ознакомительного отрывка
Купить книгу