LINUX.ORG.RU

Почему Undefined Behaviour настолько Undefined?

 ,


0

7

Недавно поразился следующему примеру:

int const F = 1000000000;

int x = 0;

for(int y = 0; y < 7; ++y)
{
    printf("x=%d, y=%d", x, y);
    x += F; // UB
}
При компиляции с -O2 на всех компиляторах, на которых пробовал, цикл становится бесконечным, вместо ожидаемых 7 шагов. Собственно, понятно, что переполнение x влечет за собой UB, но логично предположить, что неопределено будет значение этого самого x. Так почему компилятор считает возможным выкинуть проверку счетчика цикла y, который с x никак не связан?

Выкинь свои компиляторы.

> cat loop.c              
#include <stdio.h>

int main() {
    int const F = 1000000000;

    int x = 0;

    for(int y = 0; y < 7; ++y)
    {
        printf("x=%d, y=%d\n", x, y);
        x += F; // UB
    }

    return 0;
}
> clang loop.c -o loop -O2 -Wall
> ./loop                        
x=0, y=0
x=1000000000, y=1
x=2000000000, y=2
x=-1294967296, y=3
x=-294967296, y=4
x=705032704, y=5
x=1705032704, y=6
hateyoufeel ★★★★★ ()
Последнее исправление: hateyoufeel (всего исправлений: 2)

чо за хрень ты несешь?

x=0, y=0 x=1000000000, y=1 x=2000000000, y=2 x=-1294967296, y=3 x=-294967296, y=4 x=705032704, y=5 x=1705032704, y=6

$ gcc --version gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4

anonymous ()
Ответ на: комментарий от hateyoufeel

Как раз хотел сообщить, что с clang всё нормально. Однако у меня и с gcc тоже всё нормально.

sevenredlines ()
Ответ на: комментарий от CatsCantFly

Проделал на gcc 5.3.0. Всё ок. Выше пишут что gcc 4.8 тоже в порядке.

hateyoufeel ★★★★★ ()
Ответ на: комментарий от CatsCantFly

Но вообще я допускаю, что gcc мог любое говно породить. Сам сталкивался с феерическими косяками в нём. Попробуй обновить/откатить версию.

hateyoufeel ★★★★★ ()
Ответ на: комментарий от CatsCantFly

если винда не 3.11, должно отрабатывать нормально, скинь исходник говорю)

mazdai ★★ ()
Ответ на: комментарий от CatsCantFly

У меня на работе только винда, так что проверяю на ней

gcc на венде? Имхо это само по себе UB :D

ps действительно, asm выхлоп покажи, как выше предлагают.

invy ★★★★★ ()
Последнее исправление: invy (всего исправлений: 1)

на всех компиляторах, на которых пробовал

Назови их все :)

tailgunner ★★★★★ ()

Ну, к примеру, x не может переполниться, значит цикл и так выполнится не более 2 раз, значит условие можно не проверять. А вообще тут не над чем задумываться - UB на то и UB, любые твои предположения насчёт поведения априори некорректны.

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

x не может переполниться, значит цикл и так выполнится не более 2 раз

Это почему?

tailgunner ★★★★★ ()
Ответ на: комментарий от slovazap

Ну, к примеру, x не может переполниться, значит цикл и так выполнится не более 2 раз

В условии цикла x вообще не фигурирует.

kirk_johnson ★★ ()
Ответ на: комментарий от tailgunner

Потому что переполнение знакового целого - UB.

slovazap ★★★★★ ()
Ответ на: комментарий от kirk_johnson
double x = 100.0f;
for(int i = 0; i < 10; ++i) {
  x/0;
}

В условии x совершенно не фигурирует!

invy ★★★★★ ()
Ответ на: комментарий от invy

И чо? УМВР.

$ cat test.c
int main()
{
    double x = 100.0f;

    for (int i = 0; i < 10; ++i) {
        printf("i = %d\n", i);
        x = x / 0;
    }

    printf("%f\n", x);

    return 0;
}
$ gcc -std=c11 test.c
$ ./a.out
i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
inf

:)

kirk_johnson ★★ ()
Последнее исправление: kirk_johnson (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Спасибо, я знаю. У вас какие-то конкретные возражения есть, или так и будете очевидные вещи повторять?

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

slovazap ★★★★★ ()
Последнее исправление: slovazap (всего исправлений: 1)
Ответ на: комментарий от kirk_johnson

А на некоторых системах может и сегфолт произойти (и происходит). В этом и есть вся суть UB.

invy ★★★★★ ()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от invy

Нет, не может. Нет, не происходит. И нет, это не UB.

For IEEE floats, division of a finite nonzero float by 0 is well-defined and results in +infinity (if the value was >zero) or -infinity (if the value was less than zero). The result of 0/0 is NaN. If you use integers, the behaviour is undefined.

hateyoufeel ★★★★★ ()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от slovazap

У вас какие-то конкретные возражения есть

У меня есть конкретный вопрос: как ты пришел к выводу «x не может переполниться, значит цикл и так выполнится не более 2 раз»?

tailgunner ★★★★★ ()

Подтверждаю бесконечный цикл на gcc 4.8.2

$  gcc -O2 -Wall -std=c99 -o loop loop.c
loop.c: В функции «main»:
loop.c:11:5: предупреждение: iteration 2u invokes undefined behavior [-Waggressive-loop-optimizations]
   x += F; // UB
     ^
loop.c:8:2: замечание: containing loop
  for(int y = 0; y < 7; ++y)
  ^


$  ./loop
x=0, y=0
x=1000000000, y=1
x=2000000000, y=2
x=-1294967296, y=3
x=-294967296, y=4
x=705032704, y=5
x=1705032704, y=6
x=-1589934592, y=7
x=-589934592, y=8
x=410065408, y=9
x=1410065408, y=10
x=-1884901888, y=11
x=-884901888, y=12
x=115098112, y=13
x=1115098112, y=14
x=2115098112, y=15
x=-1179869184, y=16
x=-179869184, y=17
x=820130816, y=18
x=1820130816, y=19
x=-1474836480, y=20
x=-474836480, y=21
x=525163520, y=22
x=1525163520, y=23
x=-1769803776, y=24
x=-769803776, y=25
x=230196224, y=26
x=1230196224, y=27
x=-2064771072, y=28
x=-1064771072, y=29
x=-64771072, y=30
x=935228928, y=31
x=1935228928, y=32
x=-1359738368, y=33
x=-359738368, y=34
...
Может дело в x86?

ashot ★★★ ()
> cat ub.c 
#include <stdio.h>

int main()
{
    int const F = 1000000000;

    int x = 0;

    for(int y = 0; y < 7; ++y)
    {
        printf("x=%d, y=%d", x, y);
        x += F; // UB
    }

    return 0;
}
> gcc ub.c -O2 -o ub
ub.c: In function ‘main’:
ub.c:12:11: warning: iteration 2u invokes undefined behavior [-Waggressive-loop-optimizations]
         x += F; // UB
           ^
ub.c:9:5: note: containing loop
     for(int y = 0; y < 7; ++y)
     ^
> ./ub
x=0, y=0x=1000000000, y=1x=2000000000, y=2x=-1294967296, y=3x=-294967296, y=4x=705032704, y=5x=1705032704, y=6% 
> gcc --version
gcc (GCC) 5.3.0
...
matrixd ()
Ответ на: комментарий от tailgunner

У меня есть конкретный вопрос: как ты пришел к выводу «x не может переполниться, значит цикл и так выполнится не более 2 раз»?

Я описал тебе логику компилятора. x не может переполниться, потому что переполнение - это UB, а UB произойти не может. Дальше тупить будешь?

slovazap ★★★★★ ()
Ответ на: комментарий от hateyoufeel

А что в стандарте написано про операцию деления?

invy ★★★★★ ()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от slovazap

Давайте вернёмся к истокам. Вот обсуждаемый цикл:

for(int y = 0; y < 7; ++y)
Вот ваше утверждение:

цикл и так выполнится не более 2 раз, значит условие можно не проверять

Почему не более двух? Какое условие можно не проверять?

Weres ★★★ ()
Ответ на: комментарий от slovazap

У меня есть конкретный вопрос: как ты пришел к выводу «x не может переполниться, значит цикл и так выполнится не более 2 раз»?

Я описал тебе логику компилятора

Это твое понимание логики компилятора. Как показано выше, оно неправильное.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Как показано выше, оно неправильное.

И где же именно? Как показано выше, именно что я сказал и происходит, условие не проверяется.

slovazap ★★★★★ ()
Последнее исправление: slovazap (всего исправлений: 1)
Ответ на: комментарий от slovazap

У меня есть конкретный вопрос: как ты пришел к выводу «x не может переполниться, значит цикл и так выполнится не более 2 раз»?

Я описал тебе логику компилятора

Как показано выше, оно неправильное.

И где же именно?

Почему Undefined Behaviour настолько Undefined? (комментарий)

Почему Undefined Behaviour настолько Undefined? (комментарий)

Цикл выполняется более 2 раз.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Спасибо, я знаю. Именно потому что условие выкинуто согласно той логике что я описал.

slovazap ★★★★★ ()
Ответ на: комментарий от slovazap

Спасибо, я знаю. Именно потому что условие выкинуто согласно той логике что я описал.

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

kirk_johnson ★★ ()
Ответ на: комментарий от kirk_johnson

Я не понимаю что ты несёшь. Скоро дойдём до того что и UB никакого нету.

slovazap ★★★★★ ()
Ответ на: комментарий от ashot

Может дело в x86?

Хотя со шлангом 7 проходов цикла.

ashot ★★★ ()
Ответ на: комментарий от slovazap

а если написать так:

for(int y = 0; y < 2; ++y)

то никакого UB нет но условие по твоей логике тоже проверять не нужно? но ведь именно отсутствие проверки и вызывает ub.

anonymous ()
Ответ на: комментарий от invy

А что в стандарте написано про операцию деления?

Что при делении double или float на 0 ты получишь +-Inf либо NaN.

hateyoufeel ★★★★★ ()

Тем, у кого все работает

Таки да, путем экспериментов - на x86_64 не воспроизводится, только на x86

CatsCantFly ()
Ответ на: комментарий от kirk_johnson

Уже неплохо. Тогда может цикл всегда выполняется 7 раз и логи выше и весь топик мне мерещатся?

slovazap ★★★★★ ()
Последнее исправление: slovazap (всего исправлений: 1)
Ответ на: комментарий от slovazap

Он выполняется 7+ раз. Ты про какие-то два раза начал тут пороть. Ты либо объясняй откуда ты сделал такие выводы, либо делись той дурью, которой упарываешься.

kirk_johnson ★★ ()
Ответ на: Тем, у кого все работает от CatsCantFly

на x86_64 не воспроизводится, только на x86

...но не со всеми компиляторами, которые я пробовал :)

GCC 4.7 - бесконечный цикл, GCC 4.3 - 7 раз.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: Тем, у кого все работает от CatsCantFly

pizдёж

user@pc:~/build/C$ gcc -std=c99 app.c 
user@pc:~/build/C$ ./a.out
x=0, y=0
x=1000000000, y=1
x=2000000000, y=2
x=-1294967296, y=3
x=-294967296, y=4
x=705032704, y=5
x=1705032704, y=6
user@pc:~/build/C$ uname -a
Linux pc 4.3.3-pc #1 SMP Tue Dec 15 19:22:18 MSK 2015 i686 athlon i686 GNU/Linux
user@pc:~/build/C$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

superuser ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.