++i + ++i
Материал из Lurkmore
A long time ago, in a galaxy far, far away... События и явления, описанные в этой статье, были давно, и помнит о них разве что пара-другая олдфагов. Но Анонимус не забывает! |
ZOMG TEH DRAMA!!!11 Обсуждение этой статьи неиллюзорно доставляет не хуже самой статьи. Рекомендуем ознакомиться и причаститься, а то и поучаствовать, иначе впечатление будет неполным. |
int i = 5;
i = ++i + ++i;
Данный код — типичный майндфак для программистов.
Содержание |
[править] Строгое описание происходящего
В данном примере происходит неоднократное изменение переменной в пределах одной точки следования, такая ситуация описывается в стандартах C и С++ как UB. Иными словами, даже попытки ответить на этот вопрос иначе как «UB.» демонстрируют недостаточную квалифицированность отвечающего. Другое дело, что после «UB» можно указать некоторые подробности, и мы этим займёмся, поскольку не утоленное вовремя любопытство приводит к драмам в обсуждении.
Конкретно неопределённость в этой, как некоторым кажется, кристально ясной конструкции в данном случае заключается в том, что согласно стандартам С и С++ побочные эффекты (то есть инкремент в данном случае) могут быть применены в любой удобный для компилятора момент между двумя точками следования. Конструкцию i = ++i + ++i;
компилятор вправе понять и как
tmp=i; i = tmp+1 + tmp+1; i++; i++;
и как
tmp=i; i++; i++; i = tmp+1 + tmp+1;
и какими-нибудь другими способами. Нужна такая свобода для проведения низкоуровневых оптимизаций в обычных случаях типа a = ++b + ++c;
, дабы между делом сэкономить пару тактов на халяву.
[править] Нельзя, но если всё-таки сделаем?
Почему нельзя, мы уже видели (надеюсь, было достаточно убедительно и даже наиболее «альтернативно одаренные» не возьмутся оспаривать, что это ведет к глюку). Однако всякий пытливый ум, несомненно, изучит этот глюк в поисках того, как именно может сглючить данный код, в какую сторону стандарты языка допускают маневры компилятора, а в какую — таки нет. Варианты «правильного» ответа колеблются между 13 и 14, хотя на LISPе (пруф) и FORTRANе (пруф) анонимусы вроде как получили 12, 13 (пруф), а на Питоне вообще 10 (поскольку там вообще нет оператора инкремента ++). Результат зависит от последовательности операций. Там, где результат 13, сначала вычисляется первый инкремент, на место первого операнда идёт 6, вычисляется второй инкремент, туда идет 7, и результаты складываются: 6+7=13. 14 можно получить, вычислив оба инкремента, а потом уже сумму. Таким образом, этот трюк хорошо использовать для ошеломления быдлокодеров, уверенных в непогрешимости своего уютненького язычка.
Вообще, по определению прединкремент выполняется ДО вычисления выражения. Если считать ++i + ++i одним выражением, то мы должны выполнить два инкремента, потом вычислить выражение и получить 14. Если считать каждый ++i выражением, а сумму — выражением, принимающим результаты других выражений как аргументы, то с точки зрения логики и математики ничего таки не изменится, а вот компилятор, вычислив одно слагаемое (имеет право — самостоятельное выражение ведь!), упустит тот момент, что при вычислении второго такого же самостийного слагаемого первое тоже, сцуко, подло изменилось. Указать синтаксисом, где начинается и заканчивается выражение, невозможно — скобки задают приоритет внутри выражения. «Выражение» — другая инстанция. Например, в А=(В=С+14) есть выражение С+14, результат которого присваивается В, причем присваивание — тоже, сцуко, является выражением, и его результат присваивается А. И никакие скобки не могут побудить ЭТО стать одним выражением. Разъединить выражение, напротив, можно: (a=++i) + (b=++i), что напрямую задает вычисление a, вычисление b и только потом вычисление суммы a+b. Это, теоретически, должно детерминировать значение a+b как 13 (если компилятор настолько строго выдерживает формальные определения языка), но зато содержит UB относительно значений a и b, поскольку оптимизировать порядок вычисления a и b компилятору никто не запрещает (см. точки следования). Очевидно, что значение (a=++i) / (b=++i) по этой причине вообще не детерминировано.
[править] Оно на башорге
[править] Исходная цитата
KoloDen
Привет, я общительный пацан, люблю поболтать, особенно с классными девченками. Но, чтобы поговорить со мной, ответьте на простую задачку анти-спам бота. Вот она: |
http://bash.org.ru/quote/212679 |
[править] Последствия
11 мая 2007 года случилось страшное — была заапрувлена вышеприведенная цитата. С тех пор разнокалиберные программисты потеряли покой и сон. Дело в том, что в зависимости от используемого языка программирования данное выражение может давать и 13 и 12 и еще больше 9000 вариантов ответа.
[править] C и С++
В этих языках может получиться и 13, и 14, и вообще чёрт-те-что. Разные компиляторы С++ выдают 13 и 14. Это пример неопределённого поведения. Неопределённое поведение — самый типичный для этих языков способ выстрелить себе в ногу.
Более того, один и тот же компилятор может выдавать разные значения в зависимости от опций оптимизатора. Некоторые об этой фигне еще и предупреждают, например gcc -Wall выдает warning: operation on ‘i’ may be undefined.
Даже в одной и той же программе может получаться разный результат:
int i=5,j=5;
i=++i+ ++i;
printf("i=%i j=%i" ,i,++j+ ++j); //Вывод: i=14 j=13
Вот описанный майндфак в квадрате на плюсах
int i = 0; int z = ++i + ++i + ++i + ++i;
[править] Java
В Java получается 13 (первый пре-инкремент увеличивает i на 1 и возвращает 6, второй пре-инкремент увеличивает i на 1 и возвращает 7) и данное поведение жёстко определено, так как в спецификации языка чётко описан приоритет операторов ([1]).
[править] awk
Awk, как и Жаба, выдаёт 13:
awk 'BEGIN { i=5;j=5;i=++i+ ++i; print i, ++j+ ++j}'
13 13
[править] ActionScript 3.0
var test:int = 5;
trace('++test + ++test =', (++test + ++test));//13
test = 5;
trace('test++ + test++ =', (test++ + test++));//11
[править] Perl
Perl выдает 14:
my $i = 5;
$i = ++$i + ++$i;
print $i;
[править] PHP
PHP выдаёт 13:
$i = 5;
$i = ++$i + ++$i;
echo $i;
[править] Развитие идеи
Утверждается, что есть люди, которым приведённого выражения недостаточно для полной потери церебральной девственности. Такие люди могут захотеть отступить от классики и изучить вопрос последствий выражения
[править] Ссылки
[ + ] Капитан заявляет: «++i + ++i имеет отношение к Башоргу. Так-то!» | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
[ + ] Любой программист без словаря поймёт, что такое ++i + ++i
|
|||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|