a++ ve ++a olayı

Başlatan 94onur94, 27 Ekim 2017, 01:08:23

94onur94

Arkadaşlar ne kadar uğraştıysam aşağıdaki iki programın nasıl o sonucu döndürdüğünü anlayamadım. Bilen birisi yardım ederse çok sevinirim.

Bildiğim durumlar ++ değişkenin başında ise (++a) önce a'nın değerini 1 arttır sonra a'yı döndür, ++ değişkenin sonunda ise (a++) önce a'yı döndür sonra a'nın değerini 1 arttır.

int a=2;
printf("%d %d", a++, ++a);

Ekranda "3 4" yazıyor. Beklediğim "2 4".

int a=2;
printf("%d %d", ++a, ++a);

Ekranda "4 4" yazıyor. Beklediğim "3 4"

volkanunal

Bende bir ara buna takılmış , bu olay tamamen derleyici kaynaklı hocam , başka bir derleyici beklediğiniz sonucu görebilirsiniz.Derleyici optimize ediyor printf içerisnde ki argümanları sanırım.
Primum nil nocere

sovalye

#2
Printf argumanlari sagdan sola cagiriyor, sonuclar icin adres degerleri alip, sonra aldigi adreslerdeki degerleri okuyup, toptan ekrana basiyor.
Ayrica ikinci ek bilgi de a++ arttirmadan sonraki degeri kullanilana kadar ayri bir adreste tutarken, ++a bunu tek cycle icinde ayni adreste degistirir. Bu yuzden programcilar ++a yi daha cok sever.

Cikacak sonuc a++ ve ++a yi cagirdigimizda sonucta okuyacagimiz a adresleri farkli farkli oluyor.'


Ilk ornek icin once ++a yi sonra a++ yi cagirdi. ++a isleme girmeden once degeri arttircak yani a=3 suan. Sonra soldan saga basmaya baslayacak.a++ geldi. Bu a nin 3 degerini aldi, 1 arttirdi. ++a yi ekrana basmak icin a nin yerine bakti orasi su an 4. Ekrana basti.


Ikinci ornek yukardaki gibi iki tane ++a var. Bunlari yaptiktan sonra a=4 oldu. Su an iki arguman icin uretilen adres ayni. Sonuc olarak ikisini de 4 de basti.


printf("%d %d", ++a, a++); orneginin sonucu 4 2. Simdi bunu bakalim. Soldan saga cagiriyoruz. Once a++ geldi. a++ yuzunden adresler ayrildi yine. 2. Arguman icinde 2 kayitli olan yeri gosteriyor. Sonra 1 arttirdi, sonra digeri 1 arttirdi. 1. Arguman 4 oldu.


En guzel ornek bence bu

int a=2;
printf("% d %d %d %d %d", ++a, a, a++, a++,++a);

Sonuc:6 6 4 3 6
Yukardaki mantikla cozumlersek. Once soldan saga cagirdik ++a anin degerini arttirdi a=3 oldu. Ama deger okunacak adres hala ayni. Simdi ++a lar adreslerini ayirip gecici degerleri depoladilar. Ilki 3 un oldugu yeri, ikincisi 4. Oldugu adresi dondurdu. Sonra ++a cagrilari islendi. A=6 oldu. Sonra ++a ve a ayni adresleri kullandigi icin hepsi ekrana ayni seyi basti.

Hocam bu saatte yazilan mesajda hatalar olabilir, anlatimim acik olmayabilir. Konuyu cok once kurcalamistim, duzgun hatirlayamiyor olabilirim. Bir sikinti varsa tekrar yazarsiniz.

vitruvius

Printf argumanlari sagdan sola cagiriyor demek yanlis olur. Hem bu neden yanlis olur, hem de konu sahibine cevap verecek olursak.

Fonksiyon Argumanlarin Cagirilma Sirasi

Elinde muhtemelen C99 standardi yoktur, (varsa 6.5.2.2 madde 10'a bakabilirsin) o yuzden buraya Function calls basligindan bir madde kopyaliyorum.

Alıntı YapThe order of evaluation of the function designator, the actual arguments, and subexpressions within the actual arguments is unspecified, but there is a sequence point before the actual call.

Yani bir fonksiyonun argumanlarinin hangi sira ile cagirilacagi tanimsizdir. Hatta yine standardda ayni baslik madde 12'de ornegi de mevut:

Alıntı YapEXAMPLE In the function call

(*pf[f1()]) (f2(), f3() + f4())

the functions f1, f2, f3, and f4 may be called in any order. All side effects have to be completed before the function pointed to by pf[f1()] is called.

Sequencer Point (On Bilgi)
Tanimini yine C99 standardindan (5.1.2.2.3) alabiliriz:
Alıntı YapEvaluation of an expression may produce side effects. At certain specified points in the execution sequence called sequence points, all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place.

Peki bu ne demek?
Bir sequence point olustugunda, o noktadan onceki butun operasyonlarin (yan etkileriyle birlikte) tamamlandiginin garantisidir. Bu, programlamada bir degerin "gecerlilik" durumuna bakildigi cok temel bir konudur. Bu yuzden elindeki C kitaplarindan, ya da online olarak daha cok okumani tavsiye ederim.

Hangi durumlarda sequence point olusur?
C99 standardi annex C:
Alıntı YapThe following are the sequence points described in 5.1.2.3:
— The call to a function, after the arguments have been evaluated (6.5.2.2).
— The end of the first operand of the following operators: logical AND && (6.5.13); logical OR || (6.5.14); conditional ? (6.5.15); comma , (6.5.17).
— The end of a full declarator: declarators (6.7.5);
— The end of a full expression: an initializer (6.7.8); the expression in an expression statement (6.8.3); the controlling expression of a selection statement (if or switch) (6.8.4); the controlling expression of a while or do statement (6.8.5); each of the expressions of a for statement (6.8.5.3); the expression in a return statement (6.8.6.4).
— Immediately before a library function returns (7.1.4).
— After the actions associated with each formatted input/output function conversion specifier (7.19.6, 7.24.2).
— Immediately before and immediately after each call to a comparison function, and also between any call to a comparison function and any movement of the objects passed as arguments to that call (7.20.5).

Ayni sequencer point icinde bir degiskenin degerini birden fazla degistirmek
Senin kodundaki temel sikinti burada basliyor. C99 standardi (6.5 madde 2) der ki:

Alıntı YapBetween the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression.72) Furthermore, the prior value shall be read only to determine the value to be stored.73)

Peki bu ne demek?
Burada iki konu var zaten kendi de dipnot dusmus (72 ve 73 numarali dipnotlar, asagida paylasacagim).

  • Ayni sequencer point icinde bir degiskenin degerini birden fazla degistirmemelisin.
  • "Left hand side" dedigimiz "sol taraf" da read-only olmali, yani orada da bir degiskenin degerini degistirmemelisin.
Bunlardan birini yaptigin takdirde programin undefined behavior'a (tanimsiz) girer ve programin ne yapacagini kestiremezsin.

Dipnot 73 der ki (az once C99 standardindan verdigim kisimdaki):
Alıntı YapThis paragraph renders undefined statement expressions such as
              i = ++i + 1;
              a[i++] = i;
while allowing
              i = i + 1;
              a = i;

Yani ust kisimdaki ornekler (ilki senin de kullandigin ayni sequence point icinde bir degiskeni iki kere degistirmek) undefined behavior'dur.

Ancak alt grupta herhangi bir sikinti yok.

Derleyici bu durumda ne yapiyor?
Iyi bir derleyici uyari ayarlari yuksek seviyedeyse (gcc gibi :) ) bu durumlarda uyari verir.

Mesela asagidaki kodu

#include <stdio.h>

int main()
{
  int a = 10;
  int i = 10;
  i = i++;
  printf("%d %d %d\n", ++a, a++, a);
  return 0;
}


gcc ile asagidaki gibi derlersem:
gcc printf.c -Wall -O0 -o printf


Asagidaki uyarilari alirim:
Alıntı Yapprintf.c:7:8: warning: multiple unsequenced modifications to 'i' [-Wunsequenced]
  i = i++;
    ~  ^
printf.c:8:24: warning: multiple unsequenced modifications to 'a' [-Wunsequenced]
  printf("%d %d %d\n", ++a, a++, a);
                       ^     ~~
2 warnings generated.

Ikisi de ayni turden bir uyari ve kaynagi ayni sequence point icinde bir degiskenin degerini birden fazla degistirmeye calistigimiz icin.

Dedigim gibi bu bir uyaridir, hata degil. Yani program derlenir, ve hatta calistirirsam asagidaki ciktiyi alirim:
Alıntı Yap11 11 12

Kolay gelsin.

sovalye

Hocam, undefined behavior u oldugu yukarda yaziyordu, karsi cikmadim ona. Ama gcc ile calistigimi eklemem gerekirdi.


Yazdiginiz kodu online derleyicilerde  gcc4.3.2 , gcc5.3.0 , gcc6 icin derledim. Kendi makinamda gcc7.2 ve c99 standardi icin derledim. Sonuc tahmin ettigim sekilde 12 10 12 cikiyor.


Dediginiz sonucu clang4.0 ile elde edebildim.

vitruvius

Ben senin verdigin cevaba karsilik degil de baslik sahibine genel bilgi olsun diye cevap verdim :)