PDA

Tam Sürümünü Görmek İçin : Fonksiyon prototipleri


quasimodo
28/10/2006, 22:31
Şimdi bir derleyiciye bir fonksiyonun prototipini tanımladığımızda , derleyici hem fonksiyonun geri dönüş değerini kontrol eder hemde fonksiyona giren paramatrelerin türünü ve sayısını kontrol eder. Yani fonksiyon prototipleriyle parametre kontrolü yapılabilir.

#include <iostream>
using namespace std;

double div(double, double);

int main()
{
double a = 9.3 ,b = 3.1;
div(a.b);
//Drleyici bu kodu gördüğünde herhangibir hata vermeyecektir
//Çünkü prototipte tanımlanan parametre sayısıyla
//kullanılan parametre sayısı aynı. Fakar ;
div(a);
//Gibi bir kod kullansaydık derleme zamanında derleyici
//bize hata verecekti.
//Çünkü prototipteki parametre sayısı iki
return 0;
}

double div(double x, double y)
{
return x/y;
}


Ama C++ ta fonksiyonların aşırı yükleme gibi bir özelliği var yani ben eğer div()
fonksiyonunu hem iki parametreli hemede tek parametreli olarak bir fonksiyon olarak yazarsam.

#include <iostream>
using namespace std;

//double div(double,double); gibi bir prototip tanımlarsam derleyici
//hata verecektir
//double div(double) desem derleyici gene hata verecektir.

int main()
{
double a = 9.3, b = 3.1;
div(a,b);
div(a);
//Derleyici bu tur kodları gördüğünde parametre sayısının
//farklılığından dolayı hata verecektir
return 0;
}

double div(double x, double y)
{
return x/y;
}

double div(double x)
{
return x;
}

Bu sorunu nasıl giderebilirim? yani aşırı yükleme olduğunda nasıl bir prototip kullanmalıyım. Her biri için farklı prototip kullanmak çözüm mü?


acehreli
28/10/2006, 22:41
Eger derleyici hata veriyorsa, 'double div(double)'in sonuna noktali virgul koymadigin icin veriyordur :D

Yoksa kodda bir sorun yok; oyle iki tane islev bildirebilir ve tanimlayabilirsin.

Ali

quasimodo
29/10/2006, 17:07
Peki söyle sorsam. Bir foksiyonun prototipini tanımladığımız zaman, derleyiciye foksiyonun geri dönüş değerinin haber verilmesinin yanısıra fonsiyona gönderdiğimiz parametrelerin sayı ve tür bakımından kontrol edilmeside sağlanmıyormu ?????

#include<iostream>
using namespace std;

double div();
/*yani div() fonksiyonunu bu şekilde tanımlamam doğru olurmu?
Bu şekilde tanımlarsam derleyicim hata verirmi?*/
int main()
{
double a,b;
div(a,b);
return 0;
}

double div(double x,double y)
{
return x/y;
}

sinanonur
29/10/2006, 18:41
Sanırım şöyle oluyor

double fff(double, double);
int fff(double);

şeklinde tanımlarsan bir sorun yok

double fff(double, double);
int fff(double, double);

yaparsan derleyici hata verecektir ama haklı tabi ne döndüreceğini nerden bilsin garip...

Yaptığı şey aldığı parametrelere karşılık gelen yazılmış bir işlev olup olmadığını kontrol etmek varsada döndürdüğü değerin koda uygun olup olmadığına bakmak.

Aynı parametreleri alarak farklı şeyler döndüremez. Ama farklı parametreler alarak aynı şeyi döndürebilir yani. Karışık oldu biraz

üstte yazdığım koda hata verecekken

double fff(double, double);
double fff(double, double);

sorun yaratmayacaktır.

Bir de şu var bir işlevi kullanmak için daha yukarda tanımlanmış olması gerekir. Ya da prototipi verilmiş olması gerekir en azından. Başlangıçta bu prototipler de bu sıra karmaşasını ortadan kaldırmak için yazılır.

Yazdığın kodu bu halde yazarsak sorun olmaz:

#include<iostream>
using namespace std;

double div();
double div(double x,double y);
/*yani div() fonksiyonunu bu şekilde tanımlamam doğru olurmu?
Bu şekilde tanımlarsam derleyicim hata verirmi?*/
int main()
{
double a,b;
div(a,b);
return 0;
}

double div(double x,double y)
{
return x/y;
}


tabii "double div();" satırının ne kadar ggerekli olduğu tartışılır hatta sanırım derleyici bi uyarı verebilir.

Umarım yardımcı olur. Ben konunun uzamnı değilim. bir hatam varsa da hocalarım düzeltir artık :)

acehreli
29/10/2006, 22:52
sinanonur, kodu su sekilde yazsak da sorun olmaz:


double div(double x,double y);
double div();


O iki islevin birbirleriyle ilgileri yoktur.

quasimodo, son verdigin ornekte derleyici hata verir ama 'double div();' diye bir islev bildirdigin icin degil, 'div(a,b);' yazdigin noktadaki div'i tanimadigi icin.

Islevlerin kimlikleri (function signature) adlarindan ve aldiklari parametrelerin tUr ve siralarindan olusur. Kimlikleri ayni olmadiklari surece istedigin kadar islev bildirebilir ve tanimlayabilirsin:


double div();
double div(string, int);
double div(int, string);
double div(double, double);
// vs.
double div(string, int); // bir islevi yeniden bildirdim; hata yok...


Ali

sinanonur
30/10/2006, 12:35
evet ben de bunu anlatmaya çalışmıştım :D

ama dikkat edilmesi gereken bir nokta daha

double div (int, string);
double div (double, string);
de muhtemelen hata verir.

çünkü div(0,"alooo"); yolladığımızda hangisi çalıştıracağını bilemez o da haklı bi yerde :)

myavuzselim
30/10/2006, 13:45
@sinanonur, hata vermiyor.

int func(int x) { return 3; }
int func(double d) { return 5; }
double func(float f) { return 7.5; }

#include <iostream>

int main() {
std::cout << func(2) << std::endl; // 3
std::cout << func(2.0) << std::endl; // 5
std::cout << func(2.0f) << std::endl; // 7.5
std::cout << func(float(3)) << std::endl; // 7.5
return 0;
}

Kafama takilan...
bir.cpp:
double func(double d) { return 3.5; }
int main() {
double x = func(1);
return 0;
}

iki.cpp
int func(int i) { return 42; }
... bu ikisinin beraber link edildiklerinde bir soruna yol acip acmayacaklariydi. bir.cpp'de func(int) diye bir fonksiyon tanimlanmadigi icin burada da bir sorun yasanmiyor. Hangi prototipin hangi fonksiyon olduguna olmasa bile, hangi fonksiyon cagriminin (function call) hangi prototipi kullanacagina derleme zamaninda karar veriliyor demek ki, yoksa zaten programcinin kafasi karisirdi.

acehreli
30/10/2006, 22:09
sinanonur'un bahsettigi karisikligi gosteren bir program soyle olabilir:


#include <string>

using std::string;

double div (int, string)
{
return 0.0;
}

double div (double, string)
{
return 0.0;
}


int main()
{
long l = 0L;
div(l, "merhaba");
}


Ama buradaki hata iki div tanimlandigi icin degil, div'in cagrildigi noktada iki aday div bulundugu icindir. Derleyici hangisini cagiracagina kendisi karar veremez.

Ali

writeLine
06/11/2006, 11:30
div fonksiyonunun dönüş değerlerinin farklı verilmesi gerekmiyor mu hata vermemesi için?

derleyici hem parametre sayısı ve tipleri ile dönüş değerlerini kontrol ediyor diye hatırlıyorum

nilsonmandela
06/11/2006, 13:58
@writeline
Hayır gerekmiyor.Aşağıda açıkladığım konular nedeniyle overload konusunda fonksiyonun geri dönüş değeri belirleyici değildir.

Fonksiyonlarda overload konusunu daha iyi anlamak için derleyicinin arkada neler yaptığını bilmek yararlı olur diye düşünüyorum.Böylece burada gözlemlediğim kafa karışıklıkları da ortaya çıkmamış olur.Ozaman başlayalım:),

C++ da bir fonksiyon tanımladık ve programımızı derledik diyelim.Fonksiyonum int fonk(int prmtr1 int prmtr2) gibi birşey olsun.Uygulamamızı derlediğimizde yazdığımız bu fonksiyon hedef koda parametre sayısı ve parametrelerin türleri ile geçirilir.Bu olay derleyiciden derleyiciye farklı olabilceği gibi örnek bi gösterim aşağıdaki gibi olabilir.

_fonk@i@i

Buradan fonk isimli fonsiyonumuzun iki parametresi olduğunu ve bunlarında int türünde olduğunu anlayabiliyoruz.Eğer int fonk(int prmtr1) gibi bir başka fonksiyon tanımlarsak yukarıda açıkladığım şeye uygun olarak bu derleyici tarafından farklı bir fonksiyon olarak anlaşılacaktır.Böylece ilk fonksiyonumuz yani 2 parametreli fonksiyon veya iki parametresi olan fonksiyonumuz çağrıldığında derleyicinin kafası karışmayacak.Gördüğünüz gibi yukarıdaki bildirimlerde fonksiyonumuzun geri dönüş değeri bulunmuyor bu yüzden aynı isimde olan ve parametre sayısı ve türü aynı olan iki fonsiyonun geri dönüş değerleri farklı olsada aşırı yükleme(overload) yapılamaz.Böyle bir durumda derleyici hangi fonksiyonu kastettiğimizi anlayamayacağı için hata verecektir.

int fonk(int a int b) {return a+b}
void fonk(int a int b) {cout<<a+b} //HATA!!!

Peki C++ da yapabildiğimiz overload olayını neden C de yapamıyorduk.Tahmin etmek zor değil.Yapamıyorduk çünkü C de yazdığımız fonksiyonun hedef koda geçirilme şekli farklı.Fonksiyonumuz C de hedef koda sadece adıyla geçiriliyordu.Örneğin biz int fonk(int a int b) gibi bir fonksiyon yazdığımızda bu dereleyici tarafından _fonk şeklinde algılanacaktır.Gördüğünüz gibi parametre bilgileri yer almıyor bu yüzden iki aynı isimli fonksiyon yazdığımızda bu fonksiyonların parametre bilgileri farklı olsa bile derleyicinin kafası karışacaktır.

Yazdığımız fonksiyonların derleyici tarafından nasıl algılandığını bilirsek sanırım overload konusunda daha az zorlanacağız.

acehreli
07/11/2006, 20:51
nilsonmandela, analizin yararli ve dogru ama soruyu yanitlamiyor.

DOnUs tUrUnUn asiri yuklemede kullanilmiyor olmasi, derleyicinin baglayiciya verirken sectigi islev adiyla ilgili degildir. Tersine, C++ standardi dOnUs tUrUnUn asiri yuklemede kullanilmadigini belirledigi icin derleyici isini senin gozlemledigin gibi yapar.

Yani ornegin, eger dOnUs tUrU hangi islevin cagrildigini belirleseydi, o zaman derleyici _fonk_@i_@i@i gibi bir ad secebilirdi. Ama islevin cagrildigi her yerde bu adi olusturamayabilirdi.:


int fonk();
long fonk();

fonk(); // <-- hangisi?


Donus tUrUnUn islev yuklemede kullanilmamasinin nedeni, C++'in dOnUs turunun kullanilmasini sart kosmamasidir.

Ali

Not: Stroustrup'un "The Design and Evolution of C++" kitabina baktim ama bu konuyla ilgili bir sey bulamadim.

nilsonmandela
08/11/2006, 19:28
acehreli, bu konuda sizinle ayrılıyoruz:) Programlamayla iligili başucu kaynaklarımdan okuduğum bilgiler nedeniyle işlev yüklemesi konusunda, program koşturulurken bu yolun izlendiğini düşünüyorum.Aslında bence aşırı yüklemeyi doğru anlama açısından önemli olan derleyicinin fonksiyonu ne şekilde algıladığı.

myavuzselim
08/11/2006, 23:10
Sanirim acehreli'nin ne demek istedigini zor da olsa anladim. Baska bir programlama dili donus degerini de overload isleminde kullanabilirdi. Mesela C+++ diye bir dil olsun, her fonksiyonun donus degeri kullaniliyor olsun.

string selam() {
return string("string: merhaba");
}
char* selam() {
return "char*: merhaba";
}
int main() {
char* ch;
ch = selam(); // "char*: merhaba"
string str = selam(); // "string: merhaba"
}

Ama return degerinin overlaod'a dahil edilmesi parametrelerin overload'a dahil edilmesinde kucuk bir problem yaratiyor:

void yaz(string s) { cout << s << endl; }
void yaz(char* ch) { cout << ch << endl; }
yaz(selam()); // hangisi?
cout << selam() << endl; // hangisi?

Ama isteyen bunun da bir caresine bakar. Mesela der ki eger birden fazla uyan selam fonksiyonu varsa ilk tanimlanan kullanilir.

Simdi acehreli diyecek ki main'den return etmeyi unutmussun. Ama yok, ben C+++'da o zorunlulugu kaldirdim :)

acehreli
09/11/2006, 02:24
myavuselim, evet oyle soylemek istedim.

Hangi islevin nasil secileceginin karari cok zor bir sey tabii. Ornegin senin aklina gelen "ilk tanimlanan", proje ilerledikce kotu sonuclar dogurabilir. Her sey guzel guzel calisirken bir programci kodu duzenlemek icin islevlerin sirasini degistirir; derleyici de sessizce yeni islevi cagirmaya baslar. :(

Bu arada, C+++'i bilmiyorum ama C++'ta main'den return etmeye gerek yoktur zaten. :) return yoksa, derleyici "return 0;" varmis gibi derlemek zorundadir.

Ali

myavuzselim
09/11/2006, 03:20
myavuselim, evet oyle soylemek istedim.

Hangi islevin nasil secileceginin karari cok zor bir sey tabii. Ornegin senin aklina gelen "ilk tanimlanan", proje ilerledikce kotu sonuclar dogurabilir. Her sey guzel guzel calisirken bir programci kodu duzenlemek icin islevlerin sirasini degistirir; derleyici de sessizce yeni islevi cagirmaya baslar. :(
Cok kotu bir secim oldugunun farkindayim. Sadece return degerinin de overloading mekanizmasina (her ne kadar guzel bir sekilde olmasa da) dahil edilebilecegini gostermekti niyetim.

Bu arada, C+++'i bilmiyorum ama C++'ta main'den return etmeye gerek yoktur zaten. :) return yoksa, derleyici "return 0;" varmis gibi derlemek zorundadir.

Tesekkurler. Zaten su durumlari hep birbiriyle karistiriyorum: int main()'den return etmemek, void main() kullanmak, veya direkt main() kullanmak. Vazgectim, C+++'da bunlarin hepsi hatali olacak, boylece ben de karistirmayacagim :)