Указатели на функции в программировании на C с примерами

Содержание:

Anonim

Указатели предоставляют большие возможности функциям "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

Программа меняет местами фактические значения переменных, потому что функция обращается к ним по адресу с помощью указателей. Здесь мы обсудим программный процесс:

  1. Мы объявляем функцию, отвечающую за обмен двух значений переменных, которая принимает два целочисленных указателя в качестве параметров и возвращает любое значение при вызове.
  2. В основной функции мы объявляем и инициализируем две целочисленные переменные ('m' и 'n'), а затем выводим их значения соответственно.
  3. Мы вызываем функцию swap (), передавая адрес двух переменных в качестве аргументов с помощью символа амперсанда. После этого мы печатаем новые поменяемые местами значения переменных.
  4. Здесь мы определяем содержимое функции swap (), которая принимает в качестве параметров адреса двух целочисленных переменных, и объявляем временную целочисленную переменную, используемую в качестве третьего ящика для хранения одной из переменных значений, которые будут помещены во вторую переменную.
  5. Сохраните содержимое первой переменной, обозначенной буквой «a», во временной переменной.
  6. Сохраните вторую переменную, на которую указывает b, в первую переменную, на которую указывает a.
  7. Обновите вторую переменную (обозначенную 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

Здесь мы объясним программный код с его деталями.

  1. Мы объявляем и определяем функцию add_array (), которая принимает в качестве параметров адрес массива (указатель) с количеством его элементов и возвращает общее накопленное суммирование этих элементов. Указатель используется для итерации элементов массива (с использованием обозначения p [k]), и мы накапливаем сумму в локальной переменной, которая будет возвращена после итерации всего массива элементов.
  2. Мы объявляем и инициализируем целочисленный массив с пятью целочисленными элементами. Мы печатаем общую сумму, передавая имя массива (который действует как адрес) и размер массива в вызываемую функцию add_array () в качестве аргументов.

Функции, возвращающие массив

В C мы можем вернуть указатель на массив, как в следующей программе:

#include 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);}

Выход:

12345

И здесь мы обсудим детали программы.

  1. Мы определяем и объявляем функцию, которая возвращает адрес массива, содержащий целочисленное значение, и не принимает никаких аргументов.
  2. Мы объявляем целочисленный указатель, который получает полный массив, построенный после вызова функции, и печатаем его содержимое путем итерации всего массива из пяти элементов.

Обратите внимание, что для хранения адреса массива, возвращаемого функцией, определен указатель, а не массив. Также обратите внимание, что когда локальная переменная возвращается из функции, мы должны объявить ее как статическую в функции.

Указатели функций

Как мы знаем по определению, указатели указывают на адрес в любой области памяти, они также могут указывать на начало исполняемого кода как функции в памяти.

Указатель на функцию объявляется с помощью *, общая формулировка его объявления:

return_type (*function_name)(arguments)

Вы должны помнить, что круглые скобки вокруг (* function_name) важны, потому что без них компилятор будет думать, что function_name возвращает указатель return_type.

После определения указателя функции мы должны присвоить его функции. Например, следующая программа объявляет обычную функцию, определяет указатель функции, назначает указатель функции на обычную функцию и после этого вызывает функцию через указатель:

#include 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");} 

Выход:

HiHiHi

  1. Мы определяем и объявляем стандартную функцию, которая печатает текст Hi k раз, указываемый параметром times, когда функция вызывается.
  2. Мы определяем функцию-указатель (с ее специальным объявлением), которая принимает целочисленный параметр и ничего не возвращает.
  3. Мы инициализируем нашу функцию указателя с помощью Hi_function, что означает, что указатель указывает на Hi_function ().
  4. Вместо того, чтобы вызывать стандартную функцию с помощью записи имени функции аргументами, мы вызываем только функцию-указатель, передавая число 3 в качестве аргументов, и все!

Имейте в виду, что имя функции указывает на начальный адрес исполняемого кода, например имя массива, указывающее на его первый элемент. Следовательно, такие инструкции, как function_ptr = & Hi_function и (* funptr) (3), верны.

ПРИМЕЧАНИЕ. Не важно вставлять адресный оператор & и оператор косвенного обращения * во время присвоения функции и вызова функции.

Массив указателей на функции

Массив указателей на функции может играть роль переключателя или оператора if для принятия решения, как в следующей программе:

#include 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 48Enter 0 to sum, 1 to subtract, 2 to multiply, or 3 to divide: 2624

Здесь мы обсуждаем детали программы:

  1. Мы объявляем и определяем четыре функции, которые принимают два целочисленных аргумента и возвращают целое значение. Эти функции складывают, вычитают, умножают и делят два аргумента относительно того, какая функция вызывается пользователем.
  2. Мы объявляем 4 целых числа для обработки операндов, типа операции и результата соответственно. Также мы объявляем массив из четырех указателей на функции. Каждый указатель на функцию элемента массива принимает два целочисленных параметра и возвращает целочисленное значение.
  3. Мы назначаем и инициализируем каждый элемент массива уже объявленной функцией. Например, третий элемент, который является указателем третьей функции, будет указывать на функцию операции умножения.
  4. Мы ищем операнды и тип операции у пользователя, набирающего текст с клавиатуры.
  5. Мы вызвали соответствующий элемент массива (указатель на функцию) с аргументами и сохраняем результат, сгенерированный соответствующей функцией.

Инструкция int (* ope [4]) (int, int); определяет массив указателей на функции. Каждый элемент массива должен иметь одинаковые параметры и возвращаемый тип.

Выражение result = ope [выбор] (x, y); запускает соответствующую функцию в соответствии с выбором, сделанным пользователем. Два введенных целых числа являются аргументами, переданными функции.

Функции, использующие указатели void

Указатели на пустоту используются во время объявления функций. Мы используем возвращаемый тип void *, позволяющий возвращать любой тип. Если мы предполагаем, что наши параметры не меняются при переходе к функции, мы объявляем это как const.

Например:

 void * cube (const void *); 

Рассмотрим следующую программу:

#include 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;}

Результат:

 4 cubed is 64 

Здесь мы обсудим детали программы:

  1. Мы определяем и объявляем функцию, которая возвращает целочисленное значение и принимает адрес неизменяемой переменной без определенного типа данных. Мы вычисляем значение куба переменной содержимого (x), на которое указывает указатель num, и, поскольку это указатель void, мы должны ввести его преобразование в целочисленный тип данных, используя указатель определенной нотации (* datatype), и мы возвращаем значение куба.
  2. Мы объявляем операнд и переменную результата. Кроме того, мы инициализируем наш операнд значением «4».
  3. Мы вызываем функцию куба, передавая адрес операнда, и обрабатываем возвращаемое значение в переменной результата.

Указатели на функции как аргументы

Другой способ использовать указатель функции, передав его в качестве аргумента другой функции, иногда называемой «функцией обратного вызова», потому что функция-получатель «вызывает ее обратно».

В файле заголовка 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 

Здесь мы обсудим детали программы:

  1. Мы определяем функцию сравнения, состоящую из двух аргументов и возвращающую 0, когда аргументы имеют одинаковое значение, <0, когда arg1 идет до arg2, и> 0, когда arg1 идет после arg2. Параметры представляют собой тип указателей void, приведенный к соответствующему типу данных массива (целое число)
  2. Мы определяем и инициализируем целочисленный массив. Размер массива сохраняется в переменной num, а размер каждого элемента массива сохраняется в переменной ширины с использованием предопределенного оператора C. sizeof ().
  3. Мы вызываем функцию qsort и передаем имя массива, размер, ширину и функцию сравнения, определенную ранее пользователем, чтобы отсортировать наш массив в порядке возрастания. Сравнение будет выполняться, принимая на каждой итерации два элемента массива, пока не будет весь массив будут отсортированы.
  4. Мы печатаем элементы массива, чтобы убедиться, что наш массив хорошо отсортирован, путем итерации всего массива с помощью цикла for.