Непонятное поведение компилятора
От:
SergeyT
Дата: 10.10.07 07:16
Оценка:
Здравтсвуйте.
Возможно этот вопрос уже обсуждался, но я к сожалению поиском не нашел.
Результатом работы следующего кода является вывод:
2134, хотя на мой взгляд должно быть 1234, проверял на VC++ 8.0 и 7.1
int inc( )
{
static int val = 0;
return ++val;
}
int main(int argc, _TCHAR* argv[])
{
cout << inc( ) << inc( );
cout << inc( );
cout << inc( );
return 0;
}
Чем объяснить такое поведение компилятора?
Re: Непонятное поведение компилятора
Здравствуйте, SergeyT, Вы писали:
ST>Здравтсвуйте.
ST> Возможно этот вопрос уже обсуждался, но я к сожалению поиском не нашел.
ST>Результатом работы следующего кода является вывод:
ST>2134, хотя на мой взгляд должно быть 1234, проверял на VC++ 8.0 и 7.1
ST>ST>int inc( )
ST>{
ST> static int val = 0;
ST> return ++val;
ST>}
ST>int main(int argc, _TCHAR* argv[])
ST>{
ST> cout << inc( ) << inc( );
ST> cout << inc( );
ST> cout << inc( );
ST> return 0;
ST>}
ST>
ST>Чем объяснить такое поведение компилятора?
Только что проверил, VC 6.0 выдаёт 2134, Intel C++ 8.1 выдаёт 1234, gcc (под Windows) — 2134
Интересно, какой компилятор глючит?
Re: Непонятное поведение компилятора
От:
Sergey
Дата: 10.10.07 07:45
Оценка:
-1
"SergeyT" <11326@users.rsdn.ru> wrote in message
news:2687726@news.rsdn.ru ...
> > cout << inc( ) << inc( );
>
> Чем объяснить такое поведение компилятора?
Вычисляется сначала правый inc( ), потом левый. Обычное дело. Если нужен порядок вычислений, совпадающий с порядком вывода, возвращайте из своих функций временные объекты, которые будут делать инкремент непосредственно в операторе вывода. Типа такого:
struct indent_t
{
struct incr
{
indent_t& src_;
explicit incr(indent_t& src) : src_(src) {}
};
struct decr
{
indent_t& src_;
explicit decr(indent_t& src) : src_(src) {}
};
int n_;
explicit indent_t(int n = 1) : n_(n) {}
incr operator ++()
{ return incr(*this ); }
decr operator --()
{ return decr(*this ); }
};
inline std::ostream& operator <<(std::ostream& os, indent_t v)
{
for (int n = v.n_; n != 0; --n)
os << " " ;
return os;
}
inline std::ostream& operator <<(std::ostream& os, indent_t::incr v)
{
++v.src_.n_;
return os << v.src_;
}
inline std::ostream& operator <<(std::ostream& os, indent_t::decr v)
{
--v.src_.n_;
return os << v.src_;
}
Использование:
indent_t indent;
output << ++indent << "чегонибудь" << endl
<< ++indent << "еще чегонибудь" << endl;
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Непонятное поведение компилятора
Здравствуйте, AleksandrN, Вы писали:
AN>Здравствуйте, SergeyT, Вы писали:
ST>>Здравтсвуйте.
ST>> Возможно этот вопрос уже обсуждался, но я к сожалению поиском не нашел.
ST>>Результатом работы следующего кода является вывод:
ST>>2134, хотя на мой взгляд должно быть 1234, проверял на VC++ 8.0 и 7.1
ST>>ST>>int inc( )
ST>>{
ST>> static int val = 0;
ST>> return ++val;
ST>>}
ST>>int main(int argc, _TCHAR* argv[])
ST>>{
ST>> cout << inc( ) << inc( );
ST>> cout << inc( );
ST>> cout << inc( );
ST>> return 0;
ST>>}
ST>>
ST>>Чем объяснить такое поведение компилятора?
AN>Только что проверил, VC 6.0 выдаёт 2134, Intel C++ 8.1 выдаёт 1234, gcc (под Windows) — 2134
AN>Интересно, какой компилятор глючит?
Понял
Вот ассемблерный код, сгенерированный VC++:
TITLE 1.cpp
.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
; COMDAT ?lock@ios@@QAAXXZ
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ?unlock@ios@@QAAXXZ
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ?unlockbuf@ios@@QAAXXZ
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ?gptr@streambuf@@IBEPADXZ
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ?setf@ios@@QAEJJJ@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ?rdbuf@ios@@QBEPAVstreambuf@@XZ
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ??4istream@@IAEAAV0@ABV0@@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ??6ostream@@QAEAAV0@P6AAAV0@AAV0@@Z@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ??6ostream@@QAEAAV0@D@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ?flush@@YAAAVostream@@AAV1@@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
; COMDAT ??4iostream@@IAEAAV0@PAVstreambuf@@@Z
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
FLAT GROUP _DATA, CONST , _BSS
ASSUME CS : FLAT , DS : FLAT , SS : FLAT
endif
_BSS SEGMENT
_?val@?1??inc @@YAHXZ@4HA DD 01H DUP (?)
_BSS ENDS
PUBLIC ?inc @@YAHXZ ; inc
_TEXT SEGMENT
?inc @@YAHXZ PROC NEAR ; inc
; File 1.cpp
; Line 5
push ebp
mov ebp , esp
; Line 7
mov eax , DWORD PTR _?val@?1??inc @@YAHXZ@4HA
add eax , 1
mov DWORD PTR _?val@?1??inc @@YAHXZ@4HA, eax
mov eax , DWORD PTR _?val@?1??inc @@YAHXZ@4HA
; Line 8
pop ebp
ret 0
?inc @@YAHXZ ENDP ; inc
_TEXT ENDS
PUBLIC _main
EXTRN ??6ostream@@QAEAAV0@H@Z:NEAR ; ostream::operator<<
EXTRN ?cout@@3Vostream_withassign@@A:BYTE ; cout
_TEXT SEGMENT
_main PROC NEAR
; Line 11
push ebp
mov ebp , esp
; Line 12
call ?inc @@YAHXZ ; inc
push eax
call ?inc @@YAHXZ ; inc
push eax
mov ecx , OFFSET FLAT :?cout@@3Vostream_withassign@@A
call ??6ostream@@QAEAAV0@H@Z ; ostream::operator<<
mov ecx , eax
call ??6ostream@@QAEAAV0@H@Z ; ostream::operator<<
; Line 13
call ?inc @@YAHXZ ; inc
push eax
mov ecx , OFFSET FLAT :?cout@@3Vostream_withassign@@A
call ??6ostream@@QAEAAV0@H@Z ; ostream::operator<<
; Line 14
call ?inc @@YAHXZ ; inc
push eax
mov ecx , OFFSET FLAT :?cout@@3Vostream_withassign@@A
call ??6ostream@@QAEAAV0@H@Z ; ostream::operator<<
; Line 16
xor eax , eax
; Line 17
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
т.е. сперва два раза подряд вызываются функция inc() и результаты заталкиваются в стек, затем они берутся из стека и передаются cout::operator<<.
А вот, что генерирует интеловский компилятор:
; -- Machine type IA32
; mark_description "Intel(R) C ++ Compiler for 32-bit applications, Version 8.1 Build 20050628Z %s";
; mark_description "-Qvc6 -Qlocation,link,C :\\Program Files\\Microsoft Visual Studio\\VC98\\Bin -Fa1i.asm";
;ident "Intel(R) C ++ Compiler for 32-bit applications, Version 8.1 Build 20050628Z %s"
;ident "-Qvc6 -Qlocation,link,C :\\Program Files\\Microsoft Visual Studio\\VC98\\Bin -Fa1i.asm"
.486P
.387
OPTION DOTNAME
_TEXT SEGMENT DWORD PUBLIC FLAT 'CODE'
ALIGN 004H
_TEXT ENDS
_DATA SEGMENT DWORD PUBLIC FLAT 'DATA'
ALIGN 004H
_DATA ENDS
_BSS SEGMENT DWORD PUBLIC FLAT 'BSS'
ALIGN 004H
_BSS ENDS
_RDATA SEGMENT DWORD PUBLIC FLAT 'DATA'
ALIGN 004H
_RDATA ENDS
_TEXT1 SEGMENT DWORD PUBLIC FLAT 'CODE'
ALIGN 004H
_TEXT1 ENDS
_DATA1 SEGMENT DWORD PUBLIC FLAT 'DATA'
ALIGN 004H
_DATA1 ENDS
ASSUME CS :FLAT ,DS :FLAT ,SS :FLAT
;ident "-defaultlib:libci"
_DATA SEGMENT DWORD PUBLIC FLAT 'DATA'
_DATA ENDS
_TEXT SEGMENT DWORD PUBLIC FLAT 'CODE'
; COMDAT ?inc @@YAHXZ
; -- Begin ?inc @@YAHXZ
; mark_begin;
IF @Version GE 612
.MMX
MMWORD TEXTEQU <QWORD >
ENDIF
IF @Version GE 614
.XMM
XMMWORD TEXTEQU <OWORD>
ENDIF
ALIGN 4
PUBLIC ?inc @@YAHXZ
?inc @@YAHXZ PROC NEAR
$B1$1: ; Preds $B1$0
mov eax , DWORD PTR val$327 ;7.12
add eax , 1 ;7.12
mov DWORD PTR val$327, eax ;7.12
ret ;7.12
ALIGN 4
; LOE
; mark_end;
?inc @@YAHXZ ENDP
;?inc @@YAHXZ ENDS
_TEXT ENDS
_BSS SEGMENT DWORD PUBLIC FLAT 'BSS'
val$327 DD 1 DUP (?) ; pad
_BSS ENDS
_DATA SEGMENT DWORD PUBLIC FLAT 'DATA'
_DATA ENDS
; -- End ?inc @@YAHXZ
_DATA SEGMENT DWORD PUBLIC FLAT 'DATA'
_DATA ENDS
_TEXT SEGMENT DWORD PUBLIC FLAT 'CODE'
; COMDAT _main
; -- Begin _main
; mark_begin;
ALIGN 4
PUBLIC _main
_main PROC NEAR
; parameter 1: 8 + ebp
; parameter 2: 12 + ebp
$B2$1: ; Preds $B2$0
push ebp ;11.1
mov ebp , esp ;11.1
sub esp , 3 ;11.1
and esp , -8 ;11.1
add esp , 4 ;11.1
push ebx ;11.1
push esi ;11.1
call ___intel_proc_init ;11.1
call ?inc @@YAHXZ ;12.11
; LOE eax esi edi
$B2$2: ; Preds $B2$1
push eax ;12.11
mov ecx , OFFSET FLAT : ?cout@@3Vostream_withassign@@A ;12.8
call ??6ostream@@QAEAAV0@H@Z ;12.8
; LOE eax esi edi
$B2$13: ; Preds $B2$2
mov ebx , eax ;12.8
; LOE ebx esi edi
$B2$3: ; Preds $B2$13
call ?inc @@YAHXZ ;12.21
; LOE eax ebx esi edi
$B2$4: ; Preds $B2$3
push eax ;12.21
mov ecx , ebx ;12.18
call ??6ostream@@QAEAAV0@H@Z ;12.18
; LOE esi edi
$B2$5: ; Preds $B2$4
call ?inc @@YAHXZ ;13.11
; LOE eax esi edi
$B2$6: ; Preds $B2$5
push eax ;13.11
mov ecx , OFFSET FLAT : ?cout@@3Vostream_withassign@@A ;13.8
call ??6ostream@@QAEAAV0@H@Z ;13.8
; LOE esi edi
$B2$7: ; Preds $B2$6
call ?inc @@YAHXZ ;14.11
; LOE eax esi edi
$B2$8: ; Preds $B2$7
push eax ;14.11
mov ecx , OFFSET FLAT : ?cout@@3Vostream_withassign@@A ;14.8
call ??6ostream@@QAEAAV0@H@Z ;14.8
; LOE esi edi
$B2$9: ; Preds $B2$8
xor eax , eax ;16.10
pop ecx ;16.10
pop ebx ;16.10
mov esp , ebp ;16.10
pop ebp ;16.10
ret ;16.10
ALIGN 4
; LOE
; mark_end;
_main ENDP
;_main ENDS
_TEXT ENDS
_DATA SEGMENT DWORD PUBLIC FLAT 'DATA'
_DATA ENDS
; -- End _main
_DATA SEGMENT DWORD PUBLIC FLAT 'DATA'
EXTRN ?cout@@3Vostream_withassign@@A:BYTE
EXTRN ?cout@@3Vostream_withassign@@A:BYTE
_DATA ENDS
EXTRN ??6ostream@@QAEAAV0@H@Z:PROC
EXTRN ___intel_proc_init:PROC
END
ИМХО, поведение Intel C++ — правильное.
Re[3]: Непонятное поведение компилятора
От:
Sergey
Дата: 10.10.07 07:47
Оценка:
> ИМХО, поведение Intel C++ — правильное.
У обоих поведение правильное. Никто не обещал, что вычислятся все будет в том порядке, в каком вы ожидаете.
Posted via RSDN NNTP Server 2.1 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re: Непонятное поведение компилятора
Здравствуйте, SergeyT, Вы писали:
ST>ST>int main(int argc, _TCHAR* argv[])
ST>{
ST> cout << inc( ) << inc( );
ST> cout << inc( );
ST> cout << inc( );
ST> return 0;
ST>}
ST>
ST>Чем объяснить такое поведение компилятора?
Тем, что порядок вычисления аргументов функций не определён...
твоё выражение cout << inc() << inc() можно переписать так:
operator << ( operator << ( count, inc() ), inc() )
Это немного не корректно, но более точное разбирательство нас только запутает.
Ну так вот, компилятор может вычислить аргументы обеих operator <<
в любом порядке
Например он может вычислять все аргументы слева направо.
Тогда он сначала вычислит operator( count, inc() ), а потом вычислит второй inc(), в результате чего выведется 12, а может вычислять справа налево, тогда он сначала вычислить второй inc(), потом operator( count, inc() ), в результате чего выведет 21...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Вот так "мегакодеры" и возникают...
От:
Erop
Дата: 10.10.07 08:00
Оценка:
Здравствуйте, Sergey!
Люди совершенно базовых вещей не понимают, а ты им таких крокодилов рекоммендуешь
((
Есть ведь намного более простое и понятное решение, например такое:
const int toOut1 = inc();
const int toOut2 = inc();
std::cout << toOut1 << toOut2;
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Непонятное поведение компилятора
Здравствуйте, SergeyT, Вы писали:
ST>Чем объяснить такое поведение компилятора?
А чем объяснить рассуждения людей, которые считают, что должно выводится 1234?
Re[4]: Непонятное поведение компилятора
Здравствуйте, Sergey, Вы писали:
>> ИМХО, поведение Intel C++ — правильное.
S>У обоих поведение правильное. Никто не обещал, что вычислятся все будет в том порядке, в каком вы ожидаете.
Да, поглядел в стандарт — определён только порядок вычисления выражения (с некоторыми исключениями), а порядок вычисления операндов выражения не определён.
Re[2]: Непонятное поведение компилятора
От:
Кодт
Дата: 10.10.07 09:23
Оценка:
Здравствуйте, Erop, Вы писали:
ST>>Чем объяснить такое поведение компилятора?
E>Тем, что порядок вычисления аргументов функций не определён...
... что в данном случае ведёт к
unspecified behavior
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Пока на собственное сообщение не было ответов, его можно удалить.
Удалить