- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
int naive_show_int(int x) {
char buf[32];
char *p = buf + sizeof(buf);
*--p = 0;
int negative = 0;
if (x < 0) {
x = -x;
negative = 1;
}
while (x > 0) {
if (x <= 0)
return -1;
int digit = '0' + x % 10;
if (digit < '0' || digit >= '9')
return -1;
*--p = digit;
x /= 10;
}
if (negative)
*--p = '-';
puts(p);
return 0;
}
Т.е. он в while входит с отрицательным x, затем игнорирует все проверки внутри цикла. Творится сущая хренота.
P.S. Кстати, деление на 10 он заменил умножением на 1717986919.
> Ну и все проверки убирает
А в случае сужения диапазона что творится? Это поддаётся объяснению?
http://ideone.com/DHWxQN
P.S. А вот про семёрку непонятно, надо дизасм глядеть.
Короче, в случае с восьмёркой компилятор видит только одно кривое число '9'. С ним он и сравнивает, а потом делает je. Т.е. получается if (digit == '9') return -1.
В случае с семёркой он честно сравнивает с семёркой, но юзает ja, как для unsigned'ов (ведь digit не может быть отрицательным). Т.е. if ((unsigned)digit > 7u) return -1. Но digit у нас отрицательный, т.е. 0xFFFFFчётотам, что явно больше 7. Поэтому и происходит return -1.
Два косяка случайно скомпенсировали друг друга и получилось правильное поведение. UB такой UB.
> Но digit у нас отрицательный, т.е. 0xFFFFFчётотам
Однако, новость. Компилятор считает, что т.к. x>0, можно просто от x % 10 откусить один байт и сложить с '0'? А нет, там int же. Компилятор считает, что т.к. x>0, можно просто от x % 10 откусить один байт и сложить с '0' потому, что потом значение используется как char?
Там сначала были assert'ы, потом на if'ы поменял, чтобы не докапывались "да у тебя же ассёрты выкокомпилились".
Хе-хе. А UB то это не убирает (переполнение знакового числа в операторе -), просто заметает под ковёр до поры до времени... Пока в компилятор не добавят новую хитровыебанную оптимизацию, которая понадеется, что старший бит этого unsigned'а всегда будет нулём...
Или там предлагают число кастануть в unsigned, а минус заменить на битовую магию?
Ололо, она таки есть! http://ideone.com/oFheOh
Т.е. (unsigned)(-x) тоже неплохо стреляет в колено...
Т.е. остаётся только сначала кастовать в unsigned (благо, емнип, это стандарту не противоречит и вполне документировано) и в нём уже делать отрицание.
Shifts: 00000080 00000040 00000020 00000010 00000008 00000004 00000002 00000000
prog.c:9:17: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
Да и в новых GCC этот -fsanitize=undefined вроде как появился
> return -1;
А за что так с девяткой?
Но взгляните сюда: http://ideone.com/DHWxQN (разные ограничения на digit)
Какой эффект!
Цитата из стандарта нужна?
0x80000000 разве int? Тогда уж -0x80000000
Да, правильное замечание.
> int x;
> x = -x;
Спойлер: http://www.cplusplus.com/reference/climits/
Где здесь C++, bormand?!
http://goo.gl/0K4VFR - c оптимизацией в GCC получаем бесконечный цикл. Без оптимизации (или с оптимизацией -O1) возвращает -2147483648
Вот ещё пример конечного/бесконечного цикла в зависимости от оптимизаций. Пикантности прибавляет факт, что условие цикла не зависит от UB. Но, наличие UB не гарантирует работу даже частей программы до него.
> x /= 10;
Простите мне мою пупость. Вы што, ожидали получить отрицателное число при делении 2 положительных???
или я что-то проглядел?
???????????????????
ЗЫ. переполнение?
Хех, не научились они пока доказывать инварианты по индукции. Вот и не выбросило проверки. С while (x > 0) инвариант цикла более явный, там выбрасывает "невыполнимые" ветки.