LINUX.ORG.RU

Порядок вычисления выражения Си

 , ,


2

3

Например, в результате вычисления операторов присваивания (см. пример) значение переменной i будет равно 9 или 11 в зависимости от того, какое подвыражение второй операции будет вычислено первым. Таким образом, с использованием разных компиляторов можно получить различные результаты.

j=3;
i=(k=j+1)+(j=5);

clang и gcc выдает i=9; у кого нибудь может быть иначе?

★★★★★

Ты только на интеле пробовал? Попробуй на армах, спарках и мипсах.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от splinter

Сначала сложатся (k=j+1), т.е. получим 4, потом (j=5) , а потом сложится 4 и 5, получим 9. Третьего не дано (если разрабы компилятора не накосячили). Так как сначала считается то, что в скобках, слева направо.

peregrine ★★★★★
()

Достойно этого треда

$ gcc main.c -O999999999999999999
$ gcc main.c -O9999999999999999999
cc1: error: argument to ‘-O’ should be a non-negative integer, ‘g’, ‘s’ or ‘fast’
d ★★★★
()
Ответ на: комментарий от peregrine

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

тут тебе не жабка

MyTrooName ★★★★★
()

У кого-нибудь может. Зачем ты пишешь код поведение которого не определено?

KblCb ★★★★★
()

а в чем ошибка-то? всё верно он посчитал

но вообще:

j=3;
(j=5)

ты по ночам делением на ноль не занимавшийся случайно?

reprimand ★★★★★
()

попробуй еще на закуску: tcc, pcc, TenDRA, icc, ack
непредвиденные результаты - такие загадочные. Эх... прям как девушки

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

На ещё поразмышляй ::)

#include <stdio.h>

int main()
{
   volatile int i,j,k=0;

   j=3;
   i=(k=j+1)+(j=5);

   printf("%d\n",i);
   printf("%d\n",k);

	return 0;
}

dron@gnu:~$ gcc -std=c99 main.c ;./a.out 
11
6
dron@gnu:~$ 

#include <stdio.h>

int main()
{
   /*volatile*/ int i,j,k=0;

   j=3;
   i=(k=j+1)+(j=5);

   printf("%d\n",i);
   printf("%d\n",k);

	return 0;
}

dron@gnu:~$ gcc -std=c99 main.c ;./a.out 
9
4
dron@gnu:~$ 

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

огог!
а я предупреждал о непредвиденных результатах! :)

reprimand ★★★★★
()

ладно, давай по другому.

#!/bin/bash

rm -f a.out 2>/dev/null
gcc -x c - <<EOF
#include <stdio.h>
int main() {
  float k;
  int i,j;
  j = 3;
  i = (k=j+1)+(j=5);
  printf("i=%i\n", i);
}
EOF
./a.out
как у тебя возник такой вопрос?

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

правильный ответ, это не правильно

anonymous
()

читай стандарт, там всё написано

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

Порядок следования операций.

Вот пойти и перечитай ещё раз про побочные эффекты и порядок вычисления.

no-such-file ★★★★★
()
Ответ на: комментарий от mix_mix

Добро пожаловать в увлекательный мир undefined, unspecified и implementation-defined behavior.

Зачет, схоронил.

Pavval ★★★★★
()

Все правильно, другого не дано.

FIL ★★★★
()

у кого нибудь может быть иначе?

msvc

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

В печку учебники. Читать и уважать надо стандарт языка.

anonymous
()

С уровнями оптимизации поиграй, авось, что и поменяется. А вообще такой код — харам.

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

Ммм, ну на сколько я помню - порядок вычисления аргументов для operator +() - unspecified. Так что так делать в любом случае не стоит, т.к. unspecified behaviour при вычислении аргументов, в купе с тем, что результат вычисления одного аргумента, зависит от другого превращается в undefined behaviour.

А вот как на эту ситуацию действуют скобки, я х3, но по логике - никак. Поправка: если вычисление выражения в скобках не является точкой следования, то в сумме это дает undefined behaviour.

batbko
()
Последнее исправление: batbko (всего исправлений: 1)
$ gcc ub.c -Wall
ub.c: In function ‘main’:
ub.c:6:22: warning: operation on ‘j’ may be undefined [-Wsequence-point]
  int i = (k=j+1) + (j=5);
                      ^
$ splint ub.c
Splint 3.1.2 --- 03 May 2009

ub.c: (in function main)
ub.c:7:20: Expression has undefined behavior (left operand uses j, modified by
             right operand): (k = j + 1) + (j = 5)
  Code has unspecified behavior. Order of evaluation of function parameters or
  subexpressions is not defined, so if a value is used and modified in
  different places not separated by a sequence point constraining evaluation
  order, then the result of the expression is unspecified. (Use -evalorder to
  inhibit warning)

Finished checking --- 1 code warning

:)

ozkriff
()

clang и gcc выдает i=9; у кого нибудь может быть иначе?

может. Рекомендую помедитировать над x-- - --x;

emulek
()
Ответ на: комментарий от peregrine

Сначала сложатся (k=j+1), т.е. получим 4, потом (j=5)

а теперь, где это в стандарте прописано?

emulek
()
Ответ на: комментарий от peregrine

ты спутал. С ассоциированием. Например X+Y+Z выполняется так: (X+Y)+Z, однако никто не гарантирует тебе, что X будет вычисляться раньше, чем Y.

Мало того, оптимизирующий компилятор считает (для встроенных типов) X+Y=Y+X, и имеет право поменять их местами. Ещё может (X+Y)+Z→X+(Y+Z), т.к. это эквивалентные выражения. Вот 3%2*7 будет всегда 1, а не 3, т.к. тут порядок имеет значение. Пример попроще: 3-4+5==-1+5==4, но не 3-(4+5)==3-9==-6.

emulek
()
Ответ на: комментарий от splinter

кстати. Попробуй с разными опциями оптимизации.

PS:

кстати вот это: j=3;i=(k=j+1)+(j=5); компилятор вычисляет ещё во время компиляции, и в самом коде просто i=9. Т.е. у тебя тут вообще вычислений нет.

emulek
()
Ответ на: комментарий от batbko

порядок вычисления аргументов для operator +() - unspecified.

лень гуглить C++ стандарт. Но ЕМНИП для const Foo &Foo::operator+(const Foo &) const порядок как раз определён. А вот для ::operator+(const Foo &, const Foo &) вроде нет.

Но я тут не уверен, и не советую на это рассчитывать.

emulek
()
Ответ на: комментарий от mix_mix

Ещё может (X+Y)+Z→X+(Y+Z), т.к. это эквивалентные выражения.

В общем случае нет.

я в курсе. Сам наступал на эти грабли. Компилятор _может_ считать их эквивалентными. При этом они _могут_ давать _разный_ результат.

И проблема не только в float/double. При почти любой оптимизации возможны проблемы.

Лично я давно взял за правило, в сомнительных случаях написать лишнююю строчку и/или добавить лишнюю переменную. Типа не t=x*y/z, а t=x*y;t/=z мне не жалко для надёжности добавить пару букв. Да и читать проще, не нужно вспоминать, чё да как. Особенно если это C++, в котором operator/() может делать что угодно. И определён хз где.

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

в любом случае надо следовать простому правилу: подумал, а оно так надо? Оно даст какой то выйгрыш? А я знаю как это работает?

Если любой из ответов на этоти вопросы «нет», то лучше сделать как то подругому :)

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