Указатели предоставляют большие возможности функциям "C", которые могут возвращать только одно значение. Благодаря параметрам указателя наши функции теперь могут обрабатывать фактические данные, а не их копии.
Чтобы изменить фактические значения переменных, вызывающий оператор передает адреса параметрам указателя в функции.
В этом руководстве вы узнаете:
- Пример указателей на функции
- Функции с параметрами массива
- Функции, возвращающие массив
- Указатели функций
- Массив указателей на функции
- Функции, использующие указатели void
- Указатели на функции как аргументы
Пример указателей на функции
Например, следующая программа меняет местами два значения из двух:
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)
Вы должны помнить, что круглые скобки вокруг (* function_name) важны, потому что без них компилятор будет думать, что function_name возвращает указатель 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_function, что означает, что указатель указывает на 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); определяет массив указателей на функции. Каждый элемент массива должен иметь одинаковые параметры и возвращаемый тип.
Выражение result = ope [выбор] (x, y); запускает соответствующую функцию в соответствии с выбором, сделанным пользователем. Два введенных целых числа являются аргументами, переданными функции.
Функции, использующие указатели void
Указатели на пустоту используются во время объявления функций. Мы используем возвращаемый тип 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, и, поскольку это указатель void, мы должны ввести его преобразование в целочисленный тип данных, используя указатель определенной нотации (* datatype), и мы возвращаем значение куба.
- Мы объявляем операнд и переменную результата. Кроме того, мы инициализируем наш операнд значением «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 (* compare (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, а размер каждого элемента массива сохраняется в переменной ширины с использованием предопределенного оператора C. sizeof ().
- Мы вызываем функцию qsort и передаем имя массива, размер, ширину и функцию сравнения, определенную ранее пользователем, чтобы отсортировать наш массив в порядке возрастания. Сравнение будет выполняться, принимая на каждой итерации два элемента массива, пока не будет весь массив будут отсортированы.
- Мы печатаем элементы массива, чтобы убедиться, что наш массив хорошо отсортирован, путем итерации всего массива с помощью цикла for.