Menu

C'de Fonksiyon Göstericileri: Söz Dizimi, Geri Çağrılar ve qsort

Fonksiyonların da adresi vardır ve bir fonksiyon göstericisi bir tanesini saklar. Bu sayfa bildirim söz dizimini çözüyor, typedef'in onu nasıl okunaklı kıldığını gösteriyor ve geri çağrılara, özel karşılaştırıcılı qsort'a ve dağıtım tablolarına doğru ilerliyor.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Derlenmiş kod da tıpkı veri gibi bellekte yaşar, dolayısıyla bir fonksiyonun bir adresi vardır. Fonksiyon göstericisi, bir tanesini saklayan bir değişkendir - ve bir fonksiyonu saklayabildiğinizde onu argüman olarak geçirebilir, bir tablosunu tutabilir ya da çalışma zamanında aralarında seçim yapabilirsiniz.

Özel sıralama düzeni, olay işleyicileri, eklenti arayüzleri ve durum makinelerinin arkasındaki yetenek budur. Aynı zamanda bir okuyucunun durup gözlerini kısmasına en çok yol açacak C söz dizimi parçasıdır, o yüzden oradan başlıyoruz.

Bildirim Söz Dizimi

Sıradan bir fonksiyon alın:

int add(int a, int b);

Adresini tutabilecek bir gösterici bildirmek için ismi (*isim) ile değiştirin:

int (*op)(int, int);

İçten dışa okuyun: op bir göstericidir, bir fonksiyona, (int, int) alan, int döndüren.

Parantezler süs değildir. Onlar olmadan *, bunun yerine dönüş tipine bağlanır:

int (*f)(int);   // int alıp int döndüren bir fonksiyona gösterici
int *g(int);     // int alıp int* döndüren bir FONKSİYON - tamamen farklı

İşte baştan sona çalışırken:

Her iki çağrı biçimi de çalışır, çünkü bir fonksiyon adı otomatik olarak bir göstericiye dönüşür. op(10, 3) yazın; (*op)(10, 3) yazımı bir kalıntıdır.

Son printf hakkında bir not: %p bir nesne göstericisi bekler ve fonksiyon göstericileri biçimsel olarak ayrı bir ailedir, dolayısıyla birini yazdırmak tam anlamıyla taşınabilir değildir. Dönüşüm, hiç çalıştığı platformlarda derleyicileri susturur; birini yazdırmanız nadiren gerekecek.

typedef Onu Okunaklı Kılar

Bildirim söz dizimi hızla çirkinleşir ve typedef standart çaredir:

int apply(BinaryOp op, int x, int y) ile ham biçimi, int apply(int (*op)(int, int), int x, int y), karşılaştırın. Aynı anlam; birini bir bakışta okuyabiliyorsunuz. Genel kurallar için bkz. typedef.

Geri Çağrılar: Davranışı Çağırana Bırakmak

Geri çağrı, başka bir fonksiyona, doğru anda sizi geri çağırabilsin diye verdiğiniz bir fonksiyondur. Bir kod parçasının işin yapısını halletmesini sağlarken kararı çağıranın vermesine izin verir.

Fonksiyon göstericileri olmadan count_even, count_positive ve count_big yazardınız - aynı döngünün üç kopyası. Onlarla döngü bir kez yazılır ve test parametre olur.

Asıl Kazanç: qsort

Standart kütüphanenin qsort'u, gerçekten kullanacağınız fonksiyon göstericisi örneğidir. Her şeyden oluşan bir diziyi sıralar ve bunu, elemanlar hakkında boyutlarından ve sizin sağladığınız bir karşılaştırıcıdan başka hiçbir şey bilmeden başarır.

void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void *, const void *));

Karşılaştırıcı iki const void * alır - iki elemanın adresleri - ve birincisi ikinciden önce geliyorsa negatif bir sayı, eşitlerse sıfır, sonra geliyorsa pozitif döndürür.

O karşılaştırıcıdan taşınacak iki alışkanlık. Asla return x - y; yazmayın - zekice görünür ve büyük ya da negatif değerler için taşar, yanlış bir işaret ve yanlış sıralanmış bir dizi üretir. Dereferans etmeden önce dönüştürün, sonra değil: *(const int *)a, çünkü bir void * üzerindeki *a'nın okuyacak bir şeyi yoktur.

Aynı makine, yapıları istediğiniz alana göre sıralar:

(q->score > p->score) - (q->score < p->score) deyimi, çıkarma ve taşma olmadan tam olarak -1, 0 ya da 1 döndürür. Ve strcmp zaten doğru dönüş geleneğine sahiptir, dolayısıyla bir metin karşılaştırıcısı tek satırdır.

Fonksiyon Göstericisi Tabloları

Bir fonksiyon göstericisi dizisi, bir if/else zincirini bir aramaya dönüştürür. Yorumlayıcılar işlem kodlarını böyle dağıtır ve menü sistemleri komutları böyle çalıştırır.

Bir işlem eklemek artık bir switch'i düzenlemek değil, bir satır eklemektir. Bunu yapılarla eşleştirin, standart C eklenti biçimini elde edersiniz: bu dilde "bir arayüz", fonksiyon göstericilerinden oluşan bir yapı demektir.

Canınızı Yakacak Şeyler

İmzalar tam olarak eşleşmelidir. Bir fonksiyon göstericisinin tipi, parametre tiplerini ve dönüş tipini içerir. Uyumsuz bir fonksiyon atamak bir kısıt ihlalidir ve yanlış tipli bir gösterici üzerinden çağırmak, "çalışsa" bile tanımsız davranıştır. Bir uyarıyı susturmak için bir karşılaştırıcının imzasını dönüştürmeyin - karşılaştırıcıyı düzeltin.

Boş bir fonksiyon göstericisi de boş bir göstericidir. Tam olarak veri göstericilerinde olduğu gibi NULL ile başlatın ve çağırmadan önce kontrol edin:

if (handler != NULL) {
    handler(event);
}

O denetimin ekmeğini neden hak ettiği için bkz. boş göstericiler.

void göstericiler tür güvenliğini kaybeder ve tür sistemi sizsiniz. Bir qsort karşılaştırıcısının içinde hiçbir şey sizi yanlış tipe dönüştürmekten alıkoymaz. Bir double dizisini compare_ints ile sıralarsanız tertemiz derlenir ve saçmalık üretir.

Bildirim söz dizimi iç içe geçer. Bir fonksiyon göstericisi döndüren bir fonksiyon int (*get_op(char c))(int, int);'dir. Kendinizi bunu yazarken bulduğunuzda typedef'e uzanın:

typedef int (*BinaryOp)(int, int);
BinaryOp get_op(char c);           // aynı şey, okunaklı

Sıkça Sorulan Sorular

C'de bir fonksiyon göstericisi nasıl bildirilir?

Fonksiyonun imzasını, fonksiyon adının geleceği yere (*isim) koyarak yazın: int (*op)(int, int);, op'u iki int alıp int döndüren bir fonksiyona gösterici olarak bildirir. *op etrafındaki parantezler zorunludur - onlar olmadan gösterici döndüren bir fonksiyon bildirmiş olursunuz.

Bir gösterici üzerinden fonksiyon nasıl çağrılır?

Ya op(3, 4) ya da (*op)(3, 4) - ikisi de geçerlidir ve aynı şeyi yapar, çünkü bir fonksiyon adı otomatik olarak bir göstericiye dönüşür. Modern C kodu düz op(3, 4) biçimini kullanır.

C'de geri çağrı (callback) nedir?

Başka bir fonksiyona, sizi geri çağırabilsin diye verdiğiniz bir fonksiyondur. qsort klasik örnektir: bir karşılaştırıcı geçirirsiniz ve qsort, iki elemandan hangisinin önce geldiğini bilmesi gerektiğinde onu çağırır. Tek bir sıralama yordamının her tip üzerinde çalışmasını sağlayan şey budur.

Bir fonksiyonun adresini alırken & gerekir mi?

Hayır. Bir fonksiyon adı zaten o fonksiyona bir göstericiye dönüşür, dolayısıyla op = add; ile op = &add; eşdeğerdir. C kodunun çoğu &'i atlar.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA