- 1
- 2
if(!true) // это - не говнокод. это - заглушка.
return false;
Нашли или выдавили из себя код, который нельзя назвать нормальным, на который без улыбки не взглянешь? Не торопитесь его удалять или рефакторить, — запостите его на говнокод.ру, посмеёмся вместе!
+142
if(!true) // это - не говнокод. это - заглушка.
return false;
Оправдывается ещё )
Напомнило: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
Спасибо за ссылку. Красивая фраза там в конце третьей части - C migrated from being a "low level systems programming language that was a tiny layer above PDP assembly" to being a "low level systems programming language, trying to provide decent performance by breaking many people's expectations.
Слабо написать парсер довольно примитивного синтаксиса? ;)
А грабли то, в основном, не синтаксические, а логические. Ради того, чтобы компилятору и его разработчику было свободнее, в стандарте рассована куча UB'ов и ID'ов. А почти любой UB - потенциальный разрыв шаблона для того, кто сталкивается с ним в первый раз, ну разве что кроме самых очевидных, до которых можно самому догадаться. Да и даже во вполне определенных областях есть грабли - например если int a = -5 и unsigned b = 10, то что вернет a<b? А чему равна 1/2? А еще до с++11 не было никакого понятия о тредах, все это отдавалось на откуп реализации, со всеми вытекающими последствиями...
Пример - разадресация NULL'а. В жабошарпике это определенное поведение - вброс экцепшена. В сишке же все думают, что должен быть краш\сегфолт и т.п. Но на самом деле это UB, и может случиться все что угодно, от нормального продолжения работы до краша.
Сравнил мягкое место с пальцем. Синтаксис сложен для парсера, но для человека он не так сложен. Это как человеку раз плянуть распознать на картинке пару объектов, но алгоритмически над этим ломают годы уже десятилетия, как же тупой компьютер научить распознавать объекты без предварительного брутфорса и огромного количества ошибок.
>сравнение int и unsigned int
>В сишке же все думают, что должен быть краш\сегфолт и т.п. Но на самом деле это UB
По-моему, это довольно очевидные вещи из разряда "здравый смысл". Алсо, это всё corner cases, с которыми вполне можно жить.
Относительно большинства многих других языков с++ как раз таки сложен. Как синтаксисом так и семантикой. Что-то даже не приходит на вскидку пример языка, который бы оказался сложнее с++ по этим характеристикам. Поможете?
> По-моему, это довольно очевидные вещи из разряда "здравый смысл".
Ну дык. Если иметь 2-3 года опыта в c/с++, когда-либо сталкиваться с похожей проблемой или где-то о ней прочесть, и смотреть на эту проблему на маленьком куске кода - то все очевидно...
Вот реальный случай из проекта моего друга: проект компилируется без ошибок и предупреждений, но вылетает посреди работы. valgrind показывает доступ за границы выделенной памяти в конструкторе некого класса. Догадаетесь ли вы с первого раза, что в проект закралась лишняя ашка, в которой описана обрезанная версия класса, видя которую new выделяет намного меньше памяти чем надо?
Если в программе нарушено правило aliasing'а указателей, здравый смысл чем-то сможет помочь? Или все-таки придется почитать о нём?
С++ очень мощный язык, и мне нравится как сам язык, так и его возможности. Но я считаю, что в то же время этот язык сложный, и мест для прострела ноги в нем вполне хватит каждому. И здесь очень много вещей, которые надо знать, т.к. догадываться придется долго, или они будут работать на одном компиляторе, но внезапно будут вести себя по-другому на другой архитектуре\компиляторе\опциях.
Автоматическое распараллеливание не сработает? Ну не всегда это нужно, а догадаться можно.
Холодно, здравый смысл не всегда помогает с сишкой ;)
Там все гораздо веселее, вот, почитай: http://dbp-consulting.com/StrictAliasing.pdf
А если я записываю в область памяти и там переменных нет, но указатели-аласы имеют указатели разных типов и не принадлежат к спску правил, то я опять же получу UB?
Эм, а разве они по стандарту не одинакового размера, к тому же равного размеру указателя?
Боль! Вообщем они снова придумали с каждым разом все более ожидаемую хуйню.
Правильно ли я понял, пользоваться union ом для разруливания в С++ нельзя (только в си), так как он не дает гарантий по стандарту? Приходится копировать память, чтобы это разрулить туда и обратно? Хорошая оптимизация.
По стандарту С++24 компилятор крестов обязан приставлять к яйцам Colt Python .357 Magnum. Он должен делать это на растоянии не больше чем 0.11 метра до цели и не ближе 0.90, чтобы жертва не почувствовала и не пришла в смятение раньше времени. Следует также маскировать его под синие розы. При каждом использовании UB, нажимать на спусковой крючок с задержкой не более implementation defined. Если же жертва сама случайно нажмет на спусковой крючок - это UB (но мы то с вами знаем что будет).
А с memcpy компиляторы неплохо расправляются, к примеру gcc вот это: Превращает вот в это:
Это да. А компиляторы с/с++, особенно gcc, идут путем замены предсказуемости в одних ситуациях на скорость в других...
Забавно, но чем извращенней байтоебство, чем больше оно давало профита раньше, тем хуже оно работает с нынешними оптимизаторами.
a проапгрейдится до unsigned и станет например 0хFFFFFFFB и a<b вернет false
>А чему равна 1/2?
Очевидно 0.
А теперь задумайся ненадолго - это здравый смысл, или знания, полученные за годы работы с сишкой? Через сколько месяцев или лет после первого знакомства с сишкой ты узнал это правило про операции с unsigned и signed одинакового размера?
> Очевидно 0.
Ну да, этот пример действительно слишком простой. Тут после пары тестов можно догадаться что происходит.
Когда достиг почтенного возраста. Уверен, что есть ещё очень много неизведанных граний. Нужно как-то взять стандарт и прочитать его наконец. Правда жить после этого станет не интересно, так как буду знать все тайны мироздания.
Будет предупреждение и я сам приведу обе части к знаковому или беззнаковому типу так, как мне надо.
П. С. Не вижу ничего плохого в такой практике. Возможно забыли подчистить. Сам так иногда делаю.
плохо сделано, я думал намного лучше будет.
Если функцию поправят, и она внезапно перестанет вбрасывать исключение - весь код, который ее юзал получит утечку управления, что намного серьезнее описанной вами проблемы.
Решением была бы, наверное, аннотация [NoReturn] или новый флажок в бинарном формате, выставляемый как в классе, где она описана, так и в классах, которые ее используют(!). И строгая проверка всего этого: если в классе А была функция, не возвращающая значения, а класс B ее юзал, то если класс B не пересобирали, а в классе А она иногда стала возвращать управление - должна быть выдана ошибка.
И, видимо, никому не надо такой геморрой ради таких редких случаев...
так .net всё равно верифицирует код на стадии jit-компиляции. если вдруг что-то поменяется (что вряд ли вероятно, т.к. такие функции обычно private/internal) то он просто бы выдал ошибку верификации
При всем при этом компилятор не выдаст ни одного ворнинга. Поэтому, имхо, без аннотации [NoReturn] такие приемы с "гарантированным" вбросом исключения бесполезны и небезопасны. Этот код абсолютно корректен с точки зрения компилятора и верификатора, но doSomething() во втором случае получит управление, хотя писавший test() об этом не подозревал.
Ну все правильно делает. Здесь noreturn это часть контракта.
P.S. В gcc тоже есть __attribute((noreturn)), но ворнинга про "недоступный код" на этом примере почему-то не выдается.
Согласен, что это небезопасно. Но с учётом того, как много разного мусора затащено в .NET (минималистичным его не назовёшь), то появление поддержки такой фичи в рантайме не было бы уж таким "геморроем ради таких редких случаев" (повторяю, BCL кишит такими утилитными функциями).
И, алсо, аннотацию лучше назвать не [NoReturn], а [MustThrow]. Как-то не ортогонально, что все методы могут быть и MustReturn, и нет (то бишь void), но MustThrow отсутствует, хотя это такой же exit point из функции.