1、函数指针实战
1.1、用函数指针调用执行函数
用函数指针指向不同的函数来实现同一个调用执行不同的结果
#include <stdio.h>
int add(int a, int b);
int sub(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);
// 定义了一个类型pFunc,这个函数指针类型指向一种特定参数列表和返回值的函数
typedef int (*pFunc)(int, int);
int main(void)
{
pFunc p1 = NULL;
char c = 0;
int a = 0, b = 0, result = 0;
printf("请输入要操作的2个整数:\n");
scanf("%d %d", &a, &b);
printf("请输入操作类型:+ | - | * | /\n");
do
{
scanf("%c", &c);
}while (c == '\n');
switch (c)
{
case '+':
p1 = add; break;
case '-':
p1 = sub; break;
case '*':
p1 = multiply; break;
case '/':
p1 = divide; break;
default:
p1 = NULL; break;
}
result = p1(a, b);
printf("%d %c %d = %d.\n", a, c, b, result);
return 0;
}
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int multiply(int a, int b)
{
return a * b;
}
int divide(int a, int b)
{
return a / b;
}
1.2、用结构体内嵌函数指针实现分层
主题:结构体内嵌函数指针实现分层
(1)程序分层——复杂程序的设计
(2)本程序要完成一个计算器,设计了2个层次:上层为framework.c,实现应用程序框架;下层为cal.c,实现计算器。实际工作时cal.c是直接完成巩固走的,但是cal.c的关键部分是调用framework.c中的函数来完成的。
(3)先写framework.c,由一个完成,这个人在framework.c中需要完成计算器的业务逻辑,并且将相对应的接口写在对应的头文件中发出来,将来别的层次的人用这个头文件来协同工作。
(4)另一个人来完成cal.c,实现具体的计算器,这个人需要framework层的工作人员来提供头文件,但是不需要framework.c文件。
(5) 上层代码注重业务逻辑,与我们最终的目标直接关联,而没有具体干活的函数。
(6)下层代码核心就是一个结构体变量(譬如本题中的struct cal_t)的逻辑:第一步先定义结构体变量;第二部填充结构体变量;第三步调用上层写好的接口函数,把结构体变量传给它即可。
//cal.h头文件
#ifndef __CAL_H__
#define __CAL_H__
typedef int (*pFunc)(int, int);
// 结构体是用来做计算器的,计算器工作时需要计算原材料
struct cal_t
{
int a;
int b;
pFunc p;
};
// 函数原型声明
int calculator(const struct cal_t *p);
#endif
//franmework.c文件
#include "cal.h"
// framework.c中应该写实际业务关联的代码
// 计算器函数
int calculator(const struct cal_t *p)
{
return p->p(p->a, p->b);
}
//cal.c文件
#include "cal.h"
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int multiply(int a, int b)
{
return a * b;
}
int divide(int a, int b)
{
return a / b;
}
int main(void)
{
int ret = 0;
struct cal_t myCal;
myCal.a = 12;
myCal.b = 4;
myCal.p = divide;
ret = calculator(&myCal);
printf("ret = %d.\n", ret);
return 0;
}
1.3.再论typedef
1.3.1、C语言的2种类型:内建类型与用户自定义类型
(1)内建类型ADT、自定义类型UDT
1.3.2、typedef定义(或者叫重命名)类型而不是变量
(1)类型是一个数据模板,变量是一个实在的数据。类型是不占内存的,而变量是占内存的。
(2)面向对象的语言中:类型就是类class,变量就是对象。
1.3.3、typedef与#define宏的区别
typedef char *pChar;
#define pChar char *
1.3.4、typedef与结构体
(1)结构体在使用时都是先定义结构体类型,再用结构体类型去定义变量。
(2)C语言语法规定,结构体类型使用时必须是struct 结构体类型名 结构体变量名;这样的方式来定义变量。
(3)使用typedef一次定义2个类型,分别是结构体变量类型,和结构体变量指针类型。
练习题目:结构体的使用
#include <stdio.h>
// 结构体类型的定义
/*
struct student
{
char name[20];
int age;
};
*/
// 定义了一个结构体类型,这个类型有2个名字:第一个名字是struct student,第二个类型名叫student_t
/*
typedef struct student
{
char name[20];
int age;
}student_t;
*/
// 第一个类型名:struct student,第二个类型名是student
typedef struct student
{
char name[20];
int age;
}student;
// 我们一次定义了2个类型:
// 第一个是结构体类型,有2个名字:struct teacher,teacher
// 第二个是结构体指针类型,有2个名字:struct teacher *, pTeacher
typedef struct teacher
{
char name[20];
int age;
int mager;
}teacher, *pTeacher;
typedef int *PINT;
typedef const int *CPINT;
// const int *p和int *const p是不同的。前者是p指向的变量是const,后者是p本身const
int main(void)
{
int a = 23;
int b = 11;
CPINT p = &a;
*p = 33; // error: assignment of read-only location ‘*p’
p = &b;
/*
PINT const p = &a;
*p = 33;
p = &b; // error: assignment of read-only variable ‘p’
*/
/*
PINT p1 = &a;
const PINT p2 = &a; // const int *p2; 或者 int *const p2;
*p2 = 33;
printf("*p2 = %d.\n", *p2);
p2 = &b; // error: assignment of read-only variable ‘p2’
*/
/*
teacher t1;
t1.age = 23;
pTeacher p1 = &t1;
printf("teacher age = %d.\n", p1->age);
struct student *pS1; // 结构体指针
student *pS2; // 同上
*/
/*
struct student s1; // struct student是类型;s1是变量
s1.age = 12;
student s2;
*/
return 0;
}
1.3.5、typedef与const
(1)typedef int *PINT; const PINT p2; 相当于是int *const p2;
(2)typedef int *PINT; PINT const p2; 相当于是int *const p2;
(3)如果确实想得到const int *p;这种效果,只能typedef const int *CPINT; CPINT p1;
1.3.6、使用typedef的重要意义(2个:简化类型、创造平台无关类型)
(1)简化类型的描述。
char *(*)(char *, char *); typedef char *(*pFunc)(char *, char *);
(2)很多编程体系下,人们倾向于不使用int、double等C语言内建类型,因为这些类型本身和平台是相关的(譬如int在16位机器上是16位的,在32位机器上就是32位的)。为了解决这个问题,很多程序使用自定义的中间类型来做缓冲。譬如linux内核中大量使用了这种技术.
内核中先定义:typedef int size_t; 然后在特定的编码需要下用size_t来替代int(譬如可能还有typedef int len_t)
(3)STM32的库中全部使用了自定义类型,譬如typedef volatile unsigned int vu32;