嵌入式C语言基础知识 您所在的位置:网站首页 定义结构体指针C语言 嵌入式C语言基础知识

嵌入式C语言基础知识

2024-04-09 04:01| 来源: 网络整理| 查看: 265

目录

一. 函数指针: 什么是函数指针?

 函数指针的三种定义方式:

(1)先定义出函数的类型,再通过类型定义函数指针变量

(2)先定义出函数指针的类型,再通过指针类型定义函数指针变量

(3)重点:直接定义函数指针变量

 函数指针和指针函数的区别:

二. 回调函数

实例一:

实例二:实例一的具体实现。

实例三:固件开发中使用到的一个回调函数实例。

三. 结构体指针

示例1:结构体指针的使用

示例2:指向结构体变量的指针 & 结构体嵌套

示例3:结构体数组指针的使用

示例4:结构体指针作为函数参数

一. 函数指针: 什么是函数指针?

指向函数入口地址的指针。

如果在程序中定义了一个函数,那么编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址,函数名表示的就是这个地址。

既然是地址,就可以定义一个指针变量来存放,这个指针变量就叫做函数指针变量。

函数指针的定义:

# 返回值类型 +(指针变量名)(形参列表) eg: int (*p)(int, int);

 函数指针的三种定义方式: (1)先定义出函数的类型,再通过类型定义函数指针变量 //定义出一个函数类型,返回值是void,形参列表(int,char) typedef void(FUNC_TYPE)(int, char); FUNC_TYPE * pFunc = func; (2)先定义出函数指针的类型,再通过指针类型定义函数指针变量 typedef void(*FUNC_TYPE)(int, char); FUNC_TYPE pFunc = func; (3)重点:直接定义函数指针变量 void(*p) (int, char) = func;

解释:

定义了一个指针变量 p ,该指针变量可以指向返回值类型为 void,且有两个整型参数的函数。p 的类型为 void(*)(int, char)。

注意:

(*p)两端的括号不能去掉。如果省略了括号,就变成一个函数声明了,即声明了一个返回值类型为指针型的函数,即变成了指针函数。

 函数指针和指针函数的区别: # 指针函数本质是一个函数,其返回值是一个指针: int* p(int, int); # 函数指针本质是一个指针,其指向一个函数: int (*p)(int, int);

简单点就是:函数名带括号的就是函数指针,否则就是指针函数。

同理,数组指针和指针数组的区别:

# 数组指针是一个指针,指向一个数组: int (*p)[ ]; # 指针数组是一个数组,数组的元素都是指针: int* p[ ];

以下实例声明了函数指针变量p,指向函数func_max:

#include int func_max(int x, int y) { return x>y ? x:y; // ?:是条件运算符,如果条件为真,则值为 x ,否则值为 y。 } int main(void) { int (*p)(int, int)= &func_max; //p是函数指针,&可以省略 int a, b, c, d; printf("请输入三个数字:"); scanf("%d %d %d", &a, &b, &c); d=p(p(a,b),c); //与直接调用函数等价,d=max(max(a,b),c) printf("最大的数字是: %d\n", d); return 0; }

输出为:

请输入三个数字:1 2 3 最大的数字是: 3 二. 回调函数

函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数。

也就是,你写一个函数A,并把这个函数A的地址赋值给一个函数指针,然后把这个函数指针当作参数赋给另一个函数B,函数B通过函数指针来调用函数A。这个函数A就是回调函数。

为什么要使用回调函数呢?

答:解耦。

实例一: #include #include // 包含Library Function所在的Software library库的头文件 int Callback() // Callback Function { // TODO return 0; } int main() // Main program { // TODO Library(Callback); // TODO return 0; }

 仔细一看可以发现:在回调中,主程序把回调函数像参数一样传入库函数。这样我们只需改变传进库函数的参数,就可以实现不同的功能,并且丝毫不需要修改库函数的实现,这就是解耦。

实例二:实例一的具体实现。

回调函数的作用 

 比如,我们写A B C D 四个函数,封装成一个库文件,然后我们的主函数里面要写一个功能函数,这个功能要用到函数A,假如不用函数指针,这个功能函数就要调用函数A,下次如果用到函数B,那么我们得删掉A,调用函数B,每次都要修改这个函数很麻烦,但如果使用回调函数就不一样了,我们可以定义4个函数指针,把4个函数的地址分别赋给4个函数指针,然后将函数指针当作参数传递给功能函数,功能函数就可以通过修改参数来调用对应的函数,而它本身不用做任何的修改。这样的话,功能函数就可以根据不同的情况,通过函数指针去调用不同的函数,代码如下:

#include #include # 4个回调函数 float ADD(float a, float b) { return a + b; } float SUB(float a, float b) { return a - b; } float MUL(float a, float b) { return a * b; } float DIV(float a, float b) { return a / b; } # 4个函数指针 float (*A)(float x, float y) = ADD; float (*B)(float x, float y) = SUB; float (*C)(float x, float y) = MUL; float (*D)(float x, float y) = DIV; # 函数指针作为函数参数,以此来调用回调函数 float fun(float x, float y, float(*p)(float x, float y)) { return p(x, y); } int main() { printf("%f", fun(2, 3, A)); } 实例三:固件开发中使用到的一个回调函数实例。 /** \brief function pointer type to void/void function **/ typedef void (*func_ptr_t)(void); # 定义一个函数指针类型 /** \brief IRQ registration structure definition **/ typedef struct stc_irq_regi_conf { en_int_src_t enIntSrc; IRQn_Type enIRQn; func_ptr_t pfnCallback; # 定义一个函数指针变量 }stc_irq_regi_conf_t; /** \brief generic error codes **/ typedef enum en_result { Ok = 0u; Error = 1u; ErrorTimeout = 2u; ErrorNotReady = 3u; }en_result_t; /** * @brief Timer0 interrupt callbackfunc */ static void Timer0_CallBack(void) { //ToDo } void TIM0_Init(void) { stc_irq_regi_conf_t stcIrqRegiConf; /* Register TMR_INI_GCMA Int to Vect.No.001 */ stcIrqRegiConf.enIRQn = TIMER01_CHA_IRQn; /* Select I2C Error or Event interrupt function */ stcIrqRegiConf.enIntSrc = TMR_INI_GCMA; /* Callback function */ stcIrqRegiConf.pfnCallback =&Timer0_CallBack; # 将回调函数的地址赋给函数指针 /* Registration IRQ */ enIrqRegistration(&stcIrqRegiConf); } /** ******************************************************************************* ** \brief IRQ Registration ** ** param [in] pstcIrqRegiConf, IRQ registration ** configure structure ** ** retval Ok, IRQ register successfully. ** ErrorInvalidParameter, IRQ No. and ** Vector No. are not match. ** ErrorUninitialized, Make sure the ** Interrupt select register (INTSEL) is ** default value (0x1FFu) before setting. ** *****************************************************************************/ # 函数指针作为该函数参数,通过函数指针调用回调函数 en_result_t enIrqRegistration(const stc_irq_regi_conf_t *pstcIrqRegiConf) { //库函数IRQ Registration } 三. 结构体指针

当一个指针变量指向结构体时,就称它为结构体指针。其定义方式为:

struct 结构体名 *变量名;

定义结构体指针的 eg 如下:

typedef struct stu{ //定义结构体 char *name; int age; char group; float score; }stu; stu stu1 = {"Tom", 18,'A', 136.5}; stu *pstu = &stu1; //结构体指针

注意:

结构体变量名和数组名不同。数组名在表达式中会被转换为数组指针,而结构体变量名不会,要想取得结构体变量的地址,必须在前面加 & ,如下所示:

struct stu *pstu = &stu1;

还应该注意,结构体和结构体变量是两个不同的概念:结构体是一种数据类型,就像int、float这些关键字本身不占用内存一样;结构体变量才包含数据,才需要内存来存储。比如下面这种写法,是错误的,不可能去取一个结构体名的地址。

struct stu *pstu = &stu; 示例1:结构体指针的使用 #include typedef struct stu { char *name; int num; char group; float score; }stu, *pstu; int main() { stu stu1 = {"Tom", 12, 'A', 136.5}; pstu pstu1 = &stu1; printf("%s %d %c %.1f \n", stu1.name, stu1.num, stu1.group, stu1.score); printf("%s的学号是%d,在%c组,今年的成绩是%f.\n", pstu1->name, pstu1->num, pstu1->group, pstu1->score); return 0; }

输出为:

Tom的学号是12,在A组,今年的成绩是136.5。 示例2:指向结构体变量的指针 & 结构体嵌套 #include #include typedef struct AGE { int year; int month; int day; }age; typedef struct STUDENT { char name[20]; int num; age birthday; float score; }student; int main(void) { student student1; //用struct STUDENT结构体类型定义结构体变量student1 #if 0 student* p = NULL; //定义一个指向struct STUDENT结构体类型的指针变量p p = &student1; // *p指向结构体变量student1的首地址,即第一个成员的地址 #else student* p = &student1; #endif strcpy(p->name,"小明"); //(*p).name等价于student1.name p->birthday.year = 1994; p->birthday.month = 9; p->birthday.day = 18; p->num = 1207041; p->score = 100; printf("name: %s\n", p->name); printf("birthday: %d-%d-%d\n", p->birthday.year, p->birthday.month, p->birthday.day); printf("num: %d\n", p->num); printf("score: %.1f\n", p->score); return 0; }

输出为:

name: 小明 birthday: 1994-9-18 num: 1207041 score: 100.0 示例3:结构体数组指针的使用

如果定义一个结构体指针变量,并把结构体数组的数组名赋给这个指针变量的话,就意味着将结构体数组的第一个元素,即第一个结构体变量的地址,也即第一个结构变量中的第一个成员的地址赋给了这个指针变量。 

#include struct stu{ char *name; int num; char group; float score; }; int main(void) { struct stu stus[] = {{"jack", 5, 'A', 12.5},{"rose", 4, 'B', 123.5},{"paul", 3, 'C', 1234.5}}; struct stu *p = stus; //定义结构体数组指针p,并指向结构体数组的第一个元素即stus[0]. int len = sizeof(stus)/sizeof(struct stu); for(p = stus; p < stus+len; p++) // p+1 就指向stus[1]。 { printf("%s\t\t%d\t%c\t%f\n", p->name, p->num, p->group, p->score); } return 0; }

 输出为:

jack 5 A 12.500000 rose 4 B 123.500000 paul 3 C 1234.500000 示例4:结构体指针作为函数参数

如果结构体成员较多,尤其是成员为数组时,传送的时间和空间开销会很大,影响程序效率。所以最好的办法就是使用结构体指针。

eg:计算全班学生的总成绩、平均成绩、140分以下的人数。

#include typedef struct stu { char* name; int num; char group; float score; }stu; stu stus[] = { {"jack", 5, 'A', 12.5}, {"rose", 4, 'B', 123.5}, {"paul", 3, 'C', 1234.5} }; void average(struct stu* ps, int len); int main() { int len = sizeof(stus) / sizeof(struct stu); average(stus, len); return 0; } void average(struct stu* ps, int len) { int i, num_140 = 0; float average, sum = 0; for (i = 0; i < len; i++) { sum += (ps + i)->score; if ((ps + i)->score < 140) { num_140++; } } printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum / 5, num_140); }

输出为:

sum=1370.50 average=274.10 num_140=2



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有