|
фича компиляторв, инкремент переменной |
|
|
|
May 11 2007, 11:25
|

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
На форуме программистов (здесь http://www.rsdn.ru/Forum/?mid=2481623) с пацанами сейчас подняли интересную тему. Думаю, что си-шникам тоже следует остерегаться подобного кода. В двух словах проблема состоит в том, как компилятор должен понимать оператор инкремента.
int i; int x;
i = 5; x = ++i + ++i;
Чему в результате будет равно х ? Прикол в том, что MS VC++ 6.0 и VC++.NET дают результат, равный 14. Майкрософтовский C# и IAR говорят, что х = 13. А более всего удивил Watcom -- у него результат зависит от того, включена-ли оптимизация или нет... Я, конечно, понимаю, что код, который обсуждается, несколько далек от жизни, но все же, знать о таких прибабахах -- надо. Вечером дома, попробую еще в CodeVision эту фичу покрутить.
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
 |
Ответов
(30 - 44)
|
May 12 2007, 09:56
|

Гуру
     
Группа: Свой
Сообщений: 3 041
Регистрация: 10-01-05
Из: Москва
Пользователь №: 1 874

|
Цитата(zltigo @ May 12 2007, 13:36)  Жаль. То есть ссылку на свой пост, про который Вы утверждаете, что там описали формальный разбор выражения, Вы привести не можете? Цитата(zltigo @ May 12 2007, 13:26)  Приводили, но не с точки зрения базовых правил а с точки зрения как может быть построена разборка выражения в потрохах компилятора. Что позволяет объяснить, почему появляется неправильный результат и почему стандарт не требует разбирать такую ерунду. Но все это не имеет отношения к пользователю компилятора написавшего дурацкое, сложно разбираемое компилятором (если он за это возьмется!), хреново оптимизируемое и трудно понимаемое человеком, но вполне однозначо трактуемое выражение - "берем одну едиственную переменную, инкрементируем один раз, инкрементируем второй раз, прибавляем к ней ее-же значение. Нет, грамматические и семантические правила - это часть стандарта языка, а не реализации конкретного компилятора. Я ссылался на грамматические правила и на семантику операций. Выражение x = ++i + ++i; должно быть грамматически разобрано следующим образом (запись дерева - стандартная префиксная) Код assign( addr( 'x' ), sum( assign( addr( 'i' ), add( value( addr( 'i' ) ), 1 ) ), assign( addr( 'i' ), add( value( addr( 'i' ) ), 1 ) ) ) ) Как при аккуратном исполнении этого дерева разбора в соответствии с семантическими правилами языка можно получить 14 - не понимаю. P.S. Если придираться - в этом дереве уже расрыта операция ++i как эквивалентная (i+=1).
--------------------
Пишите в личку.
|
|
|
|
|
May 12 2007, 10:16
|

Гуру
     
Группа: Модераторы
Сообщений: 2 095
Регистрация: 27-08-04
Из: Россия, СПб
Пользователь №: 553

|
По моему все очень просто, т.к. код архитектурно зависимый. Даже считая по дереву: Код 1. *i=*i+1;[6] 2. *i=*i+1;[7] 3. x=i+i;[14] С другой стороны, результат в java(как в стековой архитектуре). Код 1. i=i+1[6] 2. push 3. i=i+1[7] 4. push 5. add[13] Все прозрачно)))
|
|
|
|
|
May 12 2007, 10:21
|

Гуру
     
Группа: Свой
Сообщений: 3 041
Регистрация: 10-01-05
Из: Москва
Пользователь №: 1 874

|
Цитата(vetal @ May 12 2007, 14:16)  3. x=i+i;[14][/code] Все прозрачно))) Так вот откуда взялась эта третья операция - x = i + i? Как она получена из дерева разбора выражения? P.S. ++i эквивалентно i += 1. Цитата An assignment expression has the value of the left operand after the assignment, but is not an lvalue.
--------------------
Пишите в личку.
|
|
|
|
|
May 12 2007, 10:42
|

Гуру
     
Группа: Модераторы
Сообщений: 2 095
Регистрация: 27-08-04
Из: Россия, СПб
Пользователь №: 553

|
Цитата Так вот откуда взялась эта третья операция - x = i + i? Как она получена из дерева разбора выражения? В дереве ясно написано x=add {++i,++i}, т.е. сначала выполняются все преинкрементом, а затем выполняется операция сложения. Т.к. операции преинкремента выполняются с одной ячейкой памяти - происходит увеличение ячейки на 2. Операция сложения складывает 2 ячейки памяти с адресом &i, т.е. как и написано в коде. Это вполне справедливо при рассмотрении x86 как машины с памятью, которой она и является))) Я не могу спрогнозировать результат только на регистровой машине(классической или как при представлении x86 в регистровом эквиваленте), т.к. неопределенность будет зависеть от результата оптимизации. На регистровой машине будут справедливы 2 подхода : -неоптимизированный (по первичному графу) получится как в x86, т.к. сначала выполнятся все "пре" операции и получится результат как в машине с памятью; -оптимизированный, когда все будет делаться на лету и максимальным использованием регистров код может выглядеть как Код ldw r1, i add r1,r1,1 stw i, r1; можно исключить mov r2,r1 add r2,r2,1 stw i,r2 add r3,r1,r2
|
|
|
|
|
May 12 2007, 10:52
|

Гуру
     
Группа: Свой
Сообщений: 3 041
Регистрация: 10-01-05
Из: Москва
Пользователь №: 1 874

|
Цитата(vetal @ May 12 2007, 14:42)  В дереве ясно написано x=add {++i,++i}, т.е. сначала выполняются все преинкрементом, а затем выполняется операция сложения. Т.к. операции преинкремента выполняются с одной ячейкой памяти - происходит увеличение ячейки на 2. Операция сложения складывает 2 ячейки памяти с адресом &i, т.е. как и написано в коде. Это вполне справедливо при рассмотрении x86 как машины с памятью, которой она и является))) Не совсем - в дереве ясно написано, что нужно сложить результат первого подвыражения и результат второго подвыражения. Если бы результат подвыражений был lvalue - тогда при вычислении сложения нужно было бы взять у lvalue (неформально - ссылки на объект) значение объекта, и тогда можно было бы прочитать из переменной i результат, отличный от приписанного в i в подвыражении, если бы преобразование lvalue в значение выполнялось после вычисления обоих подвыражений как преобразование типа для операции '+'. Но так как результат вычисления подвыражения lvalue не является - мы не имеем права считывать его обратно из i и обязаны использовать именно то значение, которое было вычислено соответсвующим подвыражением. Одно из подвыражений всегда даст 6, в каком бы порядке ни выполнять операции. Что происходит в реальных компиляторах - вполне понятно, это не вопрос. Речь шла про "правильность", причем, после закрывания глаз на то, что такое выражение в целом имеет undefined bihavior.
--------------------
Пишите в личку.
|
|
|
|
|
May 12 2007, 12:23
|

Гуру
     
Группа: Свой
Сообщений: 3 041
Регистрация: 10-01-05
Из: Москва
Пользователь №: 1 874

|
Цитата(vetal @ May 12 2007, 15:11)  Потому и "undefined bihavior", что по моему мнению оператор + складывает 2 ячейки памяти с адресом I, а по вашему он заводит дополнительную переменную. Тут всё и кроется)) undefined bihavior - потому что часто зависит, например, от порядка вычисления подвыражений, да и для свободы оптимизатора полезно, когда побочные эффекты можно вычислять в произвольном порядке. Ваше мнение о том, "что оператор + складывает 2 ячейки памяти с адресом i" противоречит утверждению из стандарта, что результат вычисления ++i не есть lvalue. Так что такую интерпретацию принять не могу как "правильную"  Цитата(zltigo @ May 12 2007, 15:48)  Вся эта бодяга действительно отдана на откуп компилятору в зависимости от платформы. Но что интересно - в результате реального эксперимента все компиляторы для x86 платформы выдали тот самый результат, а под ARM и BF выдали warning - не смогли на штатном механизме обеспечить? Интересный такой нюанс. Надо будет GNU на не на x86 платформе спытать. arm-elf-gcc версии 4.1.1 выдает 13.
--------------------
Пишите в личку.
|
|
|
|
|
May 12 2007, 13:14
|

Гуру
     
Группа: Свой
Сообщений: 3 041
Регистрация: 10-01-05
Из: Москва
Пользователь №: 1 874

|
Цитата(zltigo @ May 12 2007, 17:05)  Молча? По умолчанию - да. В программе, приведенной ниже, с ключем -Wsequence-point выдается предупреждение "warning: operation on 'i' may be undefined" внутри main(), но не внутри f(). В обоих случаях - 13. Код int f( int* p1, int* p2 ) { return ++(*p1) + ++(*p2); }
int main( void ) { int i; int x1, x2;
i = 5; x1 = ++i + ++i;
i = 5; x2 = f( &i, &i );
return x2; } P.S. С включенной оптимизацией -O3 main своится к возврату 13.
--------------------
Пишите в личку.
|
|
|
|
|
  |
4 чел. читают эту тему (гостей: 4, скрытых пользователей: 0)
Пользователей: 0
|
|
|