|
[ sportbili @ 15.09.2002. 15:21 ] @
| Sta se ovde desava?
U nekom clanku na internetu sam nasao sledece
Citat:
26. Under my compiler, the code "int i = 7; printf("%d\n", i++ * i++);"
prints 49. Regardless of the order of evaluation, shouldn't it
print 56?
A: The operations implied by the postincrement and postdecrement
operators ++ and -- are performed at some time after the operand's
former values are yielded and before the end of the expression, but
not necessarily immediately after, or before other parts of the
expression are evaluated.
Code:
int main() {
int a=7;
printf("%d\n", a++ * a++);
printf("%d %d %d\n", a++, a++, a++); // <------
}
Postavila su se dva pitanja. Prvo, tvrdnja iz clanka nije tacna jer dobijam bas
56 sto znaci da je a u drugom izrazu (a++) vec dobila drugu vrednost. Pitanje glasi
da li to zavisi od kompajlera ili neceg drugog? Tekst je iz 1991. znaci nakon ANSI C-a
Drugo se tice printf-a. Kada izvrsim program drugi printf izbacuje
11 10 9
znaci obrnuto od onoga sto sam ocekivao (9 10 11). Zasto?
|
[ Dragi Tata @ 15.09.2002. 23:19 ] @
1. I na mom kompajleru (VC.7.0) izbacuje 49, a to bi i trebalo po standardu. Koji kompajler ti koristiš?
2. Na mom kompajleru, rezultat je 7, 7, 7 a a je 10 posle poziva printf - opet onako kako treba da bude...
[ random @ 16.09.2002. 00:42 ] @
Jako zanimljivo. GCC će ih takođe ispisati obrnutim redom. Evo odgovarajućeg asm koda:
Code: movl $0x7,0xfffffffc(%ebp) ; snimamo vrednost "7" negde u data segment
; sad se spremamo da pozovemo printf, a parametre stavljamo na stek obrnutim redosledom
mov 0xfffffffc(%ebp),%eax ; treći parametar ide na stek (ono treće a++);
push %eax ; pa odatle "7" ide na stek
incl 0xfffffffc(%ebp) ; post-inkrementira se treći parametar (sad je 8)
mov 0xfffffffc(%ebp),%eax ; e ovde je caka, jer iz tačke gledišta kompajlera, svi parametri su isti, pa se koristi isti podatak (već post-inkrementiran, iznosi 8)
push %eax ; "8" ide na stek
incl 0xfffffffc(%ebp) ; ponovo se inkrementira isti taj podatak
mov 0xfffffffc(%ebp),%eax ; i još jednom sve isto, za prvi parametar
push %eax ; "9" ide na stek
incl 0xfffffffc(%ebp)
push $0x401040
call 0x401100 <printf>
Na kraju kad printf() pokupi parametre, on ih kupi obruntim redosledom od onog kojim su stavljani (LIFO disciplina), znači prvo 9, pa 8, pa 7. Problem leži u tome što korisnik/programer pretpostavlja da se parametri procesiraju sa leva na desno, što nije slučaj.
Pravo rešenje bi bilo da se naprave posebne kopije svakog parametra u memoriji, bez obzira da li su isti ili ne, a ne da se inkrementira jedan te isti podatak dvaput pri evaluaciji izraza. Dakle problem je kod kompajera.
Zanimljivo je da će ih GCC, čak i kad se specificira -ansi flag, ispisati brojeve obrnutim redosledom. Zaista čudan propust, ako standard kaže drugačije?
Što se prvog pitanja tiče, sa GCC-om sam dobio 49. Provera dibagerom je pokazala da je prvo pomnožio vrednost samu sa sobom, a zatim je dvaput inkrementirao.
[ Dragi Tata @ 16.09.2002. 01:29 ] @
Ako ćemo po asembleru, evo šta uradi VC.NET u release izdanju:
Code:
00401000 push 7
00401002 push 7
00401004 push 7
00401006 push offset string "%d %d %d\n" (4060ECh)
0040100B call printf (401020h)
00401010 add esp,10h
Dakle, kompajler je dovoljno pametan da zaključi da mu vrednost promenljive ne treba posle štampanja, pa je nije ni povećavao.
U debug verziji:
Code:
00411A2E mov dword ptr [a],7
00411A35 mov eax,dword ptr [a]
00411A38 mov dword ptr [ebp-0D0h],eax
00411A3E mov ecx,dword ptr [a]
00411A41 add ecx,1
00411A44 mov dword ptr [a],ecx
00411A47 mov edx,dword ptr [a]
00411A4A mov dword ptr [ebp-0D4h],edx
00411A50 mov eax,dword ptr [a]
00411A53 add eax,1
00411A56 mov dword ptr [a],eax
00411A59 mov ecx,dword ptr [a]
00411A5C mov dword ptr [ebp-0D8h],ecx
00411A62 mov edx,dword ptr [a]
00411A65 add edx,1
00411A68 mov dword ptr [a],edx
00411A6B mov eax,dword ptr [ebp-0D0h]
00411A71 push eax
00411A72 mov ecx,dword ptr [ebp-0D4h]
00411A78 push ecx
00411A79 mov edx,dword ptr [ebp-0D8h]
00411A7F push edx
00411A80 push offset string "%d %d %d\n" (4240B8h)
00411A85 call @ILT+1150(_printf) (411483h)
00411A8A add esp,10h
U debug verziji je ispisao 9,8,7 , hehehe...
Uglavnom, zanimljiva "gimnastika za mozak", ali najtoplije preporučujem da se ovakve konstrukcije izbegavaju kod ozbiljnog rada.
[ Milan Aksic @ 16.09.2002. 02:05 ] @
I Borlandov kompajler ce ispisati vrednosti na isti nacin.
Inace zanimljivon posle drugog poziva printf-a u VC 6 a je 9, 9, 9, i ovde se postfiksni operater primenjuje u drugom pozivu printf-a od prvog do zadnjeg parametra, i u eventualnom sledecem pozivu postavlja tacnu vrednost 12, a ne od operanda do operanda.
[ sportbili @ 16.09.2002. 03:18 ] @
Citat:
1. I na mom kompajleru (VC.7.0) izbacuje 49, a to bi i trebalo po standardu. Koji kompajler ti koristiš?
Borland C/C++ 3.1 Compiler :-)
Matoro ali sluzi. Nego trazio sam po netu tutoriale za asm za pocetnike
pa ako neko zna neki, link bi dobro dosao (ja sam nasao neke ali
mozda imate preporuku?)
Pozdravi
[ sspasic @ 16.09.2002. 18:06 ] @
Tu je ispravan odgovor (po ansi) 49 ili 56, zavisno od implementacije.
C za sve operatore (sem || i &&) ne odredjuje redosled kojim se podizrazi izracunavaju.
Jedino sto garantuje kod i++ je da ce se vrednost uvecati POSLE izracunavanja vrednosti i (ali ne mora odmah posle), a ne posle izracunavanja vrednosti i++ * i++.
Slicno vazi i za redosled izracunavanja parametara u pozivu funkcije.
Pogledajte K&R, poglavlje 2.2
[ Rapaic Rajko @ 16.09.2002. 20:51 ] @
Ja, iskreno, nisam toliko dobar poznavalac standarda, ali po nekoj logici, trebalo bi da ispravan rezultat bude 49. Razmislite, sta je logicno u sledecem slucaju?
int a=7, b=7;
printf("%d\n",a++ * b++);
Ocigledno je (bar meni) jedini ispravan rezultat 49. Nazalost, izgleda da moj kompajler (CBuilder) ne misli tako.
Rajko
[ Dragi Tata @ 16.09.2002. 21:02 ] @
Kao što već napomenuh, pitanje je od čisto akademskog značaja. U praksi, printf("%d\n",a++ * b++); i slične egzibicije se smatraju lošim kodom i treba ih izbegavati po svaku cenu. Nikakve koristi od toga, a kod je nečitljiv, teško ga je debugovati, a da i ne pominjem da razni kompajleri (ili čak isti sa promenjenim opcijama za optimizaciju) različito izvršavaju takve instrukcije, kao što se da videti iz prethodnih postova. Jednostavno, batalite to...
Copyright (C) 2001-2025 by www.elitesecurity.org. All rights reserved.
|