1

Сегодня мы узнаем, как НЕ надо писать программы. И - почему.

Любой начинающий (быдло)кодер знает, что писать

x-- - --x;

нельзя. Но мало кто знает, ПОЧЕМУ, и что в действительности происходит. А знать это необходимо, потому как IRL всё не так просто, как в этом, далеко не тривиальном примере с тройным дном.

Итак преступим:

Как всем хорошо известно, компилятор строит для любого выражение дерево, узлами которого служат арифметические (и проч.) операции, а листьями - переменные и числа. Затем (на этапе исполнения), компилятор обходит дерево в строго определённо порядке, и выдаёт вполне детерминированный результат. При этом, следует учитывать, что константные выражения компилятор вычисляет ещё на этапе компиляции, и в программу выражения типа 2 * 2 попросту не попадают, в лучшем случае там будет 4, а скорее всего, компилятор и четвёрку постарается как-нить убрать.

Построение дерева для выражения x-- - --x;

В этом выражении присутствует 3 операции, причём две из них(--) имеют более высокий приоритет, чем третья (-). Дерево такое:

   -                                                                                                                          
  / \
x--  --x

Очевидно, что в данном случае должны сначала выполнится декременты, а лишь потом - вычитание. Но стандарт особо описывает данную ситуацию, утверждая, что порядок выполнения самих декриментов НЕ определён. А раз так, то возможно всего два варианта(в начале x равен 10):

1) сначала выполняется x-- (10->9)
затем второй --x (9->8)
т.к. результатом x-- является x, то получается 10-8 == 2

2) второй случай --x (10->9)
x-- (9->8 результат 9)
9-9 == 0.

Теория - прекрасная штука. Проверяем

 Консоль:
ksu@ksu:~/test$ cat m.c
#include <stdio.h>

int main()
{
    int x;
    printf("Введите число: ");
    scanf("%d", &x);
    printf("\nx-- - --x = %d\n", x-- - --x);
    return 0;
}
ksu@ksu:~/test$ gcc -Wall -o m m.c
m.c: В функции ‘main’:
m.c:8:32: предупреждение: операция над ‘x’ может дать неопределенный результат
ksu@ksu:~/test$ ./m
Введите число: 10

x-- - --x = 1

facepalm  :O

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

2

вариант "создатели gcc == быдлокодеры" отметаем сразу. Быдлокодеры не они... Но почему?  :o

Задачка с тройным дном: не всё так просто - весь фокус в том, что если открыть стандарт, то можно узнать, что результат x-- тождественно равен x, т.е. наше дерево ни к чёрту не годится - на самом деле, то, что приоритет x-- выше, ничего не означает. Точнее, это вовсе не говорит о том, что компилятор обязан выполнить x-- до (-). Может и после. Это приводит нас к третьему варианту, который мы проморгали. А именно:
3)
--x (10->9)
- (10 - 9 == 1)
ну и наконец выполняется всё остальное...

Конечно непонятно, по каким причинам, наш компилятор выбрал самый нелогичный вариант, но он имеет на это полное право! Ибо стандарт разрешает.

Ребята, поля в этой книге слишком узки, и времени у меня уже нет. Потому дальнейший анализ продолжим завтра. Я ещё забыл рассмотреть, что произойдёт с самой x. По законам алгебры, если вычесть из 10 1 дважды, получим ессно 8. И тем не менее, мой компилятор получает таки 9  oO

Эта загадка как раз и является ключом к третьему дну задачи, и кроме того, объясняет, почему компилятор выбирает такой нелогичный на первый (и на второй тоже) путь.

Желаю удачи, и надеюсь, мне не придётся объяснять, в чём тут дело bx

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

3

Вообще-то все логично. Знаете чем отличаются записи --х и х-- ? Нет? Почитайте.

4

shumomer, дядя, где вы были 15 лет назад?!

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

5 (12.04.2011 22:22:32 отредактировано mycetophylax)

shumomer пишет:

Вообще-то все логично.

раз логично, то объясните. Не можете?  Я об этом узнал (о том, КАК это происходит) лет 10 назад. Любопытно, что про сами префиксные/постфиксные операции я узнал лет на 5 раньше. Мне понадобилось читать мануалы примерно 5 лет... Такие дела...

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

6

похоже никто не знает... ладно, подсказка номер 9001

|0000040b 8b45f4                         mov         eax, [ebp-0000000c]                                                      
│0000040e 8b55f4                         mov         edx, [ebp-0000000c]
│00000411 4a                             dec         edx
│00000412 8955f4                         mov         [ebp-0000000c], edx
│00000415 8b55f4                         mov         edx, [ebp-0000000c]
│00000418 89c1                           mov         ecx, eax
│0000041a 29d1                           sub         ecx, edx
│0000041c 89ca                           mov         edx, ecx
│0000041e 48                             dec         eax                                                                      
│0000041f 8945f4                         mov         [ebp-0000000c], eax

результат в edx равен (очевидно) 1.
а x будет очевидно x - 1.

может мне хоть кто-то объяснит - почему?

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

7

drBatty пишет:

Конечно непонятно, по каким причинам, наш компилятор выбрал самый нелогичный вариант, но он имеет на это полное право! Ибо стандарт разрешает.

А почему не логичный?
Мне кажется все достаточно логично и "человечно". У нас есть выражение разница двух чисел - не более. Но в этом же выражении (кроме получения нужного значения разницы) мы (точнее программер сунувший это в свой код) еще хотим сделать дополнительные действия с переменной x.

Т.е. это выражение означает, что мы хотим получить разницу между Х и (X-1) и логично получить 1.
Только X-- мы говорим, что хотим уменьшить х после того, как посчитаем это выражение.
Т.е. по человечески читаем с лева на право: 10 (потом надо будет уменьшить)  -  (10-1) = 1


Но вот где такое выражение могло бы быть применено я не представляю.

То что не убивает нас, делает нас сильнее! © Ницше.
When life puts you in tough situations, don’t say "why me". Just say "try me".
GNU/Linux Debian

8

Я тоже не понимаю почему не логичный. Если почитать про постфиксные и префиксные операции, то вроде бы всё понятно. В чём тут подвох?

9

voral пишет:

А почему не логичный?

потому что многие считают, что получится 0 или 2. Т.е.
x-- == 10
--x == 9-1 == 8
- == 10-8 == 2

или
--x == 9
x-- == 9
- == 0

Здесь складывается впечатление, что компилятор зачем-то засунул первое x-- в самый конец выражения. (только так можно получить 1). Но это НЕ ТАК. На самом деле, при x--, переменная x расщепляется на две:
Первая часть уменьшается на 1, и соответствует "истинному x",
Вторая часть НЕ уменьшается, причём именно она и участвует в вычислениях.

Любопытно так-же и то, что операция --x на самом деле равносильна x = (x-1). Вот только одно но: в си нет конструкции языка "равно", за то есть арифметическая операция X=Y. Как у любой бинарной операции, у "=" есть:
1) операнды X, Y
2) результат, который равен Y
Но кроме этого, у равно есть и
3) побочный эффект - X становится равным Y.
В нашей ситуации, выражение на самом деле вычисляется так:
(x2 <= x, x1 <= x-1) // это операция х--
(x3 <= x2 - 1, x1 <= x2 - 1) // это операция --x, которую компилятор был вынужден поместить перед минусом, ибо её результат является аргументом вычитания, а старшинство -- выше. Потому компилятор поменял - и -- местами
Обратите внимание, что аргумент этой операции - "временная" величина, а результат (побочный) пишется естественно в "постоянную" x1. Это правило.
(x4 = x2 - x3) // вот тут и происходит собственно вычитание. Вычитается уменьшенное на 1 значение началной переменой, из самой переменной. Потому результат тождественно равен константе 1.

Само x получает сначала значение x-1, а потом, при неявной операции присваивания внутри --x, оно вторично получает значение x-1. Т.е., как и записано, происходит двойной инкремент, но он происходит дважды над одним и тем-же значением x.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

10

а вот теперь посмотрите, что сделал оптимизирующий компилятор:

│000003e0 8d4c2404                       lea         ecx, [esp+0x4]
│000003e4 83e4f0                         and         esp, fffffff0
│000003e7 ff71fc                         push        dword ptr [ecx-4]
│000003ea 55                             push        ebp
│000003eb 89e5                           mov         ebp, esp                                                                ▒
│000003ed 51                             push        ecx                                                                     ▒
│000003ee 83ec20                         sub         esp, 0x20                                                               ▒
│000003f1 6800850408                     push        08048500                                                                ▒
│000003f6 e805ffffff                     call        0x300                                                                    
│000003fb 59                             pop         ecx                                                                      
│000003fc 58                             pop         eax                                                                      
│000003fd 8d45f4                         lea         eax, [ebp-0000000c]                                                      
│00000400 50                             push        eax                                                                      
│00000401 681c850408                     push        0804851c                                                                 
│00000406 e805ffffff                     call        0x310                                                                    
│0000040b 836df402                       sub         dword ptr [ebp-0000000c], 0x2                                            
│0000040f 58                             pop         eax                                                                      
│00000410 5a                             pop         edx                                                                      
│00000411 6a00                           push        0x0                                                                      
│00000413 681f850408                     push        0804851f                                                                 
│00000418 e8e3feffff                     call        0x300               
│0000041d 5a                             pop         edx               
│0000041e 59                             pop         ecx               
│0000041f 8b45f4                         mov         eax, [ebp-0000000c]
│00000422 50                             push        eax      
│00000423 6828850408                     push        08048528 
│00000428 e8d3feffff                     call        0x300
│0000042d 31c0                           xor         eax, eax
│0000042f 8b4dfc                         mov         ecx, [ebp-4]  
│00000432 c9                             leave                
│00000433 8d61fc                         lea         esp, [ecx-4]  
│00000436 c3                             ret                  

он тупо написал
x-- - --x => 0
x => 2

:lol:

ибо он проверил все допустимые варианты, и выбрал тот, при котором выражение тождественно даёт 0.

Ибо, все операции умножения/сложения/деления/итд на константу 0 - тривиальны. (при делении на ноль вообще можно весь код в помойку выкинуть, странно, что компилятор так не делает).

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

11

Тут не один, а два "подвоха")

  Для начала, порядок операций в Си точно определен; для данного случая:

1. Префиксный декремент.
2. Вычисление выражения и возврат значения.
3. Постфиксный декремент.

  Т.е., например такой код:

#include <stdio.h>

int main()
{
    int x = 35;
    int y = 35;

    printf("\ny-- - --x = %d\n", y-- - --x);
    return 0;
}

  Выведет "1" на любом сишном компиляторе. Но, это для случая с двумя разными переменными.
  Неопределенность возникает из-за того, что переменная "i-- - --i" - одна.

  Для понимания ситуации, вот парочка примеров:

#include <stdio.h>

int main()
{
    int i;
    printf("Введите число: ");
    scanf("%d", &i);

    int x = i;
    printf("\nx-- - --x = %d\n", x-- - --x);
    return 0;
}


#include <stdio.h>
int main()
{
    int x = 133;
    printf("\nx-- - --x = %d\n", x-- - --x);
    return 0;
}

  Хоть компилер и пишет, что ситауция неопределенна, возвращает 0. Здравый смысл подсказывает, что x - одна переменная, и вычитается она сама из себя, и неважно, какие там декременты стоят.

  В случае же с кодом drBatty - это отдельная песня. Для начала, небольшой код:

#include <stdio.h>
int main()
{
    int x = 133;
    int* px = &x;
    printf("\nx-- - --x = %d\n", x-- - --x);
    return 0;

    int y = 133;
    printf("\ny-- - -y = %d\n", y-- - --y);
    return 0;
}

  Это конечно мои догадки, но думаю, из-за ссылки на переменную, компилятор начинает её обрабатывать как будто этих переменных две. Т.е. в соответствии с порядком операций.

12

voral пишет:

Но вот где такое выражение могло бы быть применено я не представляю.

за то я слишком хорошо представляю  ah
дело в том, что IRL такого конечно не бывает, в RL всё сложнее и запутаннее, и потому, очень сложно найти эти зависимости. Даже, если о них знаешь. Лучше так не писать. Но ведь я как раз и хотел показать, КАК НЕ НАДО ДЕЛАТЬ.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

13

Newton пишет:

Для начала, порядок операций в Си точно определен

нет. в нём ЯВНО записано, что порядок НЕ ОПРЕДЕЛЁН.

Выведет "1" на любом сишном компиляторе.

ну а откуда тут неоднозначность-то? в каком порядке не считай, получится 1

│000003b3 ff4df0                         dec         dword ptr [ebp-00000010]
│000003b6 8b45f0                         mov         eax, [ebp-00000010]
│000003b9 8b55f4                         mov         edx, [ebp-0000000c]
│000003bc 29c2                           sub         edx, eax
│000003be ff4df4                         dec         dword ptr [ebp-0000000c]

первая команда может когда угодно делать y--, ибо её результат никого не волнует. Мало того, она его может и НЕ ДЕЛАТЬ, ибо всё равно вы y не используете.
А то, что вы юзаете константы - вообще...

0000040a 83e822                         sub         eax, 0x22

у вас просто не будет никаких вычислений!

Newton пишет:

Хоть компилер и пишет, что ситауция неопределенна, возвращает 0. Здравый смысл подсказывает, что x - одна переменная

мне всё равно, что подсказывает ваш здравый смысл. Стандарт намекает, что тут у вас константы, и программа не обязана их считать. и не считает, ЧСХ.

Newton пишет:

Это конечно мои догадки, но думаю, из-за ссылки на переменную, компилятор начинает её обрабатывать как будто этих переменных две. Т.е. в соответствии с порядком операций.

ссылка тут не причём. Можете задать x любым нетривиальным методом, например прочитав его с диска. А переменных конечно две - как может быть одна, если x-- имеет ДВА РЕЗУЛЬТАТА.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

14 (11.04.2011 20:29:22 отредактировано mycetophylax)

Ну, братва, вы даете! Хотите маслица подолью? -- Извольте!

одно и то же выражение в разных языках C/C++ и C# дает разный результат:

int x = 3;
res = x++ + ++x;

Я эту тему поднимал лет пять назад не то на rsdn.ru . Кому интересно, думаю нагуглите сами. Тоже долго обсасывали. Сопли жевали, жевали сошлись на том, что в описании языка ведь не даром говориться о том, чтобы не использовать такие приемы. Стандарт языка не гарантирует их одинакового поведения на разных платформах/компиляторах. Какой смысл изучать то, что языком не регламентируется? Выйдет новая рерсия компилятора и весь код пойдет лесом.

Live Free or Die

15

zhevak, да что вы говорите?!

 Консоль:
ksu@ksu:~/test$ cat zh.c
#include <stdio.h>

int main()
{
    int x = 3;
    int res = x++ + ++x;
    printf("x++ + ++x =%d\n", res);
    return 0;
}
ksu@ksu:~/test$ gcc -Wall -o zh zh.c
zh.c: В функции ‘main’:
zh.c:6:18: предупреждение: операция над ‘x’ может дать неопределенный результат
ksu@ksu:~/test$ ./zh
x++ + ++x =8
ksu@ksu:~/test$ cat zh.cpp
#include <stdio.h>

int main()
{
    int x = 3;
    int res = x++ + ++x;
    printf("x++ + ++x =%d\n", res);
    return 0;
}
ksu@ksu:~/test$ g++ -Wall -o zh zh.cpp
zh.cpp: В функции ‘int main()’:
zh.cpp:6:20: предупреждение: операция над ‘x’ может дать неопределенный результат
ksu@ksu:~/test$ ./zh
x++ + ++x =8

всё одно и то-же у меня получается. Да и ваша задачка куда как проще моей - в ней результат не зависит от порядка слагаемых, а значит тонкие моменты ускользают. (например то, когда и как выполняются инкременты)

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

16 (11.04.2011 20:21:02 отредактировано mycetophylax)

zhevak пишет:

Я эту тему поднимал лет пять назад не то на rsdn.ru

эта тема неоднократно поднималась в FIDO задолго до этого  ag

zhevak пишет:

сошлись на том, что в описании языка ведь не даром говориться о том, чтобы не использовать такие приемы. Стандарт языка не гарантирует их одинакового поведения на разных платформах/компиляторах. Какой смысл изучать то, что языком не регламентируется?

неверный вывод: я такое постоянно в чужом коде вижу (честно говоря, иногда и в своём), просто это более тщательно замаскированно...

zhevak пишет:

одно и то же выражение в разных языках C/C++ и C# дает разный результат:

да. результат отличается, но анализ кода говорит, что разница не принципиальная - просто компилятор имеет право, и оптимизирует тривиальные операции когда аргументы целые, но не в состоянии оптимизировать, если аргументы не являются тривиальным типом (я проверил - даже если это inline подстановка даже тех же целых, этой оптимизации нет)

ЗЫЖ повторяю - константы не вычисляются в runtime!

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

17 (11.04.2011 15:25:27 отредактировано zhevak)

drBatty пишет:

zhevak, да что вы говорите?!

 Консоль:
ksu@ksu:~/test$ cat zh.c
...
x++ + ++x =8

всё одно и то-же у меня получается. Да и ваша задачка куда как проще моей - в ней результат не зависит от порядка слагаемых, а значит тонкие моменты ускользают. (например то, когда и как выполняются инкременты)

Дмитрий, обратите внимание, я сказал -- в C/C++ (1) и С# (2)

А остальное, извините, комментировать не буду. (Помолчу-ка я лучше в тряпочку какое-то время ...)

Live Free or Die

18

Это переменные, весь вопрос в инициализации. Ну заменим на:

  int x, y;

  printf("give me a number x: ");
  scanf("%d", &x);

  printf("give me a number y: ");
  scanf("%d", &y);

  printf("x-- - --y = %d\n", x-- - --y);

Что изменится? Результат все равно один.
 

ну а откуда тут неоднозначность-то?

  Когда переменных две, нет никакой неоднозначности.

константы не вычисляются в runtime!

  В данном случае, не важно когда они вычисляются.

19

zhevak пишет:

обратите внимание, я сказал -- в C/C++ (1) и С# (2)

а... извините. что такое C# - без понятия.

Newton пишет:

Что изменится? Результат все равно один.

дык ессно. откуда там неоднозначность? первый декримент вообще не имеет отношения к вычитанию, и его можно вычислять когда угодно, в т.ч. и не вычислять вовсе (в вашем примере).

Newton пишет:

Когда переменных две, нет никакой неоднозначности.

нет и раздвоения - ибо в вашем выражении участвет только одно Y.

Мой пример как раз и ценен тем, что у меня x раздваивается, причём операция --x читает одну переменную х1, а пишет вторую - х2. Потому и получается столь неожиданный результат. ибо x--, --x вычтет из x двойку, а не единицу. А x-- - --x вычитает таки единицу.

Newton пишет:

В данном случае, не важно когда они вычисляются.

угу. 2*2 всегда равно 4. Ну и что тут интересного и полезного?

В моём случае - важно. Ибо, если у вас есть функция, которая работает 1 час, то её выпадение вы заметите (программа будет работать на 1 час быстрее), и выпадение будет, когда оптимизирующий компилятор умножит эту функцию на x-- - --x, или любое другое выражение, которое тождественно равно нулю (если конечно у компилятора хватит ума распарсить, что x-- - --x может быть равно нулю). Заметьте, тут нет условий, просто умножение

x = function() * y;

компилируется в

x = 0;

если на этапе компиляции компилятор выяснил, что y может быть константой равной нулю.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

20

Мой пример как раз и ценен тем, что у меня x раздваивается

  Это то, что я и пытался сказать, только другими словами. Когда я говорил про одну переменную, это означало - одну, явно объявленную программистом переменную.
 
  Тут и начинается неоднозначность - компилер пытается разделить её на две, при этом не забывая, что переменная все-таки одна.
 
  Если брать реальный компилятор (а не то что написано в стандарте), то его поведение вполне понятно - он вычитает переменную саму из себя.
( Если я неправ - доказательства в студию!)
 
  int x = 3;
  int y = x-- - --x;
 
  y = 0 = 2-- - 2;
  x = 1;
 
  Т.е., префикс-декремент , вычитание и возврат результата (0), пост-декремент. Тоже, само с вариантом:
 
  int x = 3;
  int y = x++ + ++x;
 
  Порядок такой: преф-инкремент, сложение и возврат результата (8), пост-инкремент.
 
  y = 8 = 4++ + 4;
  x = 5;

------------------------------------------------------------------------------
  Что же касается использования в рабочем коде.. какой же ССЗБ будет такое использовать? А если уж так хочется, то можно сделать что-нибудь вроде:
x = function() * (y+1);

21

Newton пишет:

( Если я неправ - доказательства в студию!)

|0000040b 8b45f4                         mov         eax, [ebp-0000000c]   // X1 <= x  | 10                                         
│0000040e 8b55f4                         mov         edx, [ebp-0000000c]  //  X2 <= x | 10
│00000411 4a                             dec         edx  // X2 = X2 - 1 | 9
│00000412 8955f4                         mov         [ebp-0000000c], edx  //  x <= X2 | 9
│00000415 8b55f4                         mov         edx, [ebp-0000000c]  //  X2 <= x (sic!)
│00000418 89c1                           mov         ecx, eax  // X3 <= X1 | 10
│0000041a 29d1                           sub         ecx, edx  //  X3 <= X3 - X2 | 10-9==1
│0000041c 89ca                           mov         edx, ecx  //  X2 <= X3 | 1
│0000041e 48                             dec         eax // X1 <= X1 - 1 | 9                                                                    
│0000041f 8945f4                         mov         [ebp-0000000c], eax  // x <= X1 | 9

кстати, если я не ошибаюсь, MSVC делал также.

Newton пишет:

Что же касается использования в рабочем коде.. какой же ССЗБ будет такое использовать?

такое не будет. но IRL всё намного запутаннее.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

22 (11.04.2011 19:24:37 отредактировано drBatty)

Newton пишет:

префикс-декремент , вычитание и возврат результата (0), пост-декремент.

ну выходит так ab

префикс-декремент, минус, возврат...
вот только постфикс-декремент выполняется от того, что было в x перед префикс-декрементом, и пишется в эту самую x, куда только что записался результат префиксного декремента.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

23

Newton пишет:

Тут и начинается неоднозначность - компилер пытается разделить её на две, при этом не забывая, что переменная все-таки одна.

он не только пытается, но и так делает. походу дела синхронизируя свои временные копии. в данном случае компилятор дважды выполняет декремент от 10, и дважды запоминает эту девятку...

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

24

Т.е., ведет себя так, будто х - это две разных переменных.
Так выходит для следующего кода:

    int x;
    printf("Введите число: ");
    scanf("%d", &x);

    printf("\nx-- - --x = %d\n", x-- - --x);
    return 0;

  Поведение меняется из-за появления ссылки на х. (Других причин не вижу)

  Такой же коде:

    int i;
    printf("Введите число: ");
    scanf("%d", &i);

    int x = i;
    printf("\nx-- - --x = %d\n", x-- - --x);
    return 0;

  Ведет себя так, как я показал выше. И это справедливо только для gcc, MSVC как раз возвращает ноль при любом варианте.

25

Newton, а какое там поведение? пробовать лень ab

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

26

Newton пишет:

Т.е., ведет себя так, будто х - это две разных переменных.

угу. на самом деле, такое поведение давно, со времён iP1 - когда в нём две трубы появилось - вот компилятор и старается раздвоить поток. Конечно он не рассчитан на мой быдлокод, но в других случаях команды спариваются, и выполняются одновременно.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

27

вот холивар на пустом месте развели.... си всегда славился множеством UB и спорить по ним бесполезно... всегда можно создать компилятор, который будет возвращать 192 в данном случае. и это будет соответствовать стандарту. если нечем заняться, то давайте ещё будем думать, чему равно x=333; (x++ - --x) * ((&((char) x))[1]) << (x-=7);

95% процентов проблем находятся между клавиатурой и стулом.

28 (12.04.2011 22:24:27 отредактировано mycetophylax)

Fat-Zer пишет:

вот холивар на пустом месте развели.... си всегда славился множеством UB и спорить по ним бесполезно... всегда можно создать компилятор, который будет возвращать 192 в данном случае.

не. в данном случае стандарт хоть и не определяет точно решение, но какие-то рамки есть.
1) тут три операции, потому мы имеем не более 6и перестановок.
2) 3 из 6 перестановок нам не подходят, ибо по стандарту -- старше - и должно вычисляться раньше.
ну и получаем мы всего 3 ответа 0,1, и 3.

Fat-Zer пишет:

если нечем заняться, то давайте ещё будем думать, чему равно x=333; (x++ - --x) * ((&((char) x))[1]) << (x-=7);

уволить быдлокодера и переписать строку по человечески? не?
просто вы видите, что это быдлокод, и я вижу. А сам быдлокодер - не видит. Вот для него эта тема и создавалась - что-бы видел, какая НЁХ получается.

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)

29

drBatty пишет:

0,1, и 3.

s/0,1, и 3/0,1, и 2/
fixed

Карусель разнесло по цепочке за час
Всех известий — конец
Да, весна началась!
(всё к лицу подлецу, как родному отцу, не рассказывай, батя, и так всё пройдёт)