Будет ли это нарушать язык или существующий код, если мы ' d добавить безопасные сравнения со знаком / без знака в C / C ++?

Прочитав этот вопрос о сравнениях со знаком / без знака (они появляются каждые пару дней, я бы сказал):

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

#include 
#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

Скомпилированный с моим стандартным компилятором (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 C ++ и найдите пункты, описывающие, как операнды преобразуются в общий тип, и это может сломаться, если общий тип переосмысливает отрицательные значения.

А как же «Почему?». Как мы видим, символ «

  • Процесс преобразования и сравнения на самом деле не является ярким примером для Правила наименьшего сюрприза

  • Я не верю, что существует код, основанный на предположении, что (коротко) -1> (без знака) 1 и не написано террористами.

  • Это все ужасно, когда вы находитесь в C ++ с шаблоном кода, потому что вам нужно набрать магию черты, чтобы связать правильный "


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

signed X < unsigned Y -> (a<(X)0) || ((Z)a<(Z)b) where Z=X|Y 

Предварительная проверка является дешевой и может также быть оптимизирована компилятором, если a> = 0 может Статически доказано.

Итак, вот мой вопрос:

Будет ли нарушен язык или существующий код, если мы добавим безопасные сравнения со знаком / без знака в C / C ++?

(«Это нарушит язык» означает, что нам нужно внесите значительные изменения в разные части языка, чтобы учесть это изменение)


ОБНОВЛЕНИЕ: в C ++ с шаблоном кода, потому что вам нужно волшебство черты типа, чтобы связать правильное "


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

signed X < unsigned Y -> (a<(X)0) || ((Z)a<(Z)b) where Z=X|Y 

Предварительная проверка обходится дешево и может также быть оптимизирована компилятором, если статическое подтверждение> 0 =.

Итак, вот мой вопрос:

Будет ли нарушен язык или существующий код, если мы добавим безопасные сравнения со знаком / без знака в C / C ++?

(«Это нарушит язык» означает, что нам нужно внесите значительные изменения в разные части языка, чтобы учесть это изменение)


ОБНОВЛЕНИЕ: в C ++ с шаблоном кода, потому что вам нужно волшебство черты типа, чтобы связать правильное "


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

signed X < unsigned Y -> (a<(X)0) || ((Z)a<(Z)b) where Z=X|Y 

Предварительная проверка обходится дешево и может также быть оптимизирована компилятором, если статическое подтверждение> 0 =.

Итак, вот мой вопрос:

Будет ли нарушен язык или существующий код, если мы добавим безопасные сравнения со знаком / без знака в C / C ++?

(«Это нарушит язык» означает, что нам нужно внесите значительные изменения в разные части языка, чтобы учесть это изменение)


ОБНОВЛЕНИЕ:

signed X < unsigned Y -> (a<(X)0) || ((Z)a<(Z)b) where Z=X|Y 

Предварительная проверка является дешевой и может также оптимизироваться компилятором, если a> 0 может быть статически доказано.

Итак, вот мой вопрос:

Будет ли нарушен язык или существующий код, если мы добавим безопасные сравнения со знаком / без знака в C / C ++?

(«Это нарушит язык» означает, что нам нужно внесите значительные изменения в разные части языка, чтобы учесть это изменение)


ОБНОВЛЕНИЕ:

signed X < unsigned Y -> (a<(X)0) || ((Z)a<(Z)b) where Z=X|Y 

Предварительная проверка является дешевой и может также оптимизироваться компилятором, если a> 0 может быть статически доказано.

Итак, вот мой вопрос:

Будет ли нарушен язык или существующий код, если мы добавим безопасные сравнения со знаком / без знака в C / C ++?

(«Это нарушит язык» означает, что нам нужно внесите значительные изменения в разные части языка, чтобы учесть это изменение)


ОБНОВЛЕНИЕ: Я запустил это на своем старом добром Turbo-C ++ 3.0 и получил вывод:

char:1
(signed  char)-1 < (unsigned  char)1 = 0

Почему (знаковый символ) -1 здесь?

20
задан Community 23 May 2017 в 11:45
поделиться

4 ответа

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

if (x-'0' < 10U)

Не говоря уже о таких вещах, как (сравнение на равенство):

size_t l = mbrtowc(&wc, s, n, &state);
if (l==-1) ... /* Note that mbrtowc returns (size_t)-1 on failure */

В стороне, указание «естественного» поведения для смешанных сравнений со знаком / без знака также повлечет за собой значительные снижение производительности, даже в программах, которые в настоящее время используют такие сравнения безопасными способами, где они уже имеют свое «естественное» поведение из-за ограничений на вводе, которые компилятору будет трудно определить (или, возможно, не сможет определить вообще) . Я уверен, что при написании собственного кода для обработки этих тестов вы уже видели, как будет выглядеть снижение производительности, и это некрасиво.

8
ответ дан 30 November 2019 в 00:47
поделиться

Я не думаю, что это сломает язык, но да, это может сломать какой-то существующий код (и эту поломку, вероятно, будет трудно обнаружить на уровне компилятора).

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

Полагаться на «предположение, что (краткое) -1> (беззнаковое) 1 » может быть сделано кем-то непреднамеренно. Существует много кода C, связанного со сложными манипуляциями с битами и подобными вещами. Вполне возможно, что какой-то программист может использовать текущее поведение сравнения в таком коде. (Другие люди уже предоставили хорошие примеры такого кода, и код даже проще, чем я ожидал).

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

7
ответ дан 30 November 2019 в 00:47
поделиться

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

c ++ 0x - и boost - являются примерами ужасного ужасного синтаксиса - такого ребенка могут любить только его родители - и далеки от простого элегантного (но сильно ограниченного) c ++ 10 лет назад.

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

И после того, как он сломан, есть еще много всего, что также может быть исправлено с обратной силой.

1
ответ дан 30 November 2019 в 00:47
поделиться

Мой ответ только для C.

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

Следовательно, вы не можете реализовать математическое сравнение значений, например x <= y , сначала преобразовав x и y в общий тип, а затем выполнив простая операция. Это серьезное отклонение от общего принципа работы операторов. Это также разрушает интуитивное представление о том, что операторы соответствуют вещам, которые обычно представляют собой отдельные инструкции в обычном оборудовании.

Даже если вы добавите к языку дополнительную сложность (и дополнительную нагрузку для авторов реализации), он не будет иметь очень хороших свойств. Например, x <= y все равно не будет эквивалентно x - y <= 0 . Если вам нужны все эти прекрасные свойства, вам придется сделать частью языка целые числа произвольного размера.

Я уверен, что существует много старого кода Unix, возможно, работающего на вашем компьютере, который предполагает, что (int) -1> (unsigned) 1 . (Хорошо, может быть, это написали борцы за свободу; -)

Если вам нужен lisp / haskell / python / $ favourite_language_with_bignums_built_in, вы знаете, где его найти ...

11
ответ дан 30 November 2019 в 00:47
поделиться