https://habrahabr.ru/post/345690/- Программирование
- Ненормальное программирование
- Python
- Java
- C
С тех пор, как я посмотрел легендарное видео
Wat Гэри Бернхардта, меня завораживает странное поведение некоторых языков программирования. Некоторые из них таят больше сюрпризов, чем другие. Например, для Java написана
целая книга с описанием пограничных ситуаций и странной специфики. Для C++ вы просто можете почитать
сами спецификации всего за $200.
Далее поделюсь с вами моей коллекцией самых неожиданных, забавных и всё-таки валидных «заклинаний» программирования. По сути, использование этих особенностей поведения ЯП считается пагубным, поскольку ваш код никоим образом не должен быть непредсказуемым. Хорошо, что многие линтеры уже осведомлены и готовы посмеяться над вами, если попробуете какое-то из перечисленных дурачеств. Но как говорится, знание — сила, так что начнём.
Вражеское переназначение True в Python 2
Рифмуется с
true, так что вы знаете, что это
poo («какашка»).
>>> True = False
>>> True
False
К счастью, такой код выводит
SyntaxError
в версии Python 3, поскольку
True, False и None теперь стали зарезервированными словами. Такая шалость всё-таки далека от подлости в C++, когда вы вставляете
#define true false
в стандартный заголовочный файл на рабочей машине коллеги.
Призрачное взаимодействие с объектом в Java и Python
Семантика
==
часто озадачивает начинающих Java-программистов, но ещё более усложняет ситуацию непостоянство оператора даже в тривиальных ситуациях, пусть это и сделано для производительности.
Integer a = 100;
Integer b = 100;
System.out.print(a == b); // prints true
Integer c = 200;
Integer d = 200;
System.out.print(c == d); // prints false
JVM использует однотипный справочник для значений в диапазоне
[-128, 127]
. Что ещё более странно, так это соответствующее поведение Python.
>>> x = 256
>>> y = 256
>>> x is y
True
>>> x = 257
>>> y = 257
>>> x is y
False
Пока ничего
слишком удивительного.
>>> x = -5
>>> y = -5
>>> x is y
True
>>> x = -6
>>> y = -6
>>> x is y
False
Похоже, нижний предел для интерпретатора Python такой же…
-5
. Целые числа в диапазоне
[-5, 256]
получают одинаковые ID. Но всё равно это работает как-то странно.
>>> x = -10
>>> y = -10
>>> x is y
False
>>> x, y = [-10, -10]
>>> x is y
True
Видимо, применение деструктурирующего присваивания сразу меняет правила. Я не уверен, почему так происходит, и даже задал
вопрос на Stack Overflow в попытке разобраться. Может быть, повторяющиеся значения в списке указывают на тот же объект для экономии памяти.
Обратная запись с индексом в C
Обратная запись с индексом мгновенно доставляет головную боль любому разработчику.
int x[1] = { 0xdeadbeef };
printf("%x\n", 0[x]); // prints deadbeef
Причина работы такого кода в том, что
array[index]
на самом деле просто синтаксический сахар для
*(array + index)
. Благодаря коммутативному свойству сложения можно поменять их местами и получить тот же результат.
Оператор «перехода» в C
На первый взгляд оператор
-->
выглядит как синтаксическая ошибка. Но когда вы понимаете, что он нормально компилируется, то начинаете думать, что это недокументированная функция языка. К счастью, это ни то, ни другое.
for (x = 3; x --> 0;) {
printf("%d ", x); // prints 2 1 0
}
«Оператор»
-->
— это на самом деле два оператора, которые в этом контексте разбираются как
(x--) > 0
. Известно, что такая штука вызывает
немалую путаницу при использовании в продакшне — чистое зло.
Оператор sizeof
в C
Оператор
sizeof
обрабатывается в процессе компиляции, что даёт ему интересные свойства.
int x = 0;
sizeof(x += 1);
if (x == 0) {
printf("wtf?"); // this will be printed
}
Поскольку объекты оператора
sizeof
анализируются в процессе компиляции, то выражение
(x += 1)
никогда не будет запущено. Также любопытно: исследования показывают, что
printf("wtf?")
— самая популярная строчка кода, которая никогда не поступает в продакшн.
Начало индексов с единицы в Lua, Smalltalk, MATLAB и др…
На форумах
/r/programminghumor полно мемов об «
индексах,
которые начинаются с
единицы». Поразительно, но немало языков программирования в реальности используют 1-индексированные массивы. Более полный список см.
здесь.
0 соответствует true
в Ruby
…и только в Ruby. *
if 0 then print 'thanks, ruby' end # prints thanks, ruby
* правка: В обсуждении Reddit мне указали, что такое справедливо также для Lua, Lisp и Erlang.
Триграфы, диграфы и токены в C
По историческим причинам в C остались альтернативные варианты написания для нецифробуквенных символов.
Триграф |
Символ |
|
Диграф |
Символ |
|
Токен |
Символ |
---|
??= |
# |
|
<: |
[ |
|
%:%: |
## |
??/ |
\ |
|
:> |
] |
|
compl |
~ |
??' |
^ |
|
<% |
{ |
|
not |
! |
??( |
[ |
|
%> |
} |
|
bitand |
& |
??) |
] |
|
%: |
# |
|
bitor |
| |
??! |
| |
|
|
|
|
and |
&& |
??< |
{ |
|
|
|
|
or |
|| |
??> |
} |
|
|
|
|
xor |
^ |
??- |
~ |
|
|
|
|
and_eq |
&= |
|
|
|
|
|
|
or_eq |
|= |
|
|
|
|
|
|
xor_eq |
^= |
|
|
|
|
|
|
not_eq |
!= |
if (true and true) { // same as if (true && true)
printf("thanks, c");
}
Некоторое чужеродное оборудование вроде IBM 3270 не позволяло набрать некоторые часто используемые символы в C/C++, так что ввели использование диагрфы, триграфы и токены, чтобы сохранить совместимость с определёнными кодировками.
Надеюсь, статья была интересной. Можете почитать
обсуждение на Reddit.