- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
int table[4];
bool exists_in_table(int v)
{
for (int i = 0; i <= 4; i++) {
if (table[i] == v) return true;
}
return false;
}
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
+51
int table[4];
bool exists_in_table(int v)
{
for (int i = 0; i <= 4; i++) {
if (table[i] == v) return true;
}
return false;
}
C хабра. Возвращает true для любых чисел. http://ideone.com/bL2vGQ
Я конечно знаю что такое UB, но этот пример повеселил.
На месте компилятора я бы рассуждал так - возвращаем
UB может быть и true и false, но если оно true, то мы можем оптимизировать функцию до одной строчки, а если false - придется перебирать первые элементы. Следовательно, будем считать его true.
1. Условие завершения цикла i<=4.
2. В цикле есть обращение к table[4].
Почему обращение к несуществующему table[4] только на чтение что-то портит?
Кмк, компилятор вворачивает какую-нибудь оптимизацию, которая отключает проверку в цикле. Я уже видел похожие прелести.
Вероятно, он рассуждает примерно так:
Хм, в массиве всего 4 элемента, i не может превзойти 3 физически, значит, уловие в цикле эквивалентно true (ведь программист не может быть дебилом, верно?). Пожалуй, заменю его на true, получу ПИРФОМАНС
P.S. У меня 32-битный gcc не зацикливает с любыми ключами оптимизации. Проблема ideone или amd64?
А версии GCC точно одинаковые? Скорее всего, у одного из вас либо ещё нет этого бага, либо он уже починен.
GCC 4.9.0 и 4.8 с -O2 вообще всё выпиливает: С -O1 разворачивает цикл, как в моём примере.
А вот GCC 4.5, 4.6 и 4.7 с -O2 выдаёт подлинный говнокод: Сравнивает указатель с указателем на конец массива, причём с помощью равенства (p == &table[4] вместо p > &table[3]), что и приводит к зацикливанию.
P.S. С -O1 ещё веселее: копирует 32-битные значения, а сравнивает 64-битные.
То, о чём я говорил, проявляется здесь:
kipar выше отлично все пояснил:
http://govnokod.ru/16352#comment239843
>UB может быть и true и false, но если оно true, то мы можем оптимизировать функцию до одной строчки, а если false - придется перебирать первые элементы. Следовательно, будем считать его true.
1. Версии gcc >= 4.8 оптимизируют код до return true, вероятно, считая, что вероятность совпадения значения несуществующего элемента с введённым числом высока. Т. е. UB равно каждому введённому числу безо всякой случайности.
2. Версии gcc от 4.5 до 4.7 тупо зацикливаются и сканируют оперативку, потому что оптимизируют условие выхода из цикла до строгого равенства.
Походу компилятор рассуждает так: "Функция либо возвращает true на первых четырех итерациях, либо имеет UB. Программист не дурак, и UB не допустит, значит функция всегда возвращает true."
>jne .L3
Никогда не понимал ну вот нахрена люди пишут выход из цикла по строгому равенству?
for (int i=0;i!=n;++i)
Или indexOf возвращает -1, я обычно стараюсь писать (indexOf()<0), на всякий случай. Те кто if (i=indexOf()!=-1) s.subString(i) должны страдать. Потому что когда-нибудь он может вернуть -2.
Ну напиши, больше/меньше что тебе стоит. Не хотим, уверены что не переполнится и не проскочит через условие.
Речь о числах.
Сейчас надо кастануть Тараса, который расскажет что в Аде есть встроенные числовые типы с ограниченным интервалом.
Дык а какая разница-то. Идея одна и та же: начинай с нуля и итерируйся до < END. Код в точности один и тот же что для итераторов, что для чисел.
Разные числовые проверки , не только итерация. Можно использовать строгое равенство.
Вот мы знаем что у нас конечная точка должа быть END, и сравниваем строго.
if (x==END) break;
А я говорю, неужели трудно сравнить if (x>=END) break;
Такие проверки не раз выручали, когда в алгоритме выявлялись проёбы или он менялся, например, было i++, поменяли на i+=2 - можем проскочить END, если у него не та чётность.
Пример c indexOf - это ведь не итерация, тут STL не поможет.
Да, тут виновата семантика indexOf.
Кстати, std::string::find() возвращает исключительно положительные беззнаковые числа, а в случае провала возвращается максимальное беззнаковое число (по совместительству std::size_t(-1); предполагатеся, что таких больших строк не бывает). При таком раскладе идиома полузакрытых интервалов прекрасно работает.
Не знаю. Это лучше исключений, и лучше подхода того же Go - возвращать джва значения в одном результат, в другом его успешность.
Всё-равно надо проверять и успешность, и валидность результата.
Концепция возврат явно невалидного значения, у сфейлившей функции (вернуть null, когда ничего не найдено) лично мне нравится.
Семантически она ужасна. В качестве хака, нехило повышающего производительность, сойдёт.
А какие альтернативы?
Ну вернул невалидное. Если это сишка - надо всё-равно руками проверить код возврата/диапазоны.
Если в языке есть исключения зачастую внутри есть проверка - получим тоже исключение.
В сишке - почти никаких. Там даже нельзя возвращать указатель вместо индекса, как в STL. Ибо конец строки быстро не получить :(
В более высокоуровневых языках - Maybe / Option(al), возможность ошибки должна проверяться на уровне типа.
Исключения в такие операции явно не вписываются.
Так это просто обычная типобезопасная обёртка, содержащая указатель.
А вот частичные функции (для ограниченого подмножества аргументов заданного типа), из них можно извлечь какой-то профит?
С точки зрения семантики "вернуть либо валидный индекс, либо ничего" и "вернуть число, возможно, индекс" - совершенно разные вещи. Первый вариант без проверки не скомпиляется, второй вариант скомпиляется без проблем. Ну и необходимость думать о виде неравенства сразу отпадает.
А можно пример. Хотя бы на скакалке.
мне лень писать код, идея должна быть ясна:
indexOf вызывается не просто так, мы как-то хотим использовать этот индекс. Взять substring, например. Но substring принимает Index, а не Maybe Index.
Т.е. с Maybe/Option нужно будет проверить полноту коробки и вытащить индекс, иначе типы не сойдутся.
Если же indexOf возвращает int, его можно совать в substring без каки-либо проверок, компилятору всё равно.
А неправильное трактование. int воспринимается как целое и монолитное. А он им не является. На самом деле он составной, состоит из 32-х кусочков (bit).
int не надо представлять цельным, надо вообразить что это кортеж из джвух значений (старший бит) флаг наличия ошибки, младшие 31 бит (индекс элемента).
Подобным образом в коллекциях я например возвращал индекс вставленного объекта плюс флаг - изменена этой вставкой коллекция или нет. Не плодить же ради этого структуру, или хуже того кортеж с боксингом. Альтернатива - джва вызова тоже отстой.
К сожалению, компилятор не обладает вашим воображением.
> Не плодить же ради этого структуру
>> В качестве хака, нехило повышающего производительность, сойдёт.
Не спорю.
> компилятор не обладает вашим воображением.
Шланг вероятнее всего i31 и i1 упакуeт.
http://ideone.com/EZTSva
Ой не факт, ой не факт... Если это локальные переменные, то он, скорее всего, тупо догонит их до разрядности проца (32 или 64) и будет прав.
Так ведь в сишке уже отработана концепция возврата натурального числа как результата успешно завершившейся функции и (не существующих в природе) отрицательных чисел как кодов ошибок.
И мне даже не жалко половины диапазона интов - пущай берут, у меня их ещё много :)
А errno?!
Можно конечно обсирать, но по сути точно так же и в асме - глобальный регистр статуса, и в каждом ядре свой (thread-local).
Не понимаю почему не сделали такой же глобальной переменной для регистра флагов.
Ну там бит переноса или переполнение подсмотреть - такое на всех платформах есть. Имхо, упростило бы жизнь.
И вроде как значение errno не определено, если функция вернула корректное значение... Но я могу ошибаться.
Its value is significant only when the return value of the call indicated an error.
Никто же не запрещает использовать в своих функциях для передачи ошибки. Главное задокументировать - проверьте errno.
А тех аргументов в winapi - тьма.
Ну и по ссылке возвращать int из indexOf - извиняйте. Крестобляди такого не потерпят. Только стек, только хардкор.
std::string::npos
Уродливая хрень. Сравнивать с ним, имхо отстой.
Мда s.indexOf(find)<0 на беззнаковых не сработает. Я б наверное сделал так ~s.indexOf(find).
>>максимальное беззнаковое число (по совместительству std::size_t(-1);
>беззнаковое число -1
О блядские кресты!!! Они там совсем ебанулись? Беззнаковый -1?!
Нет, ну так обосраться и получить underflow и/или конвертацию signed в unsigned прямо в доке.
static const size_t npos = -1;
Святое плюсодерьмо.
Культурный, образованный человек написал бы ~0. Но нахуя? Это слишком скушно. Вот -1 ведь гораздо интереснее, это резко повышает шансы разработчика проебать.
а что в этом страшного? конвертация сигнед в унсигнед, вроде, вполне известно задекларирована - беззнаковое представление должно тупо бит в бит соответствовать знаковому
другое дело, что -1 в прямом коде это 100...001, т.е. обожемой, сокращаем максимальный беззнаковый интервал в половину
и, самое печальное, все, кто пострадал от STL на архитектурах с прямым кодом, давно ликвидированы, и даже родственникам запрещено сообщать места их захоронений
Её следует избегать, как источник раздирающих жопу ошибок. И особенно необходимо избегать сравнений signed и unsigned.
Вот кто-то так же посудит и будет сравнивать с константой из доки:
s.indexOf(find)==-1
Оно заработает с неявной конвертацией, но это опасно.
Хотя бы тем что у такого как я есть желание поменять на s.indexOf(find)<0, -1 всегда меньше нуля. Так меня в школе обучали.
Вот он: http://goo.gl/u6Ahtt
Либо в table есть 42 либо имеем UB. Значит в table есть 42.
А ты как думаешь? :)
Нет. Если ты его не попросишь. Вежливо.
Но и тогда у него может быть плохое настроение, или ему будет просто лень ;)
Именно за это все любят кресты.
This option instructs the compiler to assume that signed arithmetic overflow of addition, subtraction and multiplication wraps around using twos-complement representation.
Особенно мне это нравится:
This flag enables some optimizations and disables other.
Ололо, КРЕСТОПРОБЛЕМЫ
This option is enabled by default for the Java front-end, as required by the Java language specification.
слово "дохуя умный" не всегда является усиленной версией слова "умный"
Но если бы еще чуток умнее, то оставил хотя бы из тех соображений, что на данной конкретной машине используется вот такой код.
Не проебали, а выебнулись: если на входе Число Тараса - то при его отрицании имеем UB (всем похуй на результат), а для остальных чисел модуль всегда положителен, и позволяет убрать лишнюю проверку, в надежде на то, что программист опытный, и Число Тараса туда не передаст.
P.S. Но не самая приятная оптимизация, да.
Это умиляет.
Уже вещества завезли?
Где можно купить?
http://ideone.com/CujF6o
P.S. Кстати, никакие -fwrapv этот код не спасут - он просто перестанет выводить это число (т.к. abs(числа тараса) отрицателен). Так что даже не надейтесь отмахаться опцией ;)
P.P.S. Да-да, жабисты, вас это тоже касается, причем еще больше, чем сишников - ибо в жабе нет unsigned.
http://ideone.com/TFW7G7
-x = ~x+1
-(1<<31) = ~(1<<31)+1 = 01111111111111111....1 +1 = 1000000000000000...0
Все ок
http://ideone.com/weT53G
http://ideone.com/t5b7T0
Ответ прост - что бы сделать совместимость с костылем нужен еще один костыль
http://www.youtube.com/watch?v=giC3-LnnV4c
http://ideone.com/5XKhX0
В прямом, блеать:
Хотя я бы на такой код тупо выдавал ошибку. Ибо такой код нинужен.
Так в чем опечатка то?)
Я не спорю, исключение намного лучше, чем возврат хуйни. Но... в шарпике же есть unsigned. Почему бы не описать Math.Abs как unsigned Abs(int x)? Тогда вообще бы не было никаких проблем...
Угу, исключения на Math.Abs.
Я бы сделал, повторюсь, доступные потоку флаги.
И не надо говорить что глобальное состояние не вписывается в реалии современного программинга .
Я даже знаю куда это положить: Thread.currentThread().wasOverflow()
Там же лежит Thread.currentThread().interrupt() - ставит точно такой же глобальный флаг прерывания, а Thread.currentThread().isInterrupted() читает этот флаг.
Глобальное (per-thread) мутабельное состояние. И ничё.
А в жабе вообще нет встроенных средств произошло ли переполнение при умножении например. Существуют конечно байтоебские приемы, но процессор ведь это уже посчитал. Зачем извращаться?
Дык оборачивание в unchecked() переключает режим обработки с trap на wrap. И никакой переголовы не остается. Байтоёбствуй сколько душе угодно.
А для остальных случаев, где не байтоёбство, а именно операции с целыми числами, исключение на переполнениях само то: и кривую хуйню случайно не вернет, и никакие флаги программисту проверять не надо.
Пример: реализация арифметики очень больших целых.
Переполнение тут не исключительная ситуация, а вполне повседневная часть алгоритма.
> а именно операции с целыми числами, исключение на переполнениях само то
Вероятнее тут одно из двух - или инструмент выбран неправильно - вам не нужны целые числа, а нужна длинная арифметика, или всё же ошибка переполнения не нужна. Хотя всякое бывает, и исключения могут быть полезны.
Согласен, но не совсем. Есть carry (который как раз повседневная часть алгоритмов - лишний бит вылез за разрядную сетку), а есть overflow (который только для знаковых интов, и, имхо, таки исключительная ситуация - знаковый бит запороло переполнением из предыдущего разряда).
помню лет 6 назад мутил длинную арифметику на строках - такое говнецо я вам скажу
Дык логически это большое число - знаковый int в twos complement или sign-magnitude. А вот физически это пачка unsigned'ов.
Поэтому с результатом всё будет норм, но в промежуточных рассчетах всё будет беззнаковым.
> длинную арифметику на строках
Да, это тот еще гемор... Равно как и BCD. Да и имеет смысл только если десятичное i/o происходит на порядок чаще чем расчеты. Т.е. чуть менее, чем никогда.
Я нихуя не понял, если честно ;) Ты о том, что эту реализацию он должен скрывать от всех? Ну да. И флаг у него там или twos complement - всем пофиг. Для юзеров библиотеки он выглядит как хаскелевый Integer или жабий BigInteger - просто большой, знаковый и удобный.
>>Иногда при байтоебствовании хочется понимать было переполнение/перенос или нет.
Лично мне не помешало бы и то, и другое, хотя чем overflow в unsingned не carry? При том что кодер знает что только что он складывал числа.
>если бы он был доступен
В этом и проблема.
Тем, что после него потом еще и знак выправлять у переполнившегося числа.
unsigned же.
А у unsigned'а в принципе не бывает overflow (переполнения) у него может быть только carry (перенос).
Да неужели?
Ну вообще-то это называется переполнение, потому что тип конечный.
За пруфами гуглить unsigned integer overflow.
>в принципе не бывает overflow (переполнения)
Или надо предоставить контрпруф.
Собственно из определения термина переполнение понятно: оно возникает когда результат не может быть выражен величиной регистра. unsigned совсем не означает безразмерный.
P.S. Что-то я становлюсь похож на царя с этими unsigned'ами...
- для байтоёбства - unsigned only в режиме unchecked (да, жаль, что флаг недоступен)
- для вычислений с небольшими числами - signed и unsigned в режиме checked (т.к. wraparound здесь нахуй не сдался, и исключение никому не помешает).
- для вычислений с большими числами - класс для больших чисел
P.S. А JIT шарп'а умеет превращать конструкции вот такого типа во флагоёбство?
Короче, нахер исключения.
Если это правда, то оптимальное сложение BigInteger'ов можно накодить только на асме, под каждую конкретную платформу :(
Пишем один универсальный для получения флага, и ifdefами частные варианты под платформу. JIT, когда увидит в байт-коде использование флага, скомпилирует его вычисление.
>processors don't have a carry flag at all (MIPS, Alpha)
А overflow? А как mono будучи портируемым на эти платформы будет проверять переполнение?
Берешь и сопровождаешь все варианты ;)
Тут либо байтоёбство и скорость, либо универсальность и удобство правки... Третьего не дано.
Из третьего выходит говно в стиле - отлаживаем производительность. там, где она и так высока, а на узкое место кладем хуй
>>if ((a>~b))
Дооо. Это чрезвычайно трудно сопровождать.
>А сопроводжаемость кода как же?
Конечно иногда приятно поебать байты и покушать ежа, но и меру нужно знать
Ну это уже много раз разбирали.
>-fwrapv этот код не спасут - он просто перестанет выводить это число (т.к. abs(числа тараса) отрицателен).
Это вполне очевидное и ожидаемое поведение.
> Да-да, жабисты, вас это тоже касается, причем еще больше, чем сишников
Практически всех касается.
>числа тараса
Откуда название?
Тарас вроде какой-то ГК с ним постил. Или в пример его приводил... Не помню уже...
P.S. Шарпье исключение, имхо, поадекватней чем минус без числа ;) Сразу видно, что нарвались на косяк.
Не Тарасом единым. Уже очень давно постилось на гк.
>Шарпье исключение, имхо, поадекватней чем минус без числа ;)
Уже надоело этот ГК #3117 приводить в пример.
И читать отрицательные проще, и проблемы нет. Ну да, отрицательный ноль, но мы и покруче видали в IEEE 754:
sqrt(-0) == -0
моя реакция:
(-0) == -0
Может быть арифметика сложнее получается? Лень сейчас думать-вспоминать, но вроде бы там вычитатель через тупой сумматор уже не замутить?
Да. Так и есть.
Считать внутри как удобно. Но! Поставить на выходе копеешную схему преобразования в обратный код, а на входе в дополнительный
Или допустим VM проектируют, ведь биты их можно интерпретировать как угодно.
Вот в жабе говорите нет unsigned, нет типа, для гурманов появились байтоебские методы которые трактуют int как unsigned и могут делить/умножать/печатать.
Ну, видимо, в то время, когда все это выбиралось - это было слишком дорого... А теперь уже слишком поздно. Кресты то все стерпят, они и с ones complement работать будут (хоть и с горением жоп у тех, кто юзал UB'ы с переполнением int'ов и касты отрицательных int'ов в unsigned). А вот у жабы и шарпа в спеке навсегда увековечено twos complement.
this International Standard permits 2’s complement, 1’s complement and signed magnitude representations for integral types
об этом есть в стандарте
Крестухи опять соснули особенно учитывая тупые проебы в других частях языка, что абсолютно верно подмечено выше:
>другое дело, что -1 в прямом коде это 100...001, т.е. обожемой, сокращаем максимальный беззнаковый интервал в половину
>и, самое печальное, все, кто пострадал от STL на архитектурах с прямым кодом, давно ликвидированы, и даже родственникам запрещено сообщать места их захоронений
Как вариант - можно было остановиться на том, что npos это максимальное значение size_t, и вообще не писать, как его получают в домашних условиях. И код вообще перестал бы зависеть от реализации знаковых чисел на целевой платформе. Никаких половин диапазонов и т.п.