В чем разница между литералами и переменными в C (signed vs unsigned short ints)?
я видел следующий код в книге компьютерные системы: перспектива программиста, 2 / E. Это хорошо работает и создает желаемый результат. Вывод может быть объяснен разницей подписанного и неподписанного представлений.
#include<stdio.h>
int main() {
if (-1 < 0u) {
printf("-1 < 0un");
}
else {
printf("-1 >= 0un");
}
return 0;
}
код выше дает -1 >= 0u
, однако, следующий код, который должен быть таким же, как и выше, не! Другими словами,
#include <stdio.h>
int main() {
unsigned short u = 0u;
short x = -1;
if (x < u)
printf("-1 < 0un");
else
printf("-1 >= 0un");
return 0;
}
доходность -1 < 0u
. Почему это произошло? Я не могу объяснить этот.
обратите внимание, что я видел похожие вопросы как этой, но они не помогают.
PS. Как сказал @Abhineet, дилемма может быть решена путем изменения short
to int
. Но как объяснить это явление? Другими словами,-1
в 4 байта это 0xff ff ff ff
и в 2 байт 0xff ff
. Учитывая их как 2S-дополнение, которое интерпретируется как unsigned
, они имеют соответствующие значения 4294967295
и 65535
. Они оба не менее 0
и я думаю, что в обоих случаях на выходе должен быть -1 >= 0u
, то есть x >= u
.
образец вывода для него на маленькой системе Intel endian:
для краткости:
-1 < 0u
u =
00 00
x =
ff ff
для int:
-1 >= 0u
u =
00 00 00 00
x =
ff ff ff ff
4 ответов
приведенный выше код дает -1 >= 0u
все целочисленные литералы (числовое constansts) имеют вид и, следовательно, также со знаком. По умолчанию, они имеют тип int
, который подписан. Когда вы добавляете u
суффикс, вы превращаете литерал в unsigned int
.
для любого выражения C, где у вас есть один операнд, который подписан и один, который не указан, правило балансировки (формально:обычные арифметические конверсии) неявное преобразование типа к unsigned.
преобразование из signed в unsigned четко определено (6.3.1.3):
в противном случае, если новый тип без знака, значение преобразуется путем многократного сложения или вычитание на одно больше максимального значения, которое может быть представлено в новом типе до тех пор, пока значение находится в диапазоне нового типа.
например, для 32-битных целых чисел на стандартных двух система дополнения, максимальное значение целого числа без знака -2^32 - 1
(4294967295, UINT_MAX в пределах.ч.) Один больше, чем максимальное значение 2^32
. И -1 + 2^32 = 4294967295
, так что буквальный -1
преобразуется в unsigned int со значением 4294967295
. Который больше 0.
при переключении типов на короткие, однако, вы в конечном итоге с небольшое целое число типа. В этом разница между двумя примерами. Всякий раз, когда малый целочисленный тип является частью выражения, the целочисленное правило продвижения неявно преобразует его в больший int (6.3.1.1):
если int может представить все значения исходного типа (Как ограничено по ширине, для битового поля), значение преобразуется в int; в противном случае он преобразуется в unsigned int. Они называются целочисленные акции. Все остальные типы не изменяются по целому числу специальные акции.
если short
меньше, чем int
на данный платформа (как и в случае с 32 и 64 битными системами), любая short
или unsigned short
поэтому всегда будет преобразовываться в int
, потому что они могут поместиться внутри.
Итак, для выражения if (x < u)
, вы на самом деле в конечном итоге с if((int)x < (int)u)
который ведет себя так, как ожидалось (-1 меньше 0).
вы сталкиваетесь с целочисленными правилами продвижения C.
операторы на типах меньше, чем int
автоматически продвигать свои операнды к int
или unsigned int
. Более подробные объяснения см. В комментариях. Есть еще один шаг для бинарные (два операнда) оператора, если типы не совпадают после этого (например, беззнаковый Инт и Инт). Я не буду пытаться суммировать правила более подробно. см. ответ Лундина.
этот блог пост охватывает это более подробно, с аналогичным примером для вашего: подписанный и неподписанный символ. Он цитирует спецификацию C99:
если int может представить все значения исходного типа, значение преобразуется в int; в противном случае он преобразуется в unsigned int. Они называются целочисленными промо-акциями. Все остальные типы не изменились целыми акциями.
вы можете играть с этим более легко на что-то вроде godbolt, С функцией, которая возвращает один или ноль. Просто посмотрите на выходные данные компилятора, чтобы увидеть, что происходит.
#define mytype short
int main() {
unsigned mytype u = 0u;
mytype x = -1;
return (x < u);
}
кроме того , что вы, похоже, предполагаете, это не свойство определенной ширины типов, здесь 2 байта против 4 байтов, но вопрос о правилах, которые должны применяться. Целочисленные правила продвижения гласят, что short
и unsigned short
превращается в int
на всех платформах, где соответствующий диапазон значений укладывается в int
. Так как это имеет место здесь, оба значения сохраняются и получают тип int
. -1
это прекрасно представимы в int
как 0
. Итак, результаты теста в -1
меньше, чем 0
.
в случае проверки -1
против 0u
общее преобразование выбрал unsigned
тип, общий тип, к которому обращаются. -1
преобразовано в unsigned
значение UINT_MAX
, что превышает 0u
.
это хороший пример, почему вы никогда не должны использовать "узкие" типы для арифметики или сравнения. Используйте их только если у вас размер север ограничение. Это редко будет иметь место для простых переменных, но в основном для больших массивов, где вы действительно можете получить от хранения в узком типе.
0u
неunsigned short
, этоunsigned int
.
Edit:: объяснение поведения, как выполняется сравнение ?
как ответил Йенс Gustedt,
это называется "обычные арифметические преобразования" по стандарту и применяется всякий раз, когда два разных целочисленных типа встречаются как операнды тот же оператор.
в сущности то, что делает
если типы разные ширина (точнее какой стандарт calls conversion rank), то он преобразуется в более широкий тип, если оба типы имеют одинаковую ширину, кроме действительно странных архитектур, беззнаковое из них wins подписывается на беззнаковое преобразование значения -1 с любым типом всегда приводит к наивысшему представимому значению типа без знака.
более объяснительный блог, написанный им, можно найти здесь.