Gönderen Konu: ılginç C++ davranışı  (Okunma sayısı 10108 defa)

ılginç C++ davranışı

« : 02.02.2012 17:35:56 »
Hızlı düğmeleri aç

skate

İleti: 5.245

A Sinner Scener
Çevrimdışı
  • Administrator
  • *****
  • Hero Member
    • Profili Görüntüle
    • http://www.akaydin.com/
printf ve türevlerinde kullanılan %d, %s v.b. string içi değişken kullanımını bilirsiniz.

Kod: [Seç]
printf("hedenin degeri: %d", hede);
şimdi şunu bir deneyin bakalım.
Kod: [Seç]
printf("%d %d", 4294967298, 8);
ılk parametre 32 bit'i geçen bir değer. Dolayısıyla ekrana ilk yazılacak değerin 4294967298 olmaması beni pek şaşırtmıyor. Peki ya ikinci değerin 8 yerine 1 olmasına ne demeli?

Bu güne kadar hep %'lerle parametrelerin indexlerinin birebir örtüşmesi gerektiğine inanmıştım ancak anlaşılan durum pek de bu biçimde işlemiyormuş.

4294967298 sayısını 64 bit binary olarak yazacak olursak.

Kod: [Seç]
4294967298 = %0000000000000000000000000000000100000000000000000000000000000010
32'şer bite parçalayınca da

Kod: [Seç]
%00000000000000000000000000000001 = 1
%00000000000000000000000000000010 = 2
yani high dword 1, low dword 2. Yani 4294967298 yerine 32bit kapasiteli %d'nin low dword'ü yazması gayet normal. ancak sonraki %d'nin yazdığı 1 de aslında ilk parametre'nin high dword'ü. peki ya 8 nereye gitti?

Kod: [Seç]
printf("%d %d %d", 4294967298, 8);
Bir tane daha %d eklediğimizde 2. parametre 3. %d'de karşımıza çıkıyor. Tabii ki bunun yerine 64bit, yani long long tipindeki değişkeni;

Kod: [Seç]
printf("%lld %d", 4294967298, 8);
bu biçimde %lld ile yazdırırsak parametreler olması gerektiği şekilde pattern ile örtüşüyor. ilginç değil mi?

ılginç C++ davranışı

« Yanıtla #1 : 03.02.2012 04:58:08 »
Hızlı düğmeleri aç

nightlord

İleti: 1.085

Çevrimdışı
  • Administrator
  • *****
  • Hero Member
    • Profili Görüntüle
    • http://www.nightnetwork.org
ilginc degil bana bug gibi gorundu. C++ ile ilgili degil de printf implementasyonunda sanki (veya vararg olayinda). hangi compiler'da ?

ılginç C++ davranışı

« Yanıtla #2 : 03.02.2012 08:39:49 »
Hızlı düğmeleri aç

anesthetic

İleti: 403

Çevrimdışı
  • ****
  • Sr. Member
    • Profili Görüntüle
    • http://resident.tr-demoscene.info/
stack'e 12byte arg atiyosun, printf de %d'leri gorur gormez dorder dorder okuyup donuyor. sonucta dort byte eksik okudugu icin de stacki corrupt ediyor.

benim aldigim sonuclar soyle
32bit gcc4.1.2, c++, derlemedi (integer constant is too large for long type)
32bit gcc4.1.2, c, "2 1"
64bit gcc4.3.4, c++, "2 8"
64bit gcc4.3.4, c, "2 8"

sondakilerin muhtemelen 4 degil 8byte boundary'de stack kullandigi icin dogru calistigini dusunup sunu denedim:

Kod: [Seç]
printf("%d %d", 9221231229496729812301263126032371021237123591203120321612314214124, 8);
sonuc bekledigim gibi cikti:
c: "-2089386260 -1"
c++: "-2089386260 -1"
« Son Düzenleme: 03.02.2012 08:59:09 Gönderen: anesthetic »

ılginç C++ davranışı

« Yanıtla #3 : 03.02.2012 08:57:16 »
Hızlı düğmeleri aç

anesthetic

İleti: 403

Çevrimdışı
  • ****
  • Sr. Member
    • Profili Görüntüle
    • http://resident.tr-demoscene.info/
stack boundary efektini dogrulamak icin 32bitlik sistemde sunu denedim:

Kod: [Seç]
printf("%d %d\n", 4294967298, 8);
printf("%c %c\n", 256 + 'A', 'B');

ilk satirda dorder bytelik seyler yazdiriyorum ama ilk gonderdigim arg 8 bytelik, sonucta ikinci argi degil de ilkinin ikinci dortlugunu yaziyor.
Kod: [Seç]
|<-------------- 4294967298 ----------------->| |<---------- 8 --------->| (bunu gonderdik)
byte1 byte2 byte3 byte4 byte5 byte6 byte7 byte8 byte9 byte10 byte11 byte12
|<-------- 2 -------->| |<-------- 1 -------->| (bunu aldi)

ikinci satirda ise birer bytelik seyler yazdiriyorum. yine ilk arg bir bytea sigmadigi icin yanlis yazilacak ama ikinci arg hemen ardindan degil sonraki dort bytea pushlandigi ve printf de ordan aldigi icin ikinci argumani dogru yazmasi lazim.

sonucta da tahmin ettigim gibi oldu:

2 1
A B

bunu da cizersek soyle bi donmustu her sey:
Kod: [Seç]
|<-------256+A------->| || (bunu gonderdik)
byte1 byte2 byte3 byte4 byte5 byte6 byte7 byte8
|| || || (bunu aldi)

aslinda derlendikten sonra cikan koda bakip emin olunabilir ama cok usendim :)
« Son Düzenleme: 03.02.2012 09:01:42 Gönderen: anesthetic »

ılginç C++ davranışı

« Yanıtla #4 : 03.02.2012 10:27:51 »
Hızlı düğmeleri aç

skate

İleti: 5.245

A Sinner Scener
Çevrimdışı
  • Administrator
  • *****
  • Hero Member
    • Profili Görüntüle
    • http://www.akaydin.com/
@nightlord: Visual Studio 2008 ve 2010 ile test ettim. 32 bit.

@anesthetic: detaylı inceleme için sağol.

ılginç C++ davranışı

« Yanıtla #5 : 03.02.2012 16:53:47 »
Hızlı düğmeleri aç

coze

İleti: 238

Çevrimdışı
  • ***
  • Full Member
    • Profili Görüntüle
burdan bu durumda en duzgun davranan compiler'in gccC++ 32 bit oldugu sonucunu cikarabilirmiyiz ?

ılginç C++ davranışı

« Yanıtla #6 : 03.02.2012 17:43:29 »
Hızlı düğmeleri aç

skate

İleti: 5.245

A Sinner Scener
Çevrimdışı
  • Administrator
  • *****
  • Hero Member
    • Profili Görüntüle
    • http://www.akaydin.com/
Alıntı yapılan: coze;28709
burdan bu durumda en duzgun davranan compiler'in gccC++ 32 bit oldugu sonucunu cikarabilirmiyiz ?
Burdan evet, böyle bir sonuç çıkıyor. Ancak daha birçok compiler var. Belki biri 32 bit bile olsa 2. parametreye kadar stack'de ilerliyordur taşma da olsa. Öyle bir compiler varsa en uygunu o demektir. Açıkçası bana "integer constant is too large for long type" daha çok warning level bir mesaj gibi geldi, öyle değil mi?

ılginç C++ davranışı

« Yanıtla #7 : 04.02.2012 06:46:14 »
Hızlı düğmeleri aç

anesthetic

İleti: 403

Çevrimdışı
  • ****
  • Sr. Member
    • Profili Görüntüle
    • http://resident.tr-demoscene.info/
Hayir warning degil, error. Hatta surda: http://codepad.org/66z3IRNm
32bitle alakasi yok tabi. Eski gcc versiyonu hata verirken, yenisi warning veriyor:
test.cpp:4: warning: format %d expects type int, but argument 2 has type long int


Yani printf'e ozgu bir kontrol eklemisler. Yoksa kendi var-argli fonksiyonumda bu warning'i vermiyor.

ılginç C++ davranışı

« Yanıtla #8 : 04.02.2012 10:27:13 »
Hızlı düğmeleri aç

skate

İleti: 5.245

A Sinner Scener
Çevrimdışı
  • Administrator
  • *****
  • Hero Member
    • Profili Görüntüle
    • http://www.akaydin.com/
@anesthetic: ılk verdiğin örnekte compile etmediğine göre error olduğunu anladım tabii ki. Benim kastettiğim "olması gereken" idi. Bence overflow, possible loss of data v.s. C/C++ için her zaman warning'dir.

http://codepad.org/dkua2oBW

Bu örnekte de aynı hatayı veriyor. Aynısını Visual Studio'da denediğimde compile ediyor ancak aşağıdaki 2 warning'i veriyor.

Warning 1 warning C4305: 'initializing' : truncation from '__int64' to 'int'
Warning 2 warning C4309: 'initializing' : truncation of constant value

Ben warning almaya daha alışığım bu tarz durumlarda fatal error'e göre. Bug çıkmaması açısından hangisi daha faydalı elbette ki tartışılabilir. Ancak overflow error bana sanki basic dilinden kalma bir "error" mesajı gibi geliyor. Birçok durumda insan overflow etmesi dert olmayacak şeyleri bilerek bu biçimde kullanabiliyor da. Örneğin her defasında unique bir id'e ihtiyacınız var diyelim ama id'ler o anlık kullanılıyorlar sadece, sonrasında bir önemi yok. id++ şeklinde kullan gitsin, gerekirse 0'a geri dönsün umrumda olmaz benim.

şu ikisine bakınca bence birini "error" olarak algılayıp, diğerinde warning bile vermemesi saçma geliyor. Sonuç olarak carry flag'i yedi mi yemedi mi? Di mi ama. :)

http://codepad.org/cjct9oUW
http://codepad.org/fLdnGygH