Örneklerle C Programlamada Fonksiyon İşaretçileri
İşaretçiler, tek bir değer döndürmekle sınırlı olduğumuz 'C' fonksiyonlarına büyük olanaklar sağlar. İşaretçi parametreleriyle, işlevlerimiz artık verilerin bir kopyası yerine gerçek verileri işleyebiliyor.
Değişkenlerin gerçek değerlerini değiştirmek için çağıran ifade, adresleri bir işlevdeki işaretçi parametrelerine iletir.
İşlev İşaretçileri Örneği
Örneğin, bir sonraki program ikinin iki değerini değiştirir:
void swap (int *a, int *b); int main() { int m = 25; int n = 100; printf("m is %d, n is %d\n", m, n); swap(&m, &n); printf("m is %d, n is %d\n", m, n); return 0;} void swap (int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp;} }
Çıktı:
m is 25, n is 100 m is 100, n is 25
Program, gerçek değişkenlerin değerlerini değiştirir çünkü işlev bunlara adres yoluyla erişir. işaretçi. Burada program sürecini tartışacağız:
- Parametre olarak iki tamsayı işaretçisini alan ve çağrıldığında herhangi bir değeri döndüren, iki değişken değerinin değiştirilmesinden sorumlu işlevi bildiririz.
- Ana fonksiyonda, iki tamsayı değişkeni ('m' ve 'n') bildirir ve başlatırız, ardından sırasıyla değerlerini yazdırırız.
- İki değişkenin adresini ve işareti kullanarak argüman olarak ileterek swap() işlevini çağırırız. Bundan sonra değişkenlerin yeni değiştirilen değerlerini yazdırıyoruz.
- Burada, iki tam sayı değişken adresini parametre olarak alan ve ikinci değişkene konulacak değer değişkenlerinden birini kaydetmek için üçüncü bir depolama kutusu olarak kullanılan geçici bir tam sayı değişkeni bildiren swap() fonksiyonunun içeriğini tanımlıyoruz.
- 'a' ile gösterilen ilk değişkenin içeriğini geçici değişkene kaydedin.
- B ile gösterilen ikinci değişkeni, a ile gösterilen ilk değişkende saklayın.
- İkinci değişkeni (b ile gösterilen), geçici değişkende kaydedilen ilk değişkenin değeriyle güncelleyin.
Dizi Parametreleriyle İşlevler
C'de bir diziyi değere göre bir fonksiyona aktaramayız. Oysa dizi adı bir işaretçidir (adres), bu nedenle diziye bir işaretçi iletmek anlamına gelen bir işleve yalnızca bir dizi adı iletiriz.
Örneğin, aşağıdaki programı ele alalım:
int add_array (int *a, int num_elements); int main() { int Tab[5] = {100, 220, 37, 16, 98}; printf("Total summation is %d\n", add_array(Tab, 5)); return 0;} int add_array (int *p, int size) { int total = 0; int k; for (k = 0; k < size; k++) { total += p[k]; /* it is equivalent to total +=*p ;p++; */} return (total);}
Çıktı:
Total summation is 471
Burada program kodunu detaylarıyla açıklayacağız
- Bir dizi adresini (işaretçiyi) öğe sayısıyla birlikte parametre olarak alan ve bu öğelerin toplam toplamını döndüren add_array() işlevini bildirir ve tanımlarız. İşaretçi, dizi öğelerini yinelemek için kullanılır (p[k] gösterimini kullanarak) ve toplamı, tüm öğe dizisi yinelendikten sonra döndürülecek olan yerel bir değişkende biriktiririz.
- Beş tamsayı öğeli bir tamsayı dizisini bildiriyor ve başlatıyoruz. Dizi adını (adres görevi görür) ve dizi boyutunu ileterek toplam toplamı yazdırırız. add_array()argüman olarak işlev denir.
Dizi Döndüren İşlevler
C'de, aşağıdaki programda olduğu gibi bir diziye işaretçi döndürebiliriz:
#include <stdio.h> int * build_array(); int main() { int *a; a = build_array(); /* get first 5 even numbers */ for (k = 0; k < 5; k++) printf("%d\n", a[k]); return 0;} int * build_array() { static int Tab[5]={1,2,3,4,5}; return (Tab);}
Çıktı:
1 2 3 4 5
Ve burada program detaylarını tartışacağız
- Tamsayı değeri içeren bir dizi adresi döndüren ve herhangi bir argüman almayan bir fonksiyon tanımlıyor ve bildiriyoruz.
- İşlev çağrıldıktan sonra oluşturulan dizinin tamamını alan bir tamsayı işaretçisi bildiririz ve beş öğeli dizinin tamamını yineleyerek içeriğini yazdırırız.
İşlev tarafından döndürülen dizi adresini depolamak için bir dizi değil, bir işaretçinin tanımlandığına dikkat edin. Ayrıca, bir işlevden yerel bir değişken döndürüldüğünde, onu işlevde statik olarak bildirmemiz gerektiğine dikkat edin.
İşlev İşaretçileri
Tanım gereği işaretçilerin herhangi bir bellek konumundaki bir adresi işaret ettiğini bildiğimiz gibi, bellekteki işlevler olarak yürütülebilir kodun başlangıcına da işaret edebilirler.
İşlev işaretçisi * ile bildirilir, bildiriminin genel ifadesi şöyledir:
return_type (*function_name)(arguments)
(*işlev_adı) etrafındaki parantezlerin önemli olduğunu unutmamalısınız çünkü bunlar olmadan derleyici, işlev_adı'nın dönüş_tipi işaretçisini döndürdüğünü düşünecektir.
Fonksiyon işaretçisini tanımladıktan sonra, onu bir fonksiyona atamamız gerekir. Örneğin, bir sonraki program sıradan bir fonksiyon bildirir, bir fonksiyon işaretçisi tanımlar, fonksiyon işaretçisini sıradan fonksiyona atar ve bundan sonra fonksiyonu işaretçi aracılığıyla çağırır:
#include <stdio.h> void Hi_function (int times); /* function */ int main() { void (*function_ptr)(int); /* function pointer Declaration */ function_ptr = Hi_function; /* pointer assignment */ function_ptr (3); /* function call */ return 0;} void Hi_function (int times) { int k; for (k = 0; k < times; k++) printf("Hi\n");}
Çıktı:
Hi Hi Hi
- Fonksiyon çağrıldığında times parametresiyle belirtilen k kez Hi metnini basan standart bir fonksiyon tanımlar ve bildiririz.
- Bir tamsayı parametresi alan ve hiçbir şey döndürmeyen bir işaretçi işlevi (özel bildirimiyle) tanımlarız.
- İşaretçi fonksiyonumuzu Hi_function ile başlatırız, bu da işaretçinin Hi_function()'a işaret ettiği anlamına gelir.
- Fonksiyon ismine argümanlarla dokunarak yapılan standart fonksiyon çağrısı yerine, argüman olarak 3 sayısını ileterek yalnızca işaretçi fonksiyonunu çağırırız, işte bu kadar!
İşlev adının, ilk öğesine işaret eden bir dizi adı gibi, yürütülebilir kodun başlangıç adresine işaret ettiğini unutmayın. Bu nedenle function_ptr = &Hi_function ve (*funptr)(3) gibi talimatlar doğrudur.
NOT: Fonksiyon ataması ve fonksiyon çağrısı sırasında adres operatörünü & ve dolaylı operatör *'i eklemek önemli değildir.
İşlev İşaretçileri Dizisi
Bir dizi işlev işaretçisi, bir sonraki programda olduğu gibi, karar vermede bir anahtar veya if ifadesi rolü oynayabilir:
#include <stdio.h> int sum(int num1, int num2); int sub(int num1, int num2); int mult(int num1, int num2); int div(int num1, int num2); int main() { int x, y, choice, result; int (*ope[4])(int, int); ope[0] = sum; ope[1] = sub; ope[2] = mult; ope[3] = div; printf("Enter two integer numbers: "); scanf("%d%d", &x, &y); printf("Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: "); scanf("%d", &choice); result = ope[choice](x, y); printf("%d", result); return 0;} int sum(int x, int y) {return(x + y);} int sub(int x, int y) {return(x - y);} int mult(int x, int y) {return(x * y);} int div(int x, int y) {if (y != 0) return (x / y); else return 0;}
Enter two integer numbers: 13 48 Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2 624
Programın detaylarını burada tartışıyoruz:
- Dört tanesini ilan edip tanımlıyoruz fonksiyonlar iki tamsayı argümanı alır ve bir tamsayı değeri döndürür. Bu işlevler, kullanıcı tarafından hangi işlevin çağrıldığına ilişkin iki argümanı toplar, çıkarır, çarpar ve böler.
- İşlenenleri, işlem türünü ve sonucu işlemek için sırasıyla 4 tam sayı bildiririz. Ayrıca dört işlev işaretçisinden oluşan bir dizi bildiririz. Dizi öğesinin her işlev işaretçisi iki tamsayı parametresi alır ve bir tamsayı değeri döndürür.
- Her dizi öğesini önceden bildirilmiş olan işlevle atar ve başlatırız. Örneğin üçüncü fonksiyon işaretçisi olan üçüncü eleman çarpma işlemi fonksiyonuna işaret edecektir.
- Kullanıcının klavyeyle yazdığı işlenenleri ve işlem türünü ararız.
- Uygun dizi elemanını (Fonksiyon işaretçisi) argümanlarla çağırdık ve uygun fonksiyonun ürettiği sonucu sakladık.
Komut int (*ope[4])(int, int); işlev işaretçilerinin dizisini tanımlar. Her dizi öğesi aynı parametrelere ve dönüş türüne sahip olmalıdır.
Sonuç = ope[seçim](x, y); kullanıcının yaptığı seçime göre uygun fonksiyonu çalıştırır. Girilen iki tamsayı, fonksiyona iletilen argümanlardır.
Geçersiz İşaretçileri Kullanan İşlevler
İşlev bildirimleri sırasında geçersiz işaretçiler kullanılır. Herhangi bir türün döndürülmesine izin veren void * return türünü kullanırız. Bir fonksiyona geçerken parametrelerimizin değişmediğini varsayarsak onu const olarak bildiririz.
Örneğin:
void * cube (const void *);
Aşağıdaki programı göz önünde bulundurun:
#include <stdio.h> void* cube (const void* num); int main() { int x, cube_int; x = 4; cube_int = cube (&x); printf("%d cubed is %d\n", x, cube_int); return 0;} void* cube (const void *num) { int result; result = (*(int *)num) * (*(int *)num) * (*(int *)num); return result;}
Sonuç:
4 cubed is 64
Program detaylarını burada tartışacağız:
- Tamsayı değeri döndüren ve belirli bir veri türü olmadan değiştirilemeyen değişkenin adresini alan bir fonksiyon tanımlıyor ve bildiriyoruz. Sayı işaretçisinin işaret ettiği içerik değişkeninin (x) küp değerini hesaplıyoruz ve bu bir geçersiz işaretçi olduğu için, bunu belirli bir gösterim (* veri türü) işaretçisi kullanarak bir tamsayı veri türüne dönüştürmemiz gerekiyor ve geri dönüyoruz küp değeri.
- İşleneni ve sonuç değişkenini bildiririz. Ayrıca işlenenimizi “4” değeriyle başlatıyoruz.
- İşlenen adresini ileterek küp fonksiyonunu çağırıyoruz ve sonuç değişkeninde dönen değeri işliyoruz.
Bağımsız Değişken Olarak İşlev İşaretçileri
Bir işlev işaretçisini, alıcı işlevin "geri çağırması" nedeniyle bazen "geri çağırma işlevi" olarak adlandırılan başka bir işleve argüman olarak ileterek yararlanmanın başka bir yolu.
Stdlib.h başlık dosyasındaki Quicksort “qsort()” işlevi, bir diziyi sıralamaya adanmış bir algoritma olan bu tekniği kullanır.
void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
- void *base : diziye yönelik void işaretçisi.
- size_t num : Dizi öğesi numarası.
- size_t genişlik Öğe boyutu.
- int (*compare (const void *, const void *) : iki bağımsız değişkenden oluşan işlev işaretçisi ve bağımsız değişkenler aynı değere sahip olduğunda 0, arg0 arg1'den önce geldiğinde <2 ve arg0 arg1'den sonra geldiğinde >2 döndürür.
Aşağıdaki program, qsort() fonksiyonunu kullanarak bir tamsayı dizisini küçükten büyüğe doğru sıralar:
#include <stdio.h> #include <stdlib.h> int compare (const void *, const void *); int main() { int arr[5] = {52, 14, 50, 48, 13}; int num, width, i; num = sizeof(arr)/sizeof(arr[0]); width = sizeof(arr[0]); qsort((void *)arr, num, width, compare); for (i = 0; i < 5; i++) printf("%d ", arr[ i ]); return 0;} int compare (const void *elem1, const void *elem2) { if ((*(int *)elem1) == (*(int *)elem2)) return 0; else if ((*(int *)elem1) < (*(int *)elem2)) return -1; else return 1;}
Sonuç:
13 14 48 50 52
Program detaylarını burada tartışacağız:
- İki bağımsız değişkenden oluşan karşılaştırma işlevini tanımlarız ve bağımsız değişkenler aynı değere sahip olduğunda 0 değerini döndürür, arg0 arg1'den önce geldiğinde <2 ve arg0 arg1'den sonra geldiğinde >2 döndürür. Parametreler, uygun dizi veri türüne dönüştürülen bir geçersiz işaretçi türüdür. (tamsayı)
- Bir tamsayı dizisi tanımlar ve başlatırız. Dizi boyutu, num değişkendir ve her dizi öğesinin boyutu önceden tanımlanmış sizeof() kullanılarak genişlik değişkeninde saklanır C operatörü.
- Biz çağrı q sıralama Dizimizi artan düzende sıralamak için daha önce kullanıcı tarafından tanımlanan dizi adını, boyutunu, genişliğini ve karşılaştırma fonksiyonunu iletin. Karşılaştırma, dizinin tamamı sıralanana kadar her yinelemede iki dizi öğesi alınarak gerçekleştirilecektir.
- Dizimizin iyi sıralandığından emin olmak için dizi elemanlarını, diziyi kullanarak tüm diziyi yineleyerek yazdırıyoruz. döngü için.