Funksiya və İş arqumentlərinə işarə edənlər

İndiyə qədər dəyişənlərə və məlumat obyektlərinə işarə edənlər haqqında danışdıq. İşarələr funksiyalara istinad etmək üçün də istifadə edilə bilər. Bu tip göstəricilər geniş istifadə üçündür. Fərz edək ki, proqramda istifadə olunan bir sıra və ya dəyişənlərdə müvəqqəti dəyişənlər olmadan dəyişikliklər etməliyik, onda bunu etmək üçün yeganə seçim həmin arqumentləri göstərici kimi ötürməkdir. Beləliklə, funksiya dəyişən və ya massivin ünvanını alacaq və heç bir nüsxə yaratmadan həmin yerdə məlumatları dəyişdirməyə başlayır. Bu səbəbdən bu dəyişikliklər avtomatik olaraq zəng proqramında əks olunacaqdır.

Bir funksiyaya sadə bir işarə dəyişdirmə funksiyası nümunəsi istifadə edilə bilər. Tutaq ki, iki ədədi dəyişdirmək üçün dəyişdirmə funksiyamız var. Aşağıdakı proqramda iki tam dəyər dəyişdirmək üçün bir fn_swap funksiyasının yazıldığını görə bilərik. Əsas funksiyada fn_swapPtr funksiyası göstəricisi elan olunur və fn_swap funksiyasına işarə edir. İndi bu göstərici funksiyasını çağırırıq, sonra fn_swap funksiyasına işarə edəcək və çağırılacaq.

#include <stdio.h>

void fn_swap (int *x, int *y) {// argument is a pointer
	int intTemp;

	intTemp = *x;
	*x = *y;
	*y = intTemp;
}

int main ()
{
	int intX = 30;
	int intY = 70;

	void(*fn_swapPtr) (int *, int *) = fn_swap; // declare a function pointer to point to function fn_swap

	printf("Variables BEFORE Swapping\n");
	printf("--------------------------\n");
	printf("intX = %d	", intX);
	printf("intY = %d	", intY);

	fn_swapPtr(&intX, &intY); // function pointer fn_swapPtr is called to call the function fn_swap

	printf("\n\variables AFTER Swapping\n");
	printf("--------------------------\n");
	printf("intX = %d	", intX);
	printf("intY = %d	", intY);
	
	return 0;
}

Gəlin bu funksiya göstəricisi haqqında ətraflı məlumat görək. Sadəcə olaraq funksiya göstəricisini elan etməklə funksiyaya zəng etmək proqramlaşdırma zamanı onun real istifadəsini nümayiş etdirməyəcək. Yuxarıdakı misal necə elan olunacağını göstərir və funksiya göstəricisinə daxil olmaq. Funksiya göstəricisindən real istifadənin şəklə düşdüyü nümunələrdən biri, toplama, çıxarma, vurma və bölməmiz olan arifmetik əməliyyatdır. Tutaq ki, iki ədədi və istifadəçidən yerinə yetiriləcək əməliyyatı qəbul etmək üçün proqram yazılıb və daxil edilən əməliyyatdan asılı olaraq müvafiq funksiya çağırılıb. Heç bir göstəricisi olmayan tipik proqram aşağıda göstərildiyi kimidir. Keçid dava ifadəsi hansı funksiyanın çağırılacağını müəyyən etmək üçün istifadə olunur.

#include <stdio.h>

int addition(int x, int y)
{
	return (x + y);
}

int subtraction(int x, int y)
{
	return (x / y);
}

int multiply(int x, int y)
{
	return (x * y);
}

int divide(int x, int y)
{
	return (x / y);
}

int main()
{
	int intX, intY, intResult;
	int intOption;
	
	printf("Enter the two Numbers: ");
	scanf("%d", &intX);
	scanf("%d", &intY);

	printf("0: Add \n 1: Subtract \n 2: Multiply \n 3: Divide\n");
	printf("Enter the operation to be performed from above list:\n");
	scanf("%d", &intOption);

	switch (intOption)
	{
	case 0:  intResult = addition(intX, intY); break;
	case 1:  intResult = subtraction(intX, intY); break;
	case 2:  intResult = multiply(intX, intY); break;
	case 3:  intResult = divide(intX, intY); break;
	}

	printf("Result = %d", intResult);
}

Tutaq ki, * functionPtr funksiyası göstəricisi yaratdıq. Sonra yuxarıdakı proqram aşağıdakı kimi dəyişdirilə bilər:
#include <stdio.h>

int addition(int x, int y)
{
	return (x + y);
}

int subtraction(int x, int y)
{
	return (x / y);
}

int multiply(int x, int y)
{
	return (x * y);
}

int divide(int x, int y)
{
	return (x / y);
}

int main()
{
	int intX, intY, intResult;
	int intOption;

	int(*operationPtr[4])(int x, int y) = { addition, subtraction, multiply, divide }; // declaring a array of function pointer
	
	printf("Enter the two Numbers: ");
	scanf("%d", &intX);
	scanf("%d", &intY);
		
	printf("0: Add \n 1: Subtract \n 2: Multiply \n 3: Divide\n");
	printf("Enter the operation to be performed from above list:\n");
	scanf("%d", &intOption);

	intResult = operationPtr[intOption](intX, intY); // calls the respective function depending upon the option entered

	printf("Result = %d", intResult);
}

Yuxarıdakı nümunədən görə bilərik ki, funksiya göstəricisinin istifadəsi proqramı sadələşdirdi və seçilmiş əməliyyatı da yerinə yetirdi. Beləliklə bir funksiya göstəricisi yaradırıq və bunları proqramda istifadə edirik. Burada qeyd edə bilərik ki, funksiya göstəricisi quruluşlarına bənzər fərqli funksiyaları həyata keçirmək üçün istifadə olunur. Başqa sözlə, funksiya göstəriciləri bir proqramda polimorfizm olduqda faydalıdır. Bu hallarda kodlamanı asanlaşdırır.

İndi funksiya göstəricisini arqument kimi necə ötürəcəyimizi görək. Yuxarıda göstərilən svop funksiyasını nəzərdən keçirək. İndi iki dəyişdirmə funksiyamız var - biri tam ədədi dəyişdirmək, digəri simvol dəyişdirmək. Hər iki funksiyada da dəyişdirmə məntiqi eynidir. Ancaq funksiyaya ötürülən mübahisə fərqli məlumat tipidir. Beləliklə, məlumatları dəyişdirmək üçün eyni dəyişdirmə funksiyasından istifadə edə bilmirik və iki ayrı dəyişdirmə funksiyasına ehtiyacımız var.

intTemp = intX;

intX = intY;

intY = intTemp;

Məntiq eyni olsa da, məlumat tipləri fərqlidir. Ancaq funksiyanı məlumat növündən müstəqil etsək, dəyişdirmə vəzifəmiz asanlaşır. Fəaliyyətə verilən məlumat tiplərindən narahat olmağımız lazım deyil. Əgər bu arqumentləri göstərici kimi etsək, o zaman onlar faktiki dəyərdən çox ünvan yerlərini göstərəcəklər və dəyişdirmə tapşırığımız daha asan olacaq - yalnız dəyərlər əvəzinə dəyişənlərin ünvanlarını dəyişdirin. Bu daha sadə görünür. Ancaq düşünürsən ki, işləməyə verilən məlumat tiplərindən xilas olacaq? Xeyr, məlumat tipini aradan qaldırmır, çünki göstəricinin göstərdiyi məlumat növünü təyin etməliyik. Göstəriciləri ötürərək tam ədədləri və simvolları dəyişdirmək üçün aşağıdakı parçaları müşahidə edin. Arqumentdə göstərilən məlumat tipləri var.

void fn_swap (int *x, int*y) {// argument is a pointer
	int intTemp;

	intTemp = *x;
	*x = *y;
	*y = intTemp;			
}

 
void fn_swap (char *x, char *y) {// argument is a pointer
	char charTemp;

	charTemp = *x;
	*x = *y;
	*y = charTemp;
}

Buradakı məqsədimiz funksiya mübahisəsindəki məlumat tiplərindən xilas olmaq və funksiya məlumat növünü pulsuz arqumentləşdirməkdir. Bu, əsas funksiyadan məlumat növündən asılı olmayaraq funksiyaları çağırmağa kömək edəcəkdir. Beləliklə, bu arqumentləri tam və ya xarakter göstəriciləri əvəzinə etibarsız göstərici kimi göstərməliyik. Fəqət mübahisəni etibarsız olaraq keçirtmək funksiyanı məlumat tiplərindən asılı vəziyyətə gətirəcək, lakin bu arqumentləri funksiyada istifadə edərkən məlumat tiplərini bilməliyik. Hansı məlumat dəyərinin olduğunu bilmədən onları dəyişdirə bilmərik. Beləliklə, arqumentləri funksiyanın daxilindəki müvafiq məlumat tiplərinə verməliyik. Beləliklə, yuxarıdakı dəyişdirmə funksiyası aşağıdakı kimi dəyişəcəkdir.
void fn_intswap (void *x, void *y) {// argument is a pointer
	int intTemp;
	int *x1 = (int *)x;
	int *y1 = (int *)y;

	intTemp = *x1;
	*x1 = *y1;
	*y1 = intTemp;
}

void fn_charswap(void *x, void *y) {// argument is a pointer
    char charTemp;
    char *x1 = (char *)x;
    char *y1 = (char *)y;

    charTemp = *x1;
    *x1 = *y1;
    *y1 = charTemp;
}

İndi hər iki funksiya arqumenti də məlumat növü pulsuzdur və ona verilən məlumat növü barədə narahat olmamalıyıq. İndi bu funksiyaları necə çağırmaq və etibarsız olan arqumentləri necə ötürmək olar? Keçirilən arqumentlərdən asılı olaraq bu funksiyalardan birini göstərəcək bir funksiya göstəricisi yaradacağıq. yəni; etibarsız göstərici arqumentlərini qəbul edəcək və iş zamanı fn_intswap ya da fn_charswap-a işarə edəcək bir funksiya göstəricisi * fnPtr yaradacağıq.
void(*fnPtr)(void *,  void *)

Burada onun arqumentlərinin də etibarsız olduğunu görə bilərik. Tərtibçi hansı məlumat tiplərinin ona keçdiyini necə biləcək? Tərtibçinin özü hansı funksiyanın çağırılacağını bilmir. İstifadəçi / tərtibatçı onu elə tərtib etməlidir ki, tərtibçi hansı funksiyanın çağırılacağını bilməlidir. Bunu kodlaşdırma metodlarından biri, hesab işində istifadə etdiyimiz metoddan istifadə etməkdir. Burada bir işarə göstəricisini arqument olaraq ötürərək başqa bir üsul görək. Beləliklə, dəyişdiriləcək dəyişənləri (məlumat növlərindən asılı olmayaraq - boşluq) və göstəricini həmin funksiyaya ötürərək çağırılacaq funksiyanı qəbul edəcək başqa bir fn_swap funksiyası yazacağıq. Bu, funksiya göstəricisinin göstərdiyi funksiyanı çağıran sadə bir funksiyadır. yəni; Əgər funksiya göstəricisi fn_intswap-a işarə edirsə, o zaman həmin funksiya çağırılacaq, fn_charswap-a işarə edirsə o zaman həmin funksiya çağırılacaqdır.
void fn_swap(void *x, void *y, void(*fnPtr)(void *,  void *)){
	fnPtr(x, y); // Calls the respective function
}

Funksiya göstəricisini müvafiq funksiyalara yönəltmək üçün necə edəcəyik? Bu, funksiya çağırışı edilərkən əsas proqramda edilir. Burada fn_swap göstəricini müvafiq funksiyaya arqument kimi ötürür.
fn_swap(&intX, &intY, fn_intswap);// swap integers using function pointer
fn_swap(&charX, &charY, fn_charswap); // swap integers using function pointer

 
#include <stdio.h>

void fn_charswap(void *x,  void *y) {// argument is a pointer
	char charTemp;
	char *x1 = (char *)x;
	char *y1 = (char *)y;

	charTemp = *x1;
	*x1 = *y1;
	*y1 = charTemp;
}

void fn_intswap(void *x,  void *y) {// argument is a pointer
	int intTemp;
	int *x1 = (int *)x;
	int *y1 = (int *)y;

	intTemp = *x1;
	*x1 = *y1;
	*y1 = intTemp;
}

void fn_swap(void *x, void *y, void(*fnPtr)(void *,  void *)){// passing function pointer as argument
	fnPtr(x, y);
}

int main()
{
	char charX = 'C';
	char charY = 'P';
	int intX = 12;
	int intY = 67;

	printf("Variables BEFORE Swapping\n");
	printf("--------------------------\n");
	printf("charX = %c	", charX);
	printf("charY = %c	", charY);
	printf("\nintX = %d	", intX);
	printf("intY = %d	", intY);

	fn_swap(&intX, &intY, fn_intswap);// swap integers using integer function pointer
	fn_swap(&charX, &charY, fn_charswap); // swap integers using character function pointer	
	//fn_swap(&intX, &intY, fn_charswap);

	printf("\n\nVariables AFTER Swapping\n");
	printf("--------------------------\n");
	printf("charX = %c	", charX);
	printf("charY = %c	", charY);
	printf("\nintX = %d	", intX);
	printf("intY = %d	", intY);

	return 0;
}

Tam proqrama və ya simvol funksiyasına əsas proqramda zəng etmək birbaşa irəlidədir. Fn_swap funksiyasını aşağıda göstərildiyi kimi arqumentlə çağırmağın nəticəsi nə olacaq?

fn_swap(&intX, &intY, fn_charswap);

Burada funksiyaya ötürülən dəyişənlər tam ədəddir, lakin funksiya göstəricisi simvol funksiyasına işarə edir. Bu tam dəyərləri dəyişdirəcəkmi? Yuxarıdakı əsas proqramdakı kodu aşağıdakı kimi dəyişdirin və kodu icra edin. Nəticə nədir? Fn_charswap çağırdı? Tam dəyər və ya xarakter dəyərini dəyişdirdi, yoxsa dəyişiklik olmadı?
//fn_swap(&intX, &intY, fn_intswap);// swap integers using function pointer
fn_swap(&charX, &charY, fn_charswap); // swap integers using function pointer	
fn_swap(&intX, &intY, fn_charswap);

Tam ədədi dəyişdirdi! Ancaq funksiya göstəricisini xarakter dəyişdirməyə keçirdik. Yenə də tam dəyərləri dəyişdirdi. Bu necə oldu? Bunun səbəbi, məlumat tipinin və dəyişənə göstəricilərin müstəqil olmasıdır. Yəni, funksiyaya keçərkən dəyişənlərin məlumat növünü təyin etməmişik və boş sayılır. Bundan əlavə, bu dəyişənlərə işarə edənlər ötürülür ki, bu dəyişənlərin ünvanı intX və intY funksiyası ünvanına ötürülür. Buna görə əsas proqram fn_swap funksiyasını çağırdıqda, bu iki dəyişənin ünvanları ötürülür. Fn_swap-a ötürülən funksiya göstəricisi fn_charswap olsa da, dəyişənlərin həqiqi dəyərini almır. Yalnız yaddaş adreslərini görür və ünvanları dəyişdirir. Ancaq içəridə dökümdən istifadə edir, çünki boşluğu dəyişənlər üçün ünvanı dəyişdirə bilməz.

Yuxarıdakı nümunədən başa düşürük ki, tam və simvolları dəyişdirmək üçün bizə iki funksiya lazım deyil. Hər hansı birini dəyişdirmək üçün yalnız bir ümumi funksiyadan istifadə edə bilərik dəyişənlərin növü göstəriciləri dəyişənə ötürməklə. Beləliklə, yuxarıdakı kodu yalnız bir dəyişdirmə funksiyasına malik olmaq üçün dəyişdirə bilərik.

PS: fn_swap funksiya göstəricisinin arqument kimi necə keçəcəyini göstərmək üçün saxlanılır. Ancaq kodu daha sadə etmək üçün bu funksiya da ləğv edilə bilər (aşağıda göstərildiyi kimi). Kodun qalan hissəsi aşağıdakı qutuda olduğu kimi qalır.

#include <stdio.h>


void fn_swapVariables(void *x, void *y) {// argument is a pointer
	int intTemp;
	int *x1 = (int *)x; // could be casted to any datatype, this actually does not matter as address of variables are considered here.
	int *y1 = (int *)y;

	intTemp = *x1;
	*x1 = *y1;
	*y1 = intTemp;
}

void fn_swap(void *x, void *y, void(*fnPtr)(void *, void *)){// passing function pointer as argument
	fnPtr(x, y);
}

int main()
{
	char charX = 'C';
	char charY = 'P';
	int intX = 12;
	int intY = 67;

	printf("Variables BEFORE Swapping\n");
	printf("--------------------------\n");
	printf("charX = %c	", charX);
	printf("charY = %c	", charY);
	printf("\nintX = %d	", intX);
	printf("intY = %d	", intY);

	fn_swap(&intX, &intY, fn_swapVariables);// swap integers using integer function pointer
	fn_swap(&charX, &charY, fn_swapVariables); // swap integers using character function pointer	
	 
	// Above 2 lines of function call can also be called as below 
	//fn_swapVariables(&intX, &intY);// swap integers without passing function pointer
	//fn_swapVariables(&charX, &charY);

	printf("\n\nVariables AFTER Swapping\n");
	printf("--------------------------\n");
	printf("charX = %c	", charX);
	printf("charY = %c	", charY);
	printf("\nintX = %d	", intX);
	printf("intY = %d	", intY);

	return 0;
}

Aşağıdakı kimi funksiya göstəricisini arifmetik əməliyyat üçün arqument kimi göstərə bilərik.

#include <stdio.h>

int addition(int x, int y)
{
	return (x + y);
}

int subtraction(int x, int y)
{
	return (x / y);
}

int multiply(int x, int y)
{
	return (x * y);
}

int divide(int x, int y)
{
	return (x / y);
}

int fnOperation(int x, int y, int(*fnPtr)(int, int)){ // function pointer is passed to the function
	return (*fnPtr)(x, y);
}

int main()
{
	int intX, intY, intResult;
	int intOption;
	
	printf("Enter the two Numbers: ");
	scanf("%d", &intX);
	scanf("%d", &intY);
		
	printf("0: Add \n 1: Subtract \n 2: Multiply \n 3: Divide\n");
	printf("Enter the operation to be performed from above list:\n");
	scanf("%d", &intOption);

	switch (intOption)
	{ // call respective functions by passing the pointers to the function
	case 0:  intResult = fnOperation(intX, intY, addition); break;
	case 1:  intResult = fnOperation(intX, intY, subtraction); break;
	case 2:  intResult = fnOperation(intX, intY, multiply); break;
	case 3:  intResult = fnOperation(intX, intY, divide); break;
	}

	printf("Result = %d", intResult);
}

Yuxarıdakı nümunə dəyişdirmə nümunəsindən fərqlidir. Burada əməliyyatı yerinə yetirmək üçün fərqli hesab funksiyaları istifadə olunur və funksiya göstəricisi istifadəçinin daxil etdiyi seçimdən asılı olaraq iş vaxtında müvafiq funksiyaya istinad edilir. Bu səbəbdən dəyişdirmə funksiyasını yerinə yetirdiyimiz kimi heç bir funksiyanı aradan qaldıra bilmərik, ancaq istifadəçi girişindən asılı olaraq tələb olunan funksiyanı çağırma rahatlığı verir. Burada dəyişənlər ünvan kimi ötürmək əvəzinə dəyərlə ötürülür.

Burada geliştiricinin dəyişənlərin növünü, dəyişənlərin sayını və fərqli hesab funksiyasına ötürmə metodunu bilməməsi lazımdır. Yalnız fnOperation funksiyasını bilirsə, bütün əməliyyatları yerinə yetirmək kifayətdir. Ayrıca, fnOperation çağırarkən, funksiya digər funksiyaya işarə edənlər ona verilən dəyərlər haqqında heç bir şey bilmir. Həqiqətən işləmə vaxtı həqiqi funksiyanı çağırarkən fnOperation funksiyası içərisindəki dəyəri alır.

Şərh yaz

Translate »
1