x210-2021-09-09
1、如果需要使用一个已有函数,但又不想使用其本身的函数名然后用自定义的函数名来替代,又或者像在STM32的IAP升级中用到的,需要跳转执行一个复位函数,但这个复位函数的入口保存在栈顶地址偏移4个字节之后的一个内存地址中,这些需求的共同点都是,想要使用这个函数的内容,但却不想要或者说使用不了原本的函数名,那这时,通过重新创建一个自定义的函数名,让其也指向这个函数的入口地址,不就可以了吗?但是需要注意的是,函数名字可以更换,但是函数的返回值和参数列表还是需要和原来函数保持一致,如图。
2、现在再对上述例子做进一步改动,不再用函数名printf赋给函数指针myprint,而是直接获取printf的地址然后赋给myprint,如图,当然这里有一个前提是没有增加或者减少其余代码,否则printf被链接到的地址也会随之发生变化,就做不了试验,另外需要说明的是,因为0x80482f0是一个地址,将其赋值给左边的myprint,第一步肯定是需要进行强制类型转换的,如果myprint只是一个普通的指针,它指向的是一个int,那么像平常那样写成myprint = (int *)0x80482f0肯定是没问题的,但是实际却是它没有指向一个int,而是指向一个返回值为int并且带有输入参数的函数,所以必须要写成myprint = (int *(const char*, ...))0x80482f0,(因为输入参数列表最重要的是参数类型而不是参数名,所以这里选择省略format不写,甚至可以将format换成别的名),不过写成这样就完了吗?和图中示例对比就能发现,这里的*少了()修饰,这样子对吗?先不看它对不对,首先看一个例子,有一个int *a,它的含义就是指针a指向一个int变量,又有一个int *b[10],它的含义就是b中的每一个元素都各自指向一个int变量,又又有一个int (*c)[10],它相当于int x[10],(因为()将*c括了起来,然后int和后面的[10]共同修饰),它的含义就是一个指针指向了带有10个元素的数组,这时候,再类推过去,就可以知道我们需要的是一个指向函数的指针(因为左边myprint就已经是一个指向printf函数的指针了),于是右边的地址0x80482f0的也应该是奔着一个能够指向函数的指针去才行,所以正确的写法应该是myprint = (int (*)(const char*, ...))0x80482f0这个。
3、注册:int (*p[7])(int a, int b),回调:int day = 0; p[day](9, 50);在注册中,可以理解成func *p[7],func再理解成带有返回值为int,两个输入参数也为int的函数,p[0]~p[6]各自存了一个指针(指向函数的入口地址),所以回调时只需要按下标进行索引并调用即可,不用再使用类似switch case这种条件判断逐个进行判断了,可以大大简化程序提高执行效率。