目录
一、概述
1.1 简介
本文主要讲解函数指针及用法,同时实例讲解结构体和枚举。
函数指针是指向函数的指针变量。 因此“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。
1.2 简单示例
int Func(int x);//这一句是声明一个函数,是我们要被函数指针调用的函数,而且函数名表示函数的首地址。
int (*p) (int x);//这里定义了一个函数指针
int (*p) (int x);
//里的int指的是函数的返回值,指要被调用的函数的返回值是整形。
//里的*p指的是指向函数首地址的指针变量。
//(int x)表示被调用的函数参数是一个且是整形。
p = Func;//这一句是把被调函数的首地址存在定义的指针变量里,用来指向被调函数。
二、实例操作
2.1 定义一个函数指针
typedef int (*stFunc_t)(stateCtx_t * st_ctx); //typedef可以使用stFunc_t去申明一个函数指针
2.2 使用数组将多个函数指针存放
const stFunc_t _CleanShutDownStateFuncs[SHUTDOWN_STEP_NUM] =
{
ShutDownPrepare,
ShutDownStopUsrService,
ShutDownStopLowTask,
ShutDownAdjustSysTask
};
具体函数定义如下:
int ShutDownPrepare(stateCtx_t * st_ctx){
#TODO
}
2.3 使用枚举确定数组成员值
typedef enum _MediaShutDownState_t
{
SHUTDOWN_PREPARE = 0,
SHUTDOWN_STOP_USER_SERVICE,
SHUTDOWN_STOP_LOW_TASK,
SHUTDOWN_ADJUST_SYS_TASK,
SHUTDOWN_STEP_NUM
}MediaShutDownState_t;
2.4 定义一个结构体
typedef struct _stateMachine_t
{
uint32_t sig; // state machine signature, debugging purpose
const stFunc_t *funcs; //成员申明一个函数指针
uint32_t nrSteps; // # states
} stateMachine_t;
对结构体进行赋值
const stateMachine_t _CleanShutDownStm =
{
INT_CHR('M', 'C', 'S', 'D'),
_CleanShutDownStateFuncs,//将最开始的指针数组传入(每个成员指向设置的函数 )
ARRAY_SIZE(_CleanShutDownStateFuncs)
};
2.5 定义一个联合体
联合是一种特殊的自定义类型,该种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间,所以联合体也被称为共用体。
如下该联合体占用32 bit
/// @brief 0x244000C4 : CPU interrupt mode
typedef union
{
uint32_t all;///< All bits.
struct
{
uint32_t CIF_INT_MODE : 1; ///<BIT [0]: RW (0x0). 1'b0: single interrupt; 1'b1: multiple interrupt for every port.
uint32_t R244000C4_31_1_RSVD :31; ///<BIT [31:1]: Reserved.
} b;
} SataCifIntMode_t;
SataCifIntMode_t cifIntMode.all = (SataCifIntMode_t)
{ .b = { .CIF_INT_MODE = 0x0}
}.all;
其他赋值方式
typedef union _nr_iv_info_tab_1
{
u32 w;
struct
{
rw32 cd : 1;
rw32 msi_en : 1;
rw32 msix_en : 1;
rw32 msix_fm : 1;
rw32 ivmask : 1;
rw32 rsv5 : 27;
};
} nr_iv_info_tab_1;
nr_iv_info_tab_1 info1 = (nr_iv_info_tab_1){ .cd = 1, .msi_en = 0, .msix_en = 0, .msix_fm = 0, .ivmask = 0 };
2.6 指针数组
实际为一个数组,存放的是一个指针,指针指向char类型的字符串
char * p[] = {
"start",
"end",
NULL,
};
printf("the p[0]:%s,p[1]:%s\n",p[0],p[1]);
//注意:该指针数组中每个元素(即指针)存放的地址为&p[0],&p[1],二级指针用到
//输出如下
the p[0]:start,p[1]:end
//p[1]表示该数组中的第一个元素的指针指向的字符串
2.6.1 二级指针的使用
该指针指向的类型是一个指针,最后指向一个char类型的字符串
char ** p;
p = &p[0]; //将p赋值一个指针的存放地址,p[0]为一个指针,指向"start"字符串
printf("the p:%s\n",*p);
**p = "hello"; //给p[0]赋值为hello
三、完整程序讲解
3.1 指针综合示例一
#include <stdio.h>
int main() {
int a = 10;
int *p = &a;
int **pp = &p;
printf("a=%d\n",a); // 打印a的值
printf("&a=%p\n",&a); // 打印a的地址
printf("p=%p\n",p); // 打印p的值,即a的地址
printf("*p=%d\n",*p); // 打印p所指向的变量的值,即a的值
printf("&p=%p\n",&p); // 打印p的地址
printf("*pp=%p\n",*pp); // 打印pp所指向的指针变量p的值,即a的地址
printf("**pp=%d\n",**pp); // 打印pp所指向的指针变量p所指向的变量的值,即a的值
printf("&pp=%p\n",&pp); // 打印pp的地址
printf("*&p=%p\n",*&p); // 打印p的值,即a的地址
printf("&*p=%p\n", &p); // 打印p的地址
return 0;
}
结果:
a=10
&a=0x7fff8ac8b2b4
p=0x7fff8ac8b2b4
*p=10
&p=0x7fff8ac8b2b8
*pp=0x7fff8ac8b2b4
**pp=10
&pp=0x7fff8ac8b2c0
*&p=0x7fff8ac8b2b4
&*p=0x7fff8ac8b2b8
进阶版:
#include <stdio.h>
int main() {
//int a = 10;
char *a = "acb";
char *p = a;
char **pp = &p;
printf("a=%s\n",a); // 打即指针a所指向的值,acb
printf("a=%p\n",a); // 打印acb的地址
printf("&a=%p\n",&a); // 打印a的地址
printf("p=%p\n",p); // 打印p的值,即acb的地址
printf("*p=%s\n",p); // 打p所指向的变量的值,即acb
printf("&p=%p\n",&p); // 打印指针p的地址
printf("pp=%p\n",pp); // 打印pp所指向的指针变量p的地址
printf("*pp=%p\n",*pp); // 打印pp所指向的指针变量p的值,即acb的地址
printf("**pp=%s\n",*pp); // 打pp所指向的指针变量p所指向的变量的值,即acb的值
printf("&pp=%p\n",&pp); // 打pp的地址
printf("*&p=%p\n",*&p); // 打印p的值,即acb的地址
printf("&*p=%p\n", &*p); // 打印acb的地址
return 0;
}
结果:
注意打印类型%p为以16进制打印传入地址存放的值,%s为打印传入的首地址的字符串
a=acb
a=0x55a4d76eb004
&a=0x7ffed2c2db70
p=0x55a4d76eb004
*p=acb
&p=0x7ffed2c2db78
pp=0x7ffed2c2db78
*pp=0x55a4d76eb004
**pp=acb
&pp=0x7ffed2c2db80
*&p=0x55a4d76eb004
&*p=0x55a4d76eb004
3.2 关于函数地址的示例二
void fun()
{
}
int main(void)
{
printf("%p %p %p\n", &fun, fun, *fun);
return 0;
}
这三个值的结果是一样的. 其实对于最后的那个*fun, 即使前面加上很多个*号, 其结果也不变, 即**fun, ***fun的结果都是一样的.
对于这个问题, 因为之前讲过函数是一种function-to-pointer方式, 其会自动转换成指针的类型, &fun是该函数的地址, 为指针类型, fun是一个函数, 会转换成其指针类型, 而对于*fun, 由于fun已经变成了指针类型, 指向这个函数, 所以*fun就是取这个地址的函数, 而又根据function-to-pointer, 该函数也转变成了一个指针, 所以以此类推, 这三个值的结果是相同的。
3.3 函数指针的嵌套运用
#include <stdio.h>
void test(int);
typedef void (*fp1)(int); //申明一个函数指针
int main(void){
const fp1 fp=test; //指针指向test函数
printf( "the fp is %x\n", fp);
(*fp)(9);//调用test函数,传入参数为9
fp(9);
const fp1 *fp2;//申明另外的函数指针
const fp1 *fp3;//申明另外的函数指针
fp3=test;//指针指向test函数
printf( "the fp2 is %x\n,the fp2 %x\n", fp2,fp3);//打印fp2,fp3指向的函数的地址值
return 0;
}
struct test_fp{
const fp1 fp;
};
void test(int a)
{
printf( "%d\n", a );
struct test_fp fp4;
printf( "the fp4 is %x\n", fp4);
}
打印结果:
the fp * is e3a2d1c2
9
the fp4 is 0
9
the fp4 is 0
the fp2 is 0
,the *fp2 e3a2d1c2
解析:
1、fp1 、*fp和fp1 fp都是定义一个函数指针,赋值的话只能fp=test;
2、const fp1 fp;定义的函数指针地址都为0;
3、程序中的 fp( 9 ); 是直接使用函数指针 fp来调用。既然 fp已经是函数指针了,所以在类型上就不需要任何转换了。
4、(*fp)( 9 ),也是合法的函数调用。在这里,fp 是函数指针,所以*fp 是对于函数的引用,是函数类型。根据标准规定的 function-to-pointer 转换又把 *fp 由函数类型转换为了函数指针类型,所以实际上 (*fp)( 9 ),相当于 fp( 9 ),这种直接的函数指针调用方式。
5、test 函数也可这样调用:(*test)( 9 );。可以这样来理解:根据 function-to-pointer 转换规定,首先 test 由函数类型转换为函数指针,那么 *test 表示的又是函数类型,
最后又根据 function-to-pointer 转换为函数指针来调用函数。这其实和 (*fp)( 9 ); 是等价的。甚至 test 函数还可以这样调用:(**test)( 9 );、(***test)( 9 );、(****test)( 9 ); 等等,
或者函数指针形式:(**fp)( 9 );、(***fp)( 9 );、(****fp)( 9 ); 等等。对此的理解可参看上段中的分析。
从上面的分析可以看出,函数调用的时候可以使用函数指针的方式,也可以使用函数指示符的方式。不过,后者会由编译器自动转换为前者的形式,即函数指针的形式。和指向对象的指针相比,这是函数指针一个比较特殊的地方。