stdarg.h ile Argüman Sayısı Belirsiz Olan Fonksiyonlar Yazmak

Açık Kaynak, Programlama, tutorial, ytulinux

Evet sayın seyirciler gün geçmiyor ki yazılımcıların iğrenç zevklerine bir başkası eklenmesin.

Bugünki (bugünkü, bugünküüü hmm evet) konumuz argüman sayısı belirsiz olan fonksiyonlar yazmak.

Hangimiz printf i görüp de, bu nasıl yazılıyor acaba dememiştir ki? Evet bugün çocukluk hayallerimiz gerçekleşiyor ve argüman sayısı kullanıma göre değişebilen mini mini fonksiyonlar yazıyoruz.

Öncelikle konu ile ilgili bir takım talihsiz açıklamalar yapayım.

Efenim öncelikle, işin bellek yerleşimine kadar uzanan bir takım hesaplamalarda bulunacağız ki ne yaptığımızın farkında olalım.

Hepinizin bildiği gibi, fonksiyonlara ait argümanlar, stack yapısının içerisinde tutulur. Stack yapısını şöyle anlatabiliriz; diyelim ki elimizde n tane sayı var. Eğer bu sayıları diziye atmamız gerekirse ne yapıyorduk? İlk gözden itibaren bu sayıları yazmaya başlıyorduk değil mi? Şimdi özel bir dizi düşünün, bu diziye ait bir de adres tutucu değişken var. N+1 tane gözü olan bu dizi tamamıyla boş olması durumunda, bu adres tutucu değişken yani int p nin değeri N olsun (c de diziler 0 dan başladığı için).
Şimdi, sayılarımızı tek tek atmak istiyoruz, ama dizinin her hangi bir elemanına direk ulaşmak gibi bir derdimiz olmasın. Sadece diziye son olarak ne yazdıysak onu almak istiyoruz. Nasıl birşey yapabiliriz?
Diziye değer at denildiği zaman dizi[p] ye bu değer atılsın, p bir eksilsin. Örneğin arka arkaya üç tane sayı atıyoruz. Dizinin durumu
dizi[N+1] –> …………………………..1 2 3
p–> n-3
olur.
İki fonksiyon tanımlayalım bu işleri yapan. Biri push, biri pop olsun. Push diziye değer atsın, pop diziden değer çeksin.
Bu durumda yukarıdaki işlemi push(3);push(2);push(1); şeklinde yaparız. Peki degisken = pop(); dediğimiz zaman hangi sayı gelecek? Son attığımız sayı 1 olduğuna göre 1 dönecek. Stack yapısı işte budur.
Şimdi fonksiyonlara geri dönelim.
Biraz derinlere doğru, assembler seviyesine inerek, bir fonksiyon çağrılırken neler yapıldığını hatırlayalım. Fonksiyona gidiş, bir geri dönüşü de gerektirir. Bu durumda programın hangi adreste kaldığını saklamamız gerekir. Bir de fonksiyonu hangi argümanlarla çağırdıysak, bunları da saklamamız gerekir. Bu durumda, bir fonk(3,2,1); komutu çalıştığında, yani fonk fonksiyonu 1 ve 2 değerleri ile çağırıldığı zaman, şu işlemler yapılıyor demektir:

//aslında burada call çalışıyor, call otomatik olarak bu adresi push ediyor ve fonksiyona ait adrese atlıyor yani ayrıca push komutu çalışmıyor ama anlaşılması açısından böyle yazdım. Bir de segmentlerarası olayı falan da var da fazla kurcalamayalım böyle iyi :)
push(kalinan_adres);
// c de önce son argüman push edilir
push(1);
push(2);
push(3);

Buraya kadar eğer anladıysak, şimdi işin programlama kısmına geçebiliriz.
Diyelim ki gelen tüm sayıları ekrana yazdıran bir program yazacağız. Yukarıda görüldüğü gibi, kaç argümanın fonksiyona verildiği ile ilgili bir bilgi tutulmadığına göre, fonksiyonumuzun ilk elemanı kaç argüman atıldığını tutmalı. Örneğin 1,2,3 sayılarını yazdıracaksak yazdir(3,1,2,3); şeklinde tasarlamamız gerekiyor.
Programımızın main kısmı şöyle olsun:
#include <stdio.h>
#include <stdarg.h>

int main ()
{
yazdir(3,1,2,3);
return 0;
}

Şimdi geldi yazdir fonksiyonuna, bunu iki şekilde yapabiliriz. Ya hazır fonksiyonlar kullanırız, ya da kendi işimizi kendimiz yapalım. Öncelikle kendimiz nasıl yapabiliriz onu görelim:

void yazdir (int i, …)
{
int *p;
// i nin adresini başlangıç adresi olarak alıyoruz.

p=&i;
for (;i>0;i–)

{
// i yi değil, diğer argümanları yazdırmak istiyoruz. İ ye göre diğerlerinin daha büyük adreslerde tutulduğunu düşünürsek (en son push edilen ilk elemandı) p yi arttırmamız gerektiğini farkederiz. Yalnız şunu da unutmamak lazım, p yi arttırırken aslında p 4 artıyor, çünkü int işaretçisi ve sizeof(int) = 4
p++;
//sonra da yazdırıyoruz
printf(“%d”,*(p));
}
}

Evet bu kadar. Tabi bu işi kütüphaneyle yapmak her zaman daha iyidir. Yoksa farklı bir yerde çalıştırmaya kalktığınız zaman sorunla karşılaşabilirsiniz. Kütüphaneyle de şu şekilde yazabiliriz.
void yazdir (int i, …)
{
// va_list tipinde bir değişken tanımlıyoruz, va_list stdargs içinde tanımlı bir struct. Birazdan işimize yarayacak.
va_list args;
// diyoruz ki, argümanlarımız &i adresinden yani ilk argümanın adresinden başlıyor.
va_start(args,&i);
for (;i>0;i–)
//  va_arg fonksiyonunun ikinci parametresine bakarsanız, int tipinde değişken çektiğimizi belirtiyoruz.
{printf(“%d”, va_arg(args,int));}
va_end(args);
}

Bu arada fonksiyonları mainin üstünde tanımlamayı unutmayın, veya aşağıda tanımlarsanız main den önce prototip kullanmayı.
Kolay gelsin…

Bu yazı toplamda 2078, bugün ise 0 kez görüntülenmiş

No Comments