Указателите дават големи възможности на функциите „C“, които сме ограничени да върнат една стойност. С параметрите на указателя нашите функции вече могат да обработват действителни данни, а не копие на данни.
За да се променят действителните стойности на променливите, извикващият оператор предава адреси на параметри на указателя във функция.
В този урок ще научите -
- Примери за указатели на функции
- Функции с параметри на масива
- Функции, които връщат масив
- Функционални указатели
- Масив от указатели за функции
- Функции, използващи кухи указатели
- Функционални указатели като аргументи
Примери за указатели на функции
Например следващата програма разменя две стойности по две:
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;}}
Изход:
m is 25, n is 100m is 100, n is 25
Програмата разменя действителните стойности на променливите, тъй като функцията ги достъпва по адрес, използвайки указатели. Тук ще обсъдим програмния процес:
- Декларираме функцията, отговорна за размяната на двете променливи стойности, която приема два целочислени указателя като параметри и връща всяка стойност, когато е извикана.
- В основната функция декларираме и инициализираме две целочислени променливи ('m' и 'n'), след което отпечатваме съответно техните стойности.
- Извикваме функцията swap (), като подаваме адреса на двете променливи като аргументи, използвайки символа амперсанд. След това отпечатваме новите заменени стойности на променливи.
- Тук дефинираме съдържанието на функцията swap (), което приема две целочислени променливи адреси като параметри и декларираме временна целочислена променлива, използвана като трето поле за съхранение, за да запазим една от променливите на стойността, която ще бъде поставена във втората променлива.
- Запазете съдържанието на първата променлива, посочена с 'a' във временната променлива.
- Съхранявайте втората променлива, посочена с b, в първата променлива, посочена с a.
- Актуализирайте втората променлива (посочена от b) със стойността на първата променлива, запазена във временната променлива.
Функции с параметри на масива
В C не можем да предадем масив по стойност на функция. Докато името на масива е указател (адрес), така че просто предаваме име на масив на функция, което означава да предадем указател на масива.
Например разглеждаме следната програма:
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);}
Изход:
Total summation is 471
Тук ще обясним програмния код с неговите подробности
- Декларираме и дефинираме функцията add_array (), която приема за масив адрес (указател) с неговия номер на елементи като параметри и връща общото натрупано сумиране на тези елементи. Указателят се използва за итерация на елементите на масива (използвайки нотация p [k]) и ние натрупваме сумирането в локална променлива, която ще бъде върната след итерация на целия масив от елементи.
- Декларираме и инициализираме цяло число масив с пет цели числа. Отпечатваме общото сумиране, като предаваме името на масива (което действа като адрес) и размера на масива на add_array (), наречена функция като аргументи.
Функции, които връщат масив
В C можем да върнем указател към масив, както е в следната програма:
#includeint * 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);}
Изход:
12345
И тук ще обсъдим подробностите за програмата
- Дефинираме и декларираме функция, която връща адрес на масив, съдържащ целочислена стойност и не е взела никакви аргументи.
- Декларираме целочислен указател, който получава пълния масив, изграден след извикването на функцията, и отпечатваме съдържанието му, като итерираме целия масив от пет елемента.
Забележете, че указател, а не масив, е дефиниран за съхраняване на адреса на масива, върнат от функцията. Също така забележете, че когато се връща локална променлива от функция, трябва да я декларираме като статична във функцията.
Функционални указатели
Както знаем по дефиниция, че указателите сочат към адрес във всяко място в паметта, те също могат да сочат към началото на изпълним код като функции в паметта.
Показалецът към функция се декларира с *, общият декларация на неговата декларация е:
return_type (*function_name)(arguments)
Трябва да запомните, че скобите около (* име_на_функция) са важни, защото без тях компилаторът ще си помисли, че името на функцията връща указател на return_type.
След като дефинираме указателя на функцията, трябва да го присвоим на функция. Например, следващата програма декларира обикновена функция, дефинира показалеца на функцията, присвоява показалеца на функцията на обикновената функция и след това извиква функцията през показалеца:
#includevoid 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");}
Изход:
HiHiHi
- Дефинираме и декларираме стандартна функция, която отпечатва Hi текст k пъти, посочени от параметъра times, когато функцията е извикана
- Дефинираме функция на показалеца (със специалната си декларация), която приема цяло число параметър и не връща нищо.
- Ние инициализираме нашата функция на показалеца с функция Hi_, което означава, че указателят сочи към функция Hi_function ().
- Вместо стандартното извикване на функция чрез залепване на името на функцията с аргументи, ние извикваме само показалечната функция, като подаваме числото 3 като аргументи и това е!
Имайте предвид, че името на функцията сочи към началния адрес на изпълнимия код като име на масив, което сочи към първия му елемент. Следователно инструкции като function_ptr = & Hi_function и (* funptr) (3) са верни.
ЗАБЕЛЕЖКА: Не е важно да поставяте адресния оператор & и оператора на непряко * по време на присвояване на функция и извикване на функция.
Масив от указатели за функции
Масив от указатели на функции може да играе превключвател или роля на оператор if за вземане на решение, както в следващата програма:
#includeint 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 48Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2624
Тук обсъждаме подробностите за програмата:
- Декларираме и дефинираме четири функции, които вземат два целочислени аргумента и връщат целочислена стойност. Тези функции добавят, изваждат, умножават и разделят двата аргумента относно това коя функция е извикана от потребителя.
- Декларираме 4 цели числа, за да обработим съответно операнди, тип операция и резултат. Също така декларираме масив от четири функционални указателя. Всеки показалец на функция на елемента на масив взема два целочислени параметъра и връща целочислена стойност.
- Присвояваме и инициализираме всеки елемент на масив с вече декларираната функция. Например, третият елемент, който е третият указател на функция, ще сочи към операцията за умножение.
- Ние търсим операнди и тип операция от потребителя, въведен с клавиатурата.
- Извикахме подходящия елемент на масив (указател на функция) с аргументи и съхраняваме резултата, генериран от съответната функция.
Инструкцията int (* ope [4]) (int, int); дефинира масива от указатели на функции. Всеки елемент на масив трябва да има същите параметри и тип на връщане.
Резултатът от оператора = ope [избор] (x, y); изпълнява подходящата функция според избора, направен от потребителя Двете въведени цели числа са аргументите, предадени на функцията.
Функции, използващи кухи указатели
Посочените кухи указатели се използват по време на декларации за функции. Използваме разрешения за връщане void * за връщане на всякакъв тип. Ако приемем, че нашите параметри не се променят при преминаване към функция, ние я декларираме като const.
Например:
void * cube (const void *);
Обмислете следната програма:
#includevoid* 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;}
Резултат:
4 cubed is 64
Тук ще обсъдим подробностите за програмата:
- Дефинираме и декларираме функция, която връща целочислена стойност и приема адрес на неизменяема променлива без определен тип данни. Изчисляваме стойността на куба на променливата на съдържанието (x), посочена от указателя num, и тъй като това е празен указател, трябва да го въведем, за да го приведем към цял тип данни, като използваме указател на конкретна нотация (* тип данни) и връщаме стойността на куба.
- Декларираме операнда и променливата на резултата. Също така инициализираме нашия операнд със стойност "4."
- Извикваме функцията куб чрез предаване на адреса на операнда и обработваме връщащата стойност в променливата на резултата
Функционални указатели като аргументи
Друг начин за използване на показалеца на функция, като го предадете като аргумент на друга функция, понякога наричана „функция за обратно извикване“, защото получаващата функция „го извиква обратно“.
В заглавния файл stdlib.h функцията Quicksort "qsort ()" използва тази техника, която представлява алгоритъм, предназначен за сортиране на масив.
void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *))
- void * base: void указател към масива.
- size_t num: Номер на елемента на масива.
- size_t width Размерът на елемента.
- int (* сравнение (const void *, const void *): показалец на функция, съставен от два аргумента и връща 0, когато аргументите имат една и съща стойност, <0, когато arg1 идва преди arg2 и> 0, когато arg1 идва след arg2.
Следващата програма сортира масив от цели числа от малко на голямо число, използвайки функцията qsort ():
#include#include 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;}
Резултат:
13 14 48 50 52
Тук ще обсъдим подробностите за програмата:
- Дефинираме функцията за сравнение, съставена от два аргумента, и връща 0, когато аргументите имат една и съща стойност, <0, когато arg1 идва преди arg2 и> 0, когато arg1 идва след arg2. Параметрите са тип void указатели, лети към съответния тип данни масив (цяло число)
- Дефинираме и инициализираме цяло число масив Размерът на масива се съхранява в променливата num и размерът на всеки елемент на масива се съхранява в променлива на ширината, използвайки оператора sizeof () C.
- Ние наричаме qsort функцията и да премине на името на масива, размера ширината и сравнение на функцията определен преди това от страна на потребителя, за да се справи нашия масив във възходящ order.The сравнение ще се извършва чрез вземане на всяка итерация две масив елементи, докато целият масив ще бъдат сортирани.
- Отпечатваме елементите на масива, за да сме сигурни, че нашият масив е добре сортиран, като итерираме целия масив, използвайки for цикъл.