Geri Dönüş adresi pointer Türünden Olan Fonksiyonlar

Başlatan armsistem, 22 Temmuz 2012, 15:41:14

armsistem

Arkadaşlar merhaba , tüm c öğrenenlerin dediği en zor öğrenilen konu göstericiler. Yaklaşık 1 haftadır webten pointers konusunu araştırıyorum. Aklıma takılan ve çözemediğim bir konu var 'Geri Dönüş adresi pointer Türünden Olan Fonksiyonlar'.

- İki değişkenin değerlerini yer değiştirebilmek için pointers kullanmaya mecburum.
void swap(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}


Aşağıda iki fonksiyon arasındaki fark nedir , özellikle olmazsa olmaz şeklinde nerelerde kullanılır.
int *topla (int *x,int *y){return *x+*y;}


int topla (int *x,int *y){return *x+*y;}






atioky_216

#1
Detaylı olarak bilmemekle birlikte Kaan Arslanın "Adan Zye C" kikitabından şunu hatırlıyorum.
Eprom kısmına yazılan fonksiyonları kullanabilmek için (reset vs...)  yararlanılabildiğinden bahsediyordu tabi kullanımı kullanıcıya kalmış.

Bunları kullanabilmek için belirli tür dönüşümlerinden yararlanılıyormuş (pek anlayamadım henüz buraları)

mesela windows sistemler için söyle diyordu, "sistemi reset atması için yazılan program 0xFFFF0000 başlangıç adresinden itibaren  bulunur" bunu,

#include <stdio.h>
int main(){
void  (*reset) (void) =  ( void (*) (void))  0xFFFF0000;  // bilinçli tür dönüşümü ile bir fonksiyonun başlangıç adresini tutuyor
reset(); // şeklinde reset attırılıyor.
return 0;
}


Erdem

Alıntı yapılan: armsistem - 22 Temmuz 2012, 15:41:14

Aşağıda iki fonksiyon arasındaki fark nedir , özellikle olmazsa olmaz şeklinde nerelerde kullanılır.
int *topla (int *x,int *y){return *x+*y;}


int topla (int *x,int *y){return *x+*y;}


İki işlevin arasındaki tek fark ilk işlevin bir int * yani bir tamsayı için gösterge, ikincisinin de bir tamsayı döndürmesidir.

void yerDegistir(int* ilk, int* ikinci)
{
    int gecici = *ilk;
    *ilk = *ikinci;
    *ikinci = gecici;
}

İlk işleve geri dönüp neden işleve parametre olarak iki tane int* türünde gösterge gönderdiğimizi düşünelim. Bunun nedeni şu. Eğer parametre olarak iki tane int gönderdiğimiz durumda işlev içinde bu değişkenlerin yerel bir kopyası oluşturulur. Tüm değişiklikler de bu kopya üzerinde gerçekleşir.

yerDegistir işlevinin kapsamından çıkıldığında ilk ve ikinci değişkenleri için ayrılan bellek alanı geri verilir, değişkenler sonlandırılır. Yaptığımız değişikliğin kalıcı olabilmesi,  ve değişikliklerin yerel kopyalar yerine değişkenlerin kendisinde olabilmesi için değişkenlerin bellek adreslerini gönderiyoruz.

C++'da ya D'de göstergeler yerine referans & kullanılabilir. Referans basitçe bir değişken için takma isimdir. Hatta C++'da gerekmedikçe göstergeleri kullanmayın gibi yararlı öğütler vardır.

Arkadaşın bahsettiği ise daha farklı bir şey. İşlev göstergesi. İşlev göstergeleri D'de de bulunan bir özellik:

http://ddili.org/ders/d/kapamalar.html

Şu şekilde basit bir fonksiyonumuz olsun.

int topla(int ilk, int ikinci)
{
    return ilk+ikinci;
}

Parametre olarak iki tane int alan ve bir int döndüren bir işlevi gösteren bir gösterge tanımlayalım.

int (*islevGostergesi)(int,int);

Göstergemizin işlevimizi göstermesini istiyoruz.

islevGostergesi = &(topla);

Artık bir işlev göstergemiz olduğuna göre onu kullanabiliriz.

int toplam= (*islevGostergesi)(2, 3); // toplam == 5

Tagli

armsistem, yukarıda verdiğin örnekte ilk kod muhtemelen derlenmeyecektir. Çünkü işlem sonucu bir int ve sen bunu int* (int pointer'ı) olarak döndüremezsin. Zorlayıp casting yaparsan o başka tabi ama bir anlamı yok.

Ayrıca, fonksiyondan pointer döndürmenin çok ciddi bir sakıncası var: Pointer'ını döndürdüğün değişken fonksiyon içinde tanımlanmışsa, fonksiyonun sonlanmasıyla birlik te o değişken de öleceğinden, pointer gerçekte olmayan bir değeri gösterecektir. O pointer'dan da bir hayır gelmez, kullandığın yerde programı patlatır sadece.

Peki pointer döndürmek nerede işe yarar? Fonksiyon içinde malloc gibi bir fonksiyon ile dinamik hafıza almışsan, bu alanı gösteren bir pointer'ı döndürebilir ve dışarıda kullanabilirsin. Fonksiyonun sona ermesi, bu şekilde dinamik olarak alınmış bir hafızanın yok olmasına sebep olmaz. Dinamik olarak alınmış hafıza ile uğraşırken memory leak (hafıza sızıntısı?) olayına karşı tetikte olmalısın.

Erdem, fonksiyon pointer'ı C ve C++'da var. Syntax biraz daha farklı diye hatırlıyorum, veya birden fazla şekli olabilir. Yazım şekli pointer döndüren fonksiyonlara benzediği için kafa karıştırıcı olabiliyor. Örneğin:
int *birseyYap(int n) fonksiyonu int pointer'ı döndüren bir fonksiyon iken;
int (*birseyYap)(int n) ise "int fonk(int)" gibi bir fonksiyonun pointer'ıdır.

Bu arada, "islevGostergesi = &(topla);" şeklinde bir yazıma gerek yok. Bilmiyorum belki eski C derleyicilerinde bu şart olabilir ama normalde şu şekilde yazılması yeterli:
islevGostergesi = topla;
Çağrılırken de doğrudan şu şekilde çağrılabilir:
int toplam= islevGostergesi(2, 3);

Kaynaklar:
http://www.newty.de/fpt/fpt.html
http://www.eskimo.com/~scs/cclass/int/sx10a.html

C++ syntax'ı biraz daha farklı olabilir. C++'ı yeni öğreniyorum ve bu dilde henüz deneme yapmadım. Ama C'de denedim, bahsettiğim gibi kullanılıyor.

Gökçe Tağlıoğlu

Erdem

Alıntı yapılan: Tagli - 23 Temmuz 2012, 00:08:51
armsistem, yukarıda verdiğin örnekte ilk kod muhtemelen derlenmeyecektir. Çünkü işlem sonucu bir int ve sen bunu int* (int pointer'ı) olarak döndüremezsin. Zorlayıp casting yaparsan o başka tabi ama bir anlamı yok.

Tabi C bilmeyen birisi olarak sallamak istemiyorum. Ama bu çalışıyor.

#include<stdio.h>
int *foo();

int main()
{
    int * birNokta;
    birNokta = foo();
    printf("%d", *birNokta);
    return 0;

}

int *foo()
{
    int *nokta = malloc(sizeof *nokta);
    *nokta = 12; 
    return nokta;
}

Ama bahsettiğim gibi C bilmiyorum sadece kulak misafiri olmuşluğum var  :D Belki bu şekilde kullanımda bellek sızıntısı oluyordur.

Alıntı yapılan: Tagli - 23 Temmuz 2012, 00:08:51
Erdem, fonksiyon pointer'ı C ve C++'da var.

Yukardaki örnek C içindi zaten. C++ için ben şimdiye kadar hiç işlev göstergesi kullandığımı hatırlamıyorum. Belki Standart Şablon Kütüphanesinin gerçeklemesinde kullanılıyordur. İşlev göstergeleri D'ye de C'den geçmiş.

Alıntı yapılan: Tagli - 23 Temmuz 2012, 00:08:51
Bu arada, "islevGostergesi = &(topla);" şeklinde bir yazıma gerek yok. Bilmiyorum belki eski C derleyicilerinde bu şart olabilir ama normalde şu şekilde yazılması yeterli:
islevGostergesi = topla;
Çağrılırken de doğrudan şu şekilde çağrılabilir:
int toplam= islevGostergesi(2, 3);

Bu kullanımı bilmiyordum. Bilgilendirme için teşekkürler..

mufitsozen

#5
calismaz denilen ornek:
Alıntı Yapint *topla (int *x,int *y){return *x+*y;}

int * deger icin int geri gonderiliyor

Alıntı Yapint *foo()
{
    int *nokta = malloc(sizeof *nokta);
    *nokta = 12;
    return nokta;
}
sizin foo ise farkli., int * icin int * geri gonderiyor.

ayrica c++ biliyorum deyipte c bilmeyen birisinede ilk defa rastliyorum! Enteresan. Insan yasadikca neler ogrenebildigini gorup sasiriyor.

Tasarimcisida dahil olmak uzere bircoklari icin  c++ c'nin ust kumesi sayilir, adi da oradan gelir. C ile yapilabilen hersey c++ ilede yapilir. En erken c++ derleyicileri, cfront diye c++ programlarini c'ye ceviren bir preprocessor ile calisirdi.

C++dan cye cevrilen programlar c compiler ile derlenirdi.

name mangling gibi c++ programcilarini sasirtan "icatlar" bu zamanlardan kalmadir.
Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.

Erdem

Alıntı yapılan: mufitsozen - 23 Temmuz 2012, 02:05:08
Tasarimcisida dahil olmak uzere bircoklari icin  c++ c'nin ust kumesi sayilir, adi da oradan gelir.

C++ dilinin geliştiricisi Stroustroup burada 'matematiksel kesinlikle konuşmak gerekirse C C++'ın bir alt kümesi değildir' diyor.

Devamını da burada okuyabilirsiniz.

http://www2.research.att.com/~bs/bs_faq.html#C-is-subset

Alıntı yapılan: mufitsozen - 23 Temmuz 2012, 02:05:08
C ile yapilabilen hersey c++ ilede yapilir. En erken c++ derleyicileri, cfront diye c++ programlarini c'ye ceviren bir preprocessor ile calisirdi.

C++dan cye cevrilen programlar c compiler ile derlenirdi.


Evet derleyici konusuna katılıyorum. Ama tersi doğru değil. Amacım burada bir dil tartışması oluşturmak değil ama C, C++'a göre olanakları sınırlı küçük bir dildir.

Belki 10 sene öncesine kadar, Kernighan ve Ritchie amca zamanından kalma bond çantaları ile dolaşan C programcılarını hala bulabilirdiniz. O zamanlarda C++ öğrenmek istiyorum önce C mi öğrenmeliyim gibi şeyler soruluyordu. Ama C++'nin öğretilmesi hakkındaki modern görüş C++ öğrenirken C bilmenin bir avantaj yerine dezavantaj getirdiğini, C++'nin C++ gibi öğretilmesi gerektiğini savunur.

Örneğin işte bir C++ programı:

#include <iostream>
#include <map>
#include <string>
#include <iterator>

using namespace std;

typedef map <string, unsigned int> Kelimeler;
typedef pair <string, unsigned int> Sayac;

class KelimeArttirici : public iterator <output_iterator_tag,
                                         void, void, void, void>
{
public:

    explicit KelimeArttirici (Kelimeler & kelimeler)
        : kelimeler_ (kelimeler)
    {}

    KelimeArttirici & operator= (const string & kelime)
    {
        ++kelimeler_ [kelime];
        return *this;
    }

    KelimeArttirici & operator* ()     { return *this; }
    KelimeArttirici & operator++ ()    { return *this; }
    KelimeArttirici & operator++ (int) { return *this; }
    
private:
    Kelimeler & kelimeler_;
};

namespace std
{
    ostream & operator<< (ostream & cikis, const Sayac & sayac)
    {
        return cikis << sayac.first << '\t' << sayac.second;
    }
}

int main ()
{
    Kelimeler kelimeler;
    copy (istream_iterator<string> (cin),
          istream_iterator<string> (),
          KelimeArttirici (kelimeler));
    
    copy (kelimeler.begin (), kelimeler.end (),
          ostream_iterator<Sayac> (cout, "\n"));
    return 0;
    
}


C öğrenip ardından C++ öğrenmeye başladığınız zaman, C++'de çok yaygın olarak kullanılmayan göstergeler, makrolar hatta diziler gibi olanakları, daha bir sürü öğrenip unutmanız gereken şeyi öğrenirsiniz. Ve işin kötü tarafı artık C stili kodlamaya başlarsınız.

Şahsen ben C++'yi C++ gibi öğreten kaynaklardan öğrendim. Ve bu şekilde öğrendiğime de memnunum  :D

mesaj birleştirme:: 23 Temmuz 2012, 04:58:49

Alıntı yapılan: mufitsozen - 23 Temmuz 2012, 02:05:08
name mangling gibi c++ programcilarini sasirtan "icatlar" bu zamanlardan kalmadir.

C++ programcılarının derleyicinin yaptığı isim süsleme gibi -- icatlara -- şaştığını düşünmüyorum. Siz şaşırıyormusunuz  ;)

Linux'ta bir program parçasının içindeki isimler şu komutla görülebilir.

$ nm deneme.o
0000000000000000 t
0000000000000000 D _D6deneme12__ModuleInfoZ
0000000000000000 T _D6deneme5toplaFiiZi
                 U _Dmodule_ref


Daha önce de derleyicinin yaptığı süslemeler konuşulmuştu. Eğer o konudan bahsediyorsak benim orada karşı çıktığım nokta insan derleyicilere ihtiyacımız olmayışıydı. Bunu da okuduğum bir kitaptan alıntı yaparak söylemiştim.

Nobody can afford human compilers any more.

Gerçekten de derleyici bu işi gerçekleştirmek için süslemeler yapıyormuş, ya da program yığıtına belirli bir sırada yerleştirmek için çağrılar yapıyormuş. Bu arkaplanda derleyicinin yaptığı işler programcıyı ilgilendirir mi?

Bence de insan derleyicilere ihtiyacımız yok.

Tagli

Alıntı yapılan: Erdem  - 23 Temmuz 2012, 01:10:24
Tabi C bilmeyen birisi olarak sallamak istemiyorum. Ama bu çalışıyor.

#include<stdio.h>
int *foo();

int main()
{
    int * birNokta;
    birNokta = foo();
    printf("%d", *birNokta);
    return 0;

}

int *foo()
{
    int *nokta = malloc(sizeof *nokta);
    *nokta = 12; 
    return nokta;
}

Ama bahsettiğim gibi C bilmiyorum sadece kulak misafiri olmuşluğum var  :D Belki bu şekilde kullanımda bellek sızıntısı oluyordur.
Bu kodun sorunsuz çalışmasının sebebi, yukarıda bahsettiğim gibi, fonksiyon içinde alanın malloc ile alınması. Burada sorun yok. Ama main içinde örneğin ikinci bir kez birNokta = foo(); dersen hafıza sızdırırsın. Buna dikkat etmek gerekir.

Ayrıca, int *nokta = malloc(sizeof *nokta); ifadesi her ne kadar çalışıyor gibi gözükse de aslında hatalı (eğer bilmediğim bir syntax değilse tabi). Doğrusu int *nokta = malloc(sizeof(int)); olmalı. (Ufak bir ayrıntı: Eski C derleyicilerinde malloc dönüşü void* olduğundan int *nokta = (int*)malloc(sizeof(int)); şeklinde bir kullanım gerekiyormuş. Ben C öğrenirken bu şekilde öğrenmiştim ve bu şekilde kullanıyordum aslında. Yeni derleyicilerde bu casting'e ihtiyaç olmadığını yeni öğrendim.) Görülen o ki, yazım şekli değişken ismini de kabul ediyor. Ama buradaki sorun, int pointer'ı kadar değil int'in kendisi kadar yer açılması gerekiyor oluşu. Yukarıdaki kodun sorunsuz çalışması, pek çok 32 bit sistemde genelde hem int* hem de int türünün 4 byte kaplamasından kaynaklanıyor. Ama C'de kod yazarken varsayımlara güvenmemek lazım.

Alıntı yapılan: Erdem  - 23 Temmuz 2012, 01:10:24
Yukardaki örnek C içindi zaten. C++ için ben şimdiye kadar hiç işlev göstergesi kullandığımı hatırlamıyorum. Belki Standart Şablon Kütüphanesinin gerçeklemesinde kullanılıyordur. İşlev göstergeleri D'ye de C'den geçmiş.
Gerçekten de pek sık kullanılmıyorlar. Ben ilk kez pthreads kullanımı sırasında karşılaşmıştım, onun dışında da sadece bir kez kullanmam gerekti (yakın zamanda, o yüzden bu konudaki bilgim taze sayılır). Kaynaklarda yazdığına göre, state machine mantığına dayanan algoritmalarda işe yarıyormuş ama olayı pek aklımda canlandıramadım.

Gökçe Tağlıoğlu

Erdem

Alıntı yapılan: gerbay - 23 Temmuz 2012, 09:16:49

Erdem hocam bu şekilde bir yaklaşım ile sadece başkalarının yazdığı kütüphaneleri kullanırsınız. Bir çok konuda da eliniz kolunuz bağlı kalır. C++ biliyorsunuz, hiç ".*" ve "->*" operatörlerini kullandınız mı, C++ da operator overloading yaptınız mı?
Ne dememi bekliyorsunuz. Daha hiç kullanmadım ilk kez mi kullanacağım demeliyim. Türkiye'de tek C++ bilenin kendiniz mi olduğunu düşünüyorsunuz.

Alıntı yapılan: gerbay - 23 Temmuz 2012, 09:16:49

diğer taraftan "name mangling" gibi teknik bir konuyu "isim süsleme" şeklinde türkçeleştirip algıyı daralttıktan sonra tabiki bir işinize yaramaz, bilmeniz de gerekmez..

"Operator overloading" mekanizmasının arkasındaki gücün "name mangling" olduğunuda hiç bir zaman anlayamazsınız.

Merak ettim. Burada anlatın bakalım 'name mangling' in arkasındaki -- gücü -- 'operator overloading' i.

Alıntı yapılan: gerbay - 23 Temmuz 2012, 09:16:49
Eğer arka planda derleyicinin yaptığı işleri tahmin edebiliyorsanız çok daha iyi, çok güçlü kodlar içeren programlar yazabilirsiniz. yığına parametrelerin nasıl yerleştirildiğini (fastcall tipi çağrılarda yığın kullanılmaz ama bu detaya sizin ihtiyacınız olmayabilir) ve yığını kimin boşalttığını bilmezseniz "printf" gibi sonsuz sayıda parametre alabilecek bir fonksiyonun nasıl yazılabildiğini anlayamazsınız. Birisi yazmış, size sadece kullanmak düşer.. 

Hiç katılmıyorum. Hatta tamanını alıntılayayım da daha iyi anlaşılsın:

Soapbox

The art of programming is in a very peculiar situation. It is developing so fast, that people who started programming when C was in its infancy are still very active in the field. In other sciences a lot of progress was made through natural attrition. The computer revolution happened well within one generation. Granted, a lot of programmers made enough money to be able to afford doing volunteer work for the rest of their lives. Still, many others are carrying around their old (although only a few years old) bag of tricks that they learned when they were programming XT's with 64k memory.

New programmers learn programming from the classics like Kernighan and Ritchie's "The C programming language." It's a great book, don't get me wrong, but it teaches the programming style of the times long gone.

The highest authority in algorithms and data structures is Donald Knuth's great classic "The Art of Programming." It's a beautiful and very thorough series of scientific books. However, I've seen C implementations of quicksort that were based on the algorithms from these books. They were pre-structured-programming monstrosities.

If your compiler is unable to optimize the human readable, maintainable version of the algorithm, and you have to double as a human compiler-- buy a new compiler! Nobody can afford human compilers any more. So, have mercy on yourself and your fellow programmers who will have to look at your code.


Bence de hala bilgisayarların 64K belleğe sabit olduğu zamanlarda öğrendikleri hileleri çantalarında taşıyan, okunabilirliği düşük felaket kodlar yazan programcılar varsa sektörü terk etsinler. Hiç bir işverenin artık insan bir derleyiciye ödeyecek parası yok!


Alıntı yapılan: gerbay - 23 Temmuz 2012, 09:16:49
Eğer C yi ve C++ template lerini iyi biliyorsanız STL e de mahkum kalmazsınız..
Bakın vatandaş üniversite öğrencisi ama ne yazmış;  https://idlebox.net/2007/stx-btree/

adam yetinmemiş ve STL container larından bazılarına replacement yazmış..

Yazmış olabilir de C ile ne alakası var. Artık C++ kitaplarının başında bulunan merhaba dünya programı bile C++ tarzı yazılıyor.

#include <iostream>

class Dunya
{
  public:
    Dunya (int no)
        : numara_ (no)
    {
        std::cout << numara_ << " numaralı Dünyadan merhaba\n";
    }
    ~Dunya ()
    {
        std::cout << numara_ << " numaralı Dünyadan güle güle!\n";
    }

  private:
    const int numara_;
};

Dunya butunDunya (1);

int main ()
{
    Dunya benimDunyam (2);
    for (int i = 3; i < 6; ++i)
        Dunya birDunya (i);
    Dunya birTaneDahaDunya (6);
}


Burada bir C programcısının aşina olduğu ne var merak ediyorum doğrusu.

İkincisi Standart Şablon Kütüphanesi'nin hangi özelliği ihtiyaçlarınızı karşılamadı da böyle bir kütüphane kullanma ihtiyacı hissettiniz.

Ona bakarsanız Andrei Alexandrescu'nun Facebook için yazdığı ve std::string'in eşdeğeri gibi çalışan bir dizgi türü var.

https://github.com/facebook/folly/blob/master/folly/FBString.h

Üçüncüsü eğer zahmet eder de bağlantısını verdiğim o kitaplardan bazılarını alır okursanız. Örneğin Accelerated C++ gibi kitaplar ilkönce Standart Kütüphane'de bulunan toplulukları tanıtır, sonlara  doğru şablonlar kullanarak kullanıcının kendi topluluklarını nasıl yazacağını da anlatır.

Alıntı yapılan: gerbay - 23 Temmuz 2012, 09:16:49
başkalarından alıntılar sizin kararlarınızı ve bakış açınızı etkilemesin, kendiniz öğrenip kendi fikrinizi oluşturun.. Adamın biri çıkmış Nobody can afford human compilers any more.  demiş, kendi fikri ve felsefi boyutu da olan bir tartışmaya çekilebilecek bir söz..

Bu bütün dünyada böyledir. Birisi gider parlak bir fikir ortaya koyar. Diğerleri de o fikri destekler.

Aslında benim düşüncelerimi gayet iyi özetleyen bir cümle kurmuş.

Alıntı yapılan: gerbay - 23 Temmuz 2012, 09:16:49
Ben size 2 farklı yazarın C++ kitabında zamanında okuduğum bir örneği vereyim;

1. yazar; kodlarken asla "const"  kullanmayın
2. yazar; kodlarken "const" olabilecek herşeyi "const" yapın..

Bunun kısa cevabı : 2 Basitçe olabilen her şeyi 'const' yap.

Eğer uzun cevabını merak ediyorsanız buradan yakın.


Erdem

Alıntı yapılan: armsistem - 23 Temmuz 2012, 19:23:14
konuyu açtığıma pişman oldum.

Neden oldunuz ben anlayamadım  ??? Anladığım kadarıyla herkes sorunuza elinden geldiği kadarıyla cevap vermeye çalıştı.

Evet aslında bir noktada konu sizin sorunuzun dışında "C++ öğrenmek için ilkönce C öğrenmek gerekir mi?" noktasına dağıldı. Ama burada forum yöneticisi arkadaşların uyanık olup konuyu bölmeleri gerekirdi diye düşünüyorum.