1.函数指针
-
1.1 函数类型
-
通过什么来区分两个不同的函数?
- 一个函数在编译时被分配一个入口地址,这个地址就称为函数的指针,函数名代表函数的入口地址。
- 函数三要素: 名称、参数、返回值。C语言中的函数有自己特定的类型。
c语言中通过typedef为函数类型重命名:
typedef int f(int, int); // f 为函数类型
typedef void p(int); // p 为函数类型
- 这一点和数组一样,因此我们可以用一个指针变量来存放这个入口地址,然后通过该指针变量调用函数。
注意:通过函数类型定义的变量是不能够直接执行,因为没有函数体。只能通过类型定义一个函数指针指向某一个具体函数,才能调用。
typedef int(p)(int, int);
void my_func(int a,int b){
printf("%d %d\n",a,b);
}
void test(){
p p1;//p1(10,20); //错误,不能直接调用,只描述了函数类型,但是并没有定义函数体,没有函数体无法调用
p* p2 = my_func;
p2(10,20); //正确,指向有函数体的函数入口地址
}
-
1.2 函数指针(指向函数的指针)

- 函数指针定义方式(先定义函数类型,根据类型定义指针变量);
- 先定义函数指针类型,根据类型定义指针变量;
- 直接定义函数指针变量;
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void func(int a ,char c)
{
printf("hello world\n");
}
void test01()
{
//1、先定义出函数类型,再通过类型定义函数指针
typedef void(FUNC_TYPE)(int, char);
FUNC_TYPE * pFunc = func;
//pFunc(10, 'a');
//2、定义出函数指针类型,通过类型定义函数指针变量
typedef void( * FUNC_TYPE2)(int, char);
FUNC_TYPE2 pFunc2 = func;
//pFunc2(20, 'b');
//3、直接定义函数指针变量
void(*pFunc3)(int, char) = func;
pFunc3(30, 'c');
//函数指针 和 指针函数 区别?
//函数指针 指向了函数的指针
//指针函数 函数返回值是指针的函数
}
//函数指针的数组
void func1()
{
printf("func1 调用了\n");
}
void func2()
{
printf("func2 调用了\n");
}
void func3()
{
printf("func3 调用了\n");
}
void test02()
{
void(*pArray[3])();
pArray[0] = func1;
pArray[1] = func2;
pArray[2] = func3;
for (int i = 0; i < 3;i++)
{
pArray[i]();
}
}
int main()
{
//test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
-
1.3 函数指针数组
函数指针数组,每个元素都是函数指针。
void func01(int a){
printf("func01:%d\n",a);
}
void func02(int a){
printf("func02:%d\n", a);
}
void func03(int a){
printf("func03:%d\n", a);
}
void test(){
#if 0
//定义函数指针
void(*func_array[])(int) = { func01, func02, func03 };
#else
void(*func_array[3])(int);
func_array[0] = func01;
func_array[1] = func02;
func_array[2] = func03;
#endif
for (int i = 0; i < 3; i ++){
func_array[i](10 + i);
(*func_array[i])(10 + i);
}
}
2. 函数指针做函数参数(回调函数)
函数参数除了是普通变量,还可以是函数指针变量。
//形参为普通变量
void fun( int x ){}
//形参为函数指针变量
void fun( int(*p)(int a) ){}
函数指针变量常见的用途之一是把指针作为参数传递到其他函数,指向函数的指针也可以作为参数,以实现函数地址的传递。
- 函数指针做函数参数.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//提供一个打印函数,可以打印任意类型的数据
void printText( void * data , void(*myPrint)(void *) )
{
myPrint(data);
}
void myPrintInt(void * data)
{
int * num = data;
printf("%d\n", *num);
}
void test01()
{
int a = 10;
printText(&a, myPrintInt);
}
struct Person
{
char name[64];
int age;
};
void myPrintPerson(void * data)
{
struct Person * p = data;
printf("姓名: %s 年龄: %d\n", p->name, p->age);
}
void test02()
{
struct Person p = { "Tom", 18 };
printText(&p, myPrintPerson);
}
int main(){
//test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
int plus(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int division(int a, int b)
{
return a / b;
}
//函数指针 做函数的参数 --- 回调函数
void Calculator(int(*myCalculate)(int, int), int a, int b)
{
int ret = myCalculate(a, b); //dowork中不确定用户选择的内容,由后期来指定运算规则
printf("ret = %d\n", ret);
}
void test01()
{
printf("请输入操作符\n");
printf("1、+ \n");
printf("2、- \n");
printf("3、* \n");
printf("4、/ \n");
int select = -1;
scanf("%d", &select);
int num1 = 0;
printf("请输入第一个操作数:\n");
scanf("%d", &num1);
int num2 = 0;
printf("请输入第二个操作数:\n");
scanf("%d", &num2);
switch (select)
{
case 1:
Calculator(plus, num1, num2);
break;
case 2:
Calculator(sub, num1, num2);
break;
case 3:
Calculator(mul, num1, num2);
break;
case 4:
Calculator(division, num1, num2);
break;
default:
break;
}
}
- 回调函数案例.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//提供一个函数,实现可以打印任意类型的数组
void printAllArray(void * pArray , int eleSize, int len , void(*myPrint)(void*) )
{
char * p = pArray;
for (int i = 0; i < len;i++)
{
//获取数组中每个元素的首地址
char * eleAddr = p + eleSize * i;
//printf("%d\n", *(int *)eleAddr);
//交还给用户做打印操作
myPrint(eleAddr);
}
}
void myPrintInt(void * data)
{
int * num = data;
printf("%d\n", *num);
}
void test01()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int len = sizeof(arr) / sizeof(int);
printAllArray(arr, sizeof(int), len, myPrintInt);
}
struct Person
{
char name[64];
int age;
};
void myPrintperson(void * data)
{
struct Person * p = data;
printf("姓名:%s 年龄:%d \n", p->name, p->age);
}
//查找数组中的元素是否存在
//参数1 数组首地址 参数2 每个元素的大小 参数3 数组元素个数 参数4 查找数据
int findArrayEle(void * pArray, int eleSize, int len, void * data , int(*myCompare)(void* ,void* ) )
{
char * p = pArray;
for (int i = 0; i < len;i++)
{
//每个元素的首地址
char * eleAddr = p + eleSize * i;
//if ( 数组中的变量的元素 == 用户传入的元素)
if ( myCompare(eleAddr,data) )
{
return 1;
}
}
return 0;
}
int myComparePerson(void * data1,void * data2)
{
struct Person * p1 = data1;
struct Person * p2 = data2;
//if ( strcmp( p1->name , p2->name) == 0 && p1->age == p2->age)
//{
// return 1;
//}
//return 0;
return strcmp(p1->name, p2->name) == 0 && p1->age == p2->age;
}
void test02()
{
struct Person personArray[] =
{
{ "aaa", 10 },
{ "bbb", 20 },
{ "ccc", 30 },
{ "ddd", 40 },
};
int len = sizeof(personArray) / sizeof(struct Person);
printAllArray(personArray, sizeof(struct Person), len, myPrintperson);
//查找数组中指定的元素是否存在
struct Person p = { "ccc", 30 };
int ret = findArrayEle(personArray, sizeof(struct Person), len, &p, myComparePerson);
if (ret)
{
printf("找到了元素\n");
}
else
{
printf("未找到\n");
}
}
int main(){
//test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
注意:函数指针和指针函数的区别:
- 函数指针是指向函数的指针;
- 指针函数是返回类型为指针的函数;