C编程中的函数指针及示例
指针为‘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 100 m is 100, n is 25
该程序交换了实际变量的值,因为函数通过地址使用指针访问它们。在这里,我们将讨论程序过程。
- 我们声明负责交换两个变量值的函数,该函数接受两个整数指针作为参数,并在被调用时返回任何值。
- 在main函数中,我们声明并初始化了两个整数变量(‘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 语言中,我们可以返回一个指向数组的指针,如下面的程序所示。
#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);}
输出
1 2 3 4 5
在这里,我们将讨论程序细节。
- 我们定义并声明了一个返回包含整数值的数组地址的函数,并且不接受任何参数。
- 我们声明一个整数指针,它接收调用函数后构建的完整数组,并通过迭代整个包含五个元素的数组来打印其内容。
请注意,用于存储函数返回的数组地址的是指针,而不是数组。还要注意,当一个局部变量要从函数返回时,我们必须在函数中将其声明为 static。
函数指针
正如我们根据定义所知,指针指向任何内存位置的地址,它们也可以指向内存中可执行代码的开头,例如函数。
指向函数的指针用 * 声明,其声明的一般语句是
return_type (*function_name)(arguments)
您必须记住,函数名周围的括号(*function_name)很重要,因为没有它们,编译器会认为 function_name 返回的是 return_type 的指针。
定义函数指针后,我们必须将其分配给一个函数。例如,下一个程序声明了一个普通函数,定义了一个函数指针,将函数指针分配给普通函数,然后通过指针调用该函数。
#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");}
输出
Hi Hi Hi
- 我们定义并声明了一个标准的函数,该函数在被调用时,根据参数 times 指示打印 k 次 Hi 文本。
- 我们定义了一个函数指针(具有其特殊声明),它接受一个整数参数并且不返回任何内容。
- 我们将我们的函数指针初始化为 Hi_function,这意味着该指针指向 Hi_function()。
- 与通过键入函数名加参数的标准函数调用不同,我们仅通过传递数字 3 作为参数来调用函数指针,仅此而已!
请记住,函数名指向可执行代码的起始地址,就像数组名指向其第一个元素一样。因此,像 function_ptr = &Hi_function 和 (*funptr)(3) 这样的指令是正确的。
注意:在函数赋值和函数调用期间插入地址运算符 & 和间接运算符 * 并不重要。
函数指针数组
函数指针数组可以起到 switch 或 if 语句的作用来做决策,如下面的程序所示。
#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
在这里,我们讨论程序细节。
- 我们声明并定义了四个函数,它们接受两个整数参数并返回一个整数值。这些函数根据用户调用的函数对两个参数进行加、减、乘、除操作。
- 我们分别声明了 4 个整数来处理操作数、操作类型和结果。此外,我们还声明了一个包含四个函数指针的数组。数组中的每个函数指针元素都接受两个整数参数并返回一个整数值。
- 我们将每个数组元素分配并初始化为已声明的函数。例如,第三个元素,也就是第三个函数指针,将指向乘法运算函数。
- 我们从用户键盘输入中获取操作数和操作类型。
- 我们使用参数调用相应的数组元素(函数指针),并将生成的相应函数结果存储起来。
指令 int (*ope[4])(int, int); 定义了函数指针数组。每个数组元素必须具有相同的参数和返回类型。
语句 result = ope[choice](x, y); 根据用户选择运行相应的函数。输入的两个整数是传递给函数的参数。
使用 void 指针的函数
Void 指针在函数声明中使用。我们使用 void * 返回类型允许返回任何类型。如果我们假设我们的参数在传递给函数时不会改变,我们将其声明为 const。
例如
void * cube (const void *);
考虑以下程序
#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;}
结果
4 cubed is 64
在这里,我们将讨论程序细节。
- 我们定义并声明了一个返回整数值的函数,该函数接受一个不可更改变量的地址,而没有指定数据类型。我们计算了 num 指针指向的内容变量 (x) 的立方值,由于它是 void 指针,我们必须使用特定的表示法 (* datatype) 指针将其类型转换为整数数据类型,然后返回立方值。
- 我们声明了操作数和结果变量。此外,我们将操作数初始化为值“4”。
- 我们通过传递操作数地址来调用 cube 函数,并在结果变量中处理返回值。
函数指针作为参数
通过将函数指针作为参数传递给另一个函数来利用函数指针的另一种方式,有时称为“回调函数”,因为接收函数会“调用它”。
在 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,当 arg1 在 arg2 之前时返回 <0,当 arg1 在 arg2 之后时返回 >0。
以下程序使用 qsort() 函数将整数数组从小到大排序。
#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;}
结果
13 14 48 50 52
在这里,我们将讨论程序细节。
- 我们定义了 compare 函数,该函数由两个参数组成,当参数值相同时返回 0,当 arg1 在 arg2 之前时返回 <0,当 arg1 在 arg2 之后时返回 >0。参数是 void 指针类型,已强制转换为适当的数组数据类型(整数)。
- 我们定义并初始化了一个整数数组。数组大小存储在 num 变量中,每个数组元素的大小使用 sizeof() 预定义C 运算符存储在 width 变量中。
- 我们调用 qsort 函数,并传递数组名、大小、宽度以及用户先前定义的比较函数来对数组进行升序排序。在每次迭代中,将对两个数组元素进行比较,直到整个数组排序完成。
- 我们打印数组元素,以确保数组已正确排序,方法是使用for 循环迭代整个数组。