После прочтения этого вопроса о сопоставленных/неподписанных сравнениях (они появляются каждые пару дней, я бы сказал):
Я задавался вопросом, почему у нас нет правильных сравнений без знака, и вместо этого этот ужасный беспорядок? Возьмите выход из этой небольшой программы:
#include <stdio.h>
#define C(T1,T2)\
{signed T1 a=-1;\
unsigned T2 b=1;\
printf("(signed %5s)%d < (unsigned %5s)%d = %d\n",#T1,(int)a,#T2,(int)b,(a<b));}\
#define C1(T) printf("%s:%d\n",#T,(int)sizeof(T)); C(T,char);C(T,short);C(T,int);C(T,long);
int main()
{
C1(char); C1(short); C1(int); C1(long);
}
Скомпилированный с моим стандартным компилятором (gcc, 64bit), я получаю следующее:
char:1
(signed char)-1 < (unsigned char)1 = 1
(signed char)-1 < (unsigned short)1 = 1
(signed char)-1 < (unsigned int)1 = 0
(signed char)-1 < (unsigned long)1 = 0
short:2
(signed short)-1 < (unsigned char)1 = 1
(signed short)-1 < (unsigned short)1 = 1
(signed short)-1 < (unsigned int)1 = 0
(signed short)-1 < (unsigned long)1 = 0
int:4
(signed int)-1 < (unsigned char)1 = 1
(signed int)-1 < (unsigned short)1 = 1
(signed int)-1 < (unsigned int)1 = 0
(signed int)-1 < (unsigned long)1 = 0
long:8
(signed long)-1 < (unsigned char)1 = 1
(signed long)-1 < (unsigned short)1 = 1
(signed long)-1 < (unsigned int)1 = 1
(signed long)-1 < (unsigned long)1 = 0
Если я компилирую для 32 бит, результат будет таким же, за исключением того, что:
long:4
(signed long)-1 < (unsigned int)1 = 0
"Как?" из всего этого легко найти: просто перейдите к разделу 6.3 стандарта C99 или главе 4 на С++ и выкопайте предложения, которые описывают, как операнды преобразуются в общий тип, и это может сломаться, если общий тип переинтерпретирует отрицательные значения.
Но как насчет "Почему?". Как мы видим, '<' не работает в 50% случаев, также зависит от конкретных размеров типов, поэтому он зависит от платформы. Вот несколько моментов, которые следует учитывать:
-
Процесс конвертации и сравнения на самом деле не является ярким примером правила наименьшего сюрприза
-
Я не верю, что есть код, который опирается на предложение, что
(short)-1 > (unsigned)1
и не написано террористами. -
Это ужасно, когда вы находитесь на С++ с кодом шаблона, потому что вам нужна магия свойств типа, чтобы вязать правильный "<".
В конце концов, сравнение значений подписи и без знака различных типов легко реализовать:
signed X < unsigned Y -> (a<(X)0) || ((Z)a<(Z)b) where Z=X|Y
Предварительная проверка является дешевой и также может быть оптимизирована компилятором, если a >= 0 может быть проверено статически.
Итак, вот мой вопрос:
Может ли он сломать язык или существующий код, если мы добавим безопасные сопоставленные/неподписанные сравнения к C/С++?
( "Разве он сломал бы язык" означает, что нам нужно внести массовые изменения в разные части языка, чтобы приспособить это изменение).
UPDATE: Я запускал это на своем старом Turbo-С++ 3.0 и получил этот вывод:
char:1
(signed char)-1 < (unsigned char)1 = 0
Почему (signed char)-1 < (unsigned char) == 0
здесь?