C Pointer Egzersizleri

Suradan devam.

1

void sayi_topla(int sayi1, int sayi2, int *sonuc);

sayi1 ile sayi2'yi toplayip sonucu sonuc'a yazacak.

2

int kalanli_bol(int bolunen, int bolen, int *sonuc, int *kalan);

Yapacagi isin isimlerden anlasilmasi lazim.
Basariliysa 0, hata durumunda != 0 (veya daha iyisi <0) dondurelim.

3

void psayi_topla(int *sayi1, int *sayi2, int *toplam);

Ilki gibi, ama sayilara pointer aliyor

4

int *buyuk_sayi(int *sayi1, int *sayi2);

Iki sayiya pointer alip buyugunu (buyuk sayiya point edeni) donduruyor.

5

void buyuk_sayi(int *sayi1, int *sayi2, int **buyuk);

Iki sayiya pointer alip buyugunu (buyuk sayiya point edeni) donduruyor. (Evet, yukaridan copy paste ettim)

6

void sayi_topla2(int sayi1, int sayi2, void *sonuc);

Kotu bir tasarim, lakin void*'i gosteren bir ornek. C’de cast kullanmadan yazilabilmesi ve cagrilabilmesi lazim.

7

struct bolme_sonucu {
	int bolum;
	int kalan;
};
void kalanli_bol(int bolunen, int bolen, struct bolme_sonucu *sonuc);
void kalanli_bol2(int bolunen, int bolen, void *sonuc);

Bunlari yazarken kotu isimlerin okunmasi zor koda yol actigini gormek mumkun olabilir :slight_smile:
(Bolme sonucunun teknik ismini bulmasam daha da feci olacakti)

8

void buyuk_sayi2(int *sayi1, int *sayi2, void *pp_buyuk);

Iki sayiya pointer alip buyuk sayiya point edeni donduruyor.
Yukaridaki suna esit: void buyuk_sayi(int *sayi1, int *sayi2, int **buyuk);
Yani void * olarak aldigi deger pointera pointer.

Yukaridaki fonksiyon isimlerini biraz degistirdim, sorun olmaz insallah.

4 Beğeni

Oldu herhalde.

void sayi_topla(int x, int y, int *z)
{
   *z = x + y;
}
int kalanli_bol(int bolunen, int bolen, int *sonuc, int *kalan)
{
   *sonuc = bolunen / bolen; // sonuc dediğiniz divison değil mi?
   *kalan = bolunen % bolen;
   
   // Kalanlı bölünüyorsa (başarılı) 0, bölünmüyorsa -1
   if (*kalan == 0)
   {
      return -1;
   }
   return 0;
}

Şu şekilde test ettim:

void test_kalanli_bol()
{
   int sonuc;
   int kalan;

   int result = kalanli_bol(247, 13, &sonuc, &kalan);

   printf("Returned: %d\n", result);
   printf("Divison: %d\n", sonuc);
   printf("Remaining: %d\n", kalan);
}
/*
Returned: -1
Divison: 19
Remaining: 0
*/

void test_sayi_topla()
{
   int num_1 = 1;
   int num_2 = 2;
   int result;

   sayi_topla(num_1, num_2, &result);

   printf("num_1 + num_2 = %d\n", result);
}
/*
num_1 + num_2 = 3
*/
1 Beğeni

Gayet olmus!

Hata icin divide-by-0 (veya gelen pointer’lar NULL) dusunuyordum ama maksat hem return value’dan hem pointer’dan deger dondurmek oldugu icin problem yok.

Peki… (soruya 3 ekliyorum)

1 Beğeni

Üç ve dört:

void psayi_topla(int *sayi1, int *sayi2, int *toplam)
{
   *toplam = *sayi1 + *sayi2;
}

// Esitligi ayrica handle'lamiyorum
int *buyuk_sayi(int *sayi1, int *sayi2)
{
   if (*sayi1 > *sayi2)
   {
      return sayi1;
   }
   return sayi2;
}

Test:

void test_psayi_topla()
{
   int sayi1 = 2;
   int sayi2 = 2;
   int toplam;

   psayi_topla(&sayi1, &sayi2, &toplam);

   printf("toplam = %d\n", toplam);
}
// toplam = 4

void test_buyuk_sayi()
{
   int sayi1 = 17;
   int sayi2 = 71;

   int *result = buyuk_sayi(&sayi1, &sayi2);

   printf("*buyuk_sayi() result: %d\n", *result);
}
// *buyuk_sayi() result: 71
1 Beğeni

Hm, o zaman bu sefer şöyle yapıyoruz sanırım:

void buyuk_sayi(int *sayi1, int *sayi2, int **buyuk)
{
   if (*sayi1 > *sayi2)
   {
      *buyuk = sayi1;
   }
   *buyuk = sayi2;
}

Test:

void test_buyuk_sayi()
{
   int sayi1 = 7;
   int sayi2 = 13;
   int *buyuk;

   buyuk_sayi(&sayi1, &sayi2, &buyuk);

   printf("Greatest: %d\n", *buyuk);
}
// Greatest: 13
1 Beğeni

Aynen!

1-2 ornek daha ekleyeyim…

1 Beğeni

Altıncısı bir tık şeymiş :smiley: Uyuyacağız mecbur, yarın devam ederim artık.

void*'i dogrudan kullanmak (okumak veya yazmak) mumkun degil, cunku neye isaret ettigini bilmiyoruz.

Neye isaret ettigini baska bir kanaldan biliyorsak (yukarida ben int* oldugunu soyluyorum) cevirip kullanabiliriz. Cevirirken cast’e ihtiyac yok.

2 Beğeni

#6


Casting’lisini yaptım ilk. Şimdilik dursun bir kenarda.

void sayi_topla(int sayi1, int sayi2, void *sonuc)
{
   *(int *)sonuc = sayi1 + sayi2;
}

Aha! Oldu mu yoksa? Hareket bu mudur?

void sayi_topla(int sayi1, int sayi2, void *sonuc)
{
   int total = sayi1 + sayi2;
   memcpy(sonuc, &total, sizeof(int *));
}

Test:

void test_sayi_topla()
{
   int sayi1 = 2;
   int sayi2 = 5;
   int sonuc;

   sayi_topla(sayi1, sayi2, &sonuc);

   printf("Total: %d\n", sonuc);
}
// Total: 7
1 Beğeni

#7

#include <stdio.h>
#include <string.h>

struct bolme_sonucu
{
    int bolum;
    int kalan;
};

void kalanli_bol(int bolunen, int bolen, struct bolme_sonucu *sonuc)
{
    int division = bolunen / bolen;
    int remaining = bolunen % bolen;

    sonuc->bolum = division;
    sonuc->kalan = remaining;
}

void kalanli_bol2(int bolunen, int bolen, void *sonuc)
{
    int division = bolunen / bolen;
    int remaining = bolunen % bolen;
    struct bolme_sonucu result = {division, remaining};

    memcpy(sonuc, &result, sizeof(struct bolme_sonucu));
}

void test_exercise_7()
{
    int bolunen = 18;
    int bolen = 6;
    struct bolme_sonucu sonuc;

    kalanli_bol(bolunen, bolen, &sonuc);

    printf(
        "-- kalanli_bol() --\n"
        "bolum: %d\n"
        "kalan: %d\n\n",
        sonuc.bolum, sonuc.kalan);

    bolunen = 67;
    bolen = 17;
    kalanli_bol2(bolunen, bolen, &sonuc);

    printf(
        "-- kalanli_bol2() --\n"
        "bolum: %d\n"
        "kalan: %d\n",
        sonuc.bolum, sonuc.kalan);
}
/*
-- kalanli_bol() --
bolum: 3
kalan: 0

-- kalanli_bol2() --
bolum: 3
kalan: 16
*/
2 Beğeni

Cok iyi!

kalanli_bol2'de sunu da yapabiliriz:

    struct bolme_sonucu *result = sonuc;
    result->bolum = division;
    result->kalan = remaining;

#8 ekledim.

2 Beğeni

#8

Epeydir aynı kodu yazıp siliyorum galiba. Mantıken çalışması lazım gibi görünüyor oysaki? (mi acaba)

core dumped verdi gcc.

#include <stdio.h>
#include <string.h>

void buyuk_sayi2(int *sayi1, int *sayi2, void *pp_buyuk)
{
   int greatest;

   if (*sayi1 > *sayi2)
   {
      greatest = *sayi1;
   }
   else
   {
      greatest = *sayi2;
   }

   memcpy(pp_buyuk, &greatest, sizeof(int **));
}

void test_exercise_8()
{
   int sayi1 = 27;
   int sayi2 = 36;
   int *buyuk;

   buyuk_sayi2(&sayi1, &sayi2, &buyuk);

   printf("Greatest: %d\n", *buyuk);
}

int main()
{
   test_exercise_8();
   return 0;
}

Ne ters gidiyor ki burada?

int **'in gosterdigi yere (int *) int yaziyoruz, ondan. buyuk'un degeri sayi oluyor. Pointer olarak okumaya calisinca 36 gibi bir adresi okumaya calisiyoruz.

Bu sefer oldu sanırım. Birkaç gündür dönememiştim. Şimdi devam!

#include <stdio.h>
#include <string.h>

void buyuk_sayi2(int *sayi1, int *sayi2, void *pp_buyuk)
{
    int greatest;

    if (*sayi1 > *sayi2)
    {
        greatest = *sayi1;
    }
    else
    {
        greatest = *sayi2;
    }

    int *temp_ptr = &greatest;
    memcpy(pp_buyuk, &temp_ptr, sizeof(int *));
}

void test_exercise_8()
{
    int sayi1 = 35;
    int sayi2 = 63;
    int *buyuk;

    buyuk_sayi2(&sayi1, &sayi2, &buyuk);

    printf("Greatest: %d\n", *buyuk);
}
// Greatest: 63

greatest, temp_ptr (ve haliyle adresleri) fonksiyon bitince gecersiz oluyor. Gecersiz pointer dondurmus oluyoruz yani.
Simdi fark ettim, yukarida da ayni sorun var.

Yani sunlar:

  1. Adresini aldigimiz seylerin omrune dikkat etmeliyiz. Degerlerine omurleri bittikten sonra erismemeliyiz.
  2. Degisik turler arasinda veri aktarimi icin memcpy dogru bir yol olsa da, egzersiz geregi = kullanmak lazim.
  3. void buyuk_sayi2(int *sayi1, int *sayi2, void *pp_buyuk)
    pp_buyuk’un gosterdigi int * degerine (pp_buyuk = int**) sayi1 veya sayi2’nin degeri yazilir diyelim.
void buyuk_sayi(int *sayi1, int *sayi2, int **buyuk)
{
   if (*sayi1 > *sayi2)
   {
      *buyuk = sayi1;
   }
   *buyuk = sayi2;
}

Suradaki cozumu alip cast kullanarak void *buyuk alan hale getirerek baslayalim yani.

Bu arada else koymayi unutmusuz, bunu da simdi gordum. Hmm, test case mi yazsam ezgersizlere, ne yapsam.

2 Beğeni

temp_ptr’ı static olarak tanımlayıp ona program boyunca geçerli bir lifetime vermiş olsaydık bu iyi bir practice olur muydu?

Fonksiyonlar void parametre ile tanımlandığı zaman bu nasıl mümkün olacak?

:no_mouth:

Baştan beri gözüm hep bir test case arıyordu. Hiç de fena olmaz bence.

Bahsettigimiz lifetime sorununu cozerdi, evet, fakat bu sefer de fonksiyonun butun cargilarinda bir tane ortak degisken oldugu icin fonksiyonun kendisiyle eszamanli olarak calismasini engellerdi. (Problem cikartarak.) Mesela iki ayri thread’den cagirmak, veya bir cagrinin sonucunu kullanmadan ikincisini yapmak yanlis sonuc verirdi.

C’de fonksiyonda obje yaratmanin guzel bir yolu yok. Zaten o yuzden cikis degerlerini ice giren pointer’lar yardimiyla veriyoruz.

Ama bu sekilde (program lifetime’li degiskenlerle) calisan fonksiyonlar C standardinda bile mevcut. bkz: strtok, localtime. strerror(3)'e bakarsan alternatif cozumu de gorebilirsin.

Dedigim gibi, void * turunu somut bir ture cevirerek.
Havada cast ile mumkun:

void *foo = ...;
*((int *) foo) = 42;
((int *) foo)[0] = 42;

Gecici degisken kullanarak da mumkun:

int *ifoo = /* (int *) */ foo;
*ifoo = 42;
ifoo[0] = 42;
2 Beğeni

Yaptım.

void buyuk_sayi(int *sayi1, int *sayi2, void *buyuk)
{

   if (*sayi1 > *sayi2)
   {
      *(int **)buyuk = sayi1;
   }
   else
   {
      *(int **)buyuk = sayi2;
   }
}

Test:

void test()
{
   int sayi1 = 5;
   int sayi2 = 1;
   int *buyuk;

   buyuk_sayi(&sayi1, &sayi2, &buyuk);

   printf("Greatest: %d\n", *buyuk);
}
// Greatest: 5
1 Beğeni
void buyuk_sayi2(int *sayi1, int *sayi2, void *pp_buyuk)
{
    int greatest;

    if (*sayi1 > *sayi2)
    {
        greatest = *sayi1;
    }
    else
    {
        greatest = *sayi2;
    }

    int *temp_ptr = &greatest;
    memcpy(pp_buyuk, &temp_ptr, sizeof(int *));
}

Bu fonksiyondaki lifetime problemini nasıl çözebiliriz peki? stdlib’den free() fonksiyonunu include edip buyuk_sayi2’nin sonunda greatest ve temp_ptr’ı temizlemeye çalıştım ama hata veriyor.
greatest ile temp_ptr’ı static olarak tanımlamaktan vazgeçtim. Thread-safe olmayacakları ile ilgili dediklerinizden dolayı. Normalde yetersiz lifetime ile kalan değişkenlerin sıkıntılarını çoğu zaman static ile çözeriz diye biliyordum.
Ne yapabiliriz o zaman buyuk_sayi2 gibi bir fonksiyon için?

Bir de C standardında kullanılan program boyunca geçerli lifetime’lara sahip static değişkenler de üstte bahsettiğiniz gibi fonksiyonun kendisiyle eşzamanlı çalışmasını engelliyor mu?

Oncelikle, buradaki sorun lifetime’i bitmis bir objeye pointer dondurulmesi. Donen pointer kullanildigi an UB bolgesinde oluyoruz. Bu yuzden free fayda etmez, edemez.

free’yi lokal degisken adresiyle cagirmak da yanlis. Sebebini fonksiyon dokumentasyonunu okuyarak ogrenebilirsiniz.

malloc bir cozum, ama zaten onunla ugrasmamak icin kullanicidan hazir allocate edilmis bir int *'in adresini aldik.

Bu durumda sayi1 veya sayi2 degerlerinden birini “dondurecegimiz” icin baska bir deger yaratmaya gerek yok.

Linkledigim dokumentasyonda yaziyor.

1 Beğeni