难以理解函数指针

trouble understanding function pointers

我很难理解函数指针在我正在使用的代码中的工作方式。

该代码描述了 NXP LPC1768 微控制器上闪存的 IAP(应用程序内编程)。

unsigned int flashParamTab[5];
unsigned int flashResultTab[5];

// IAP entry function
#define IAP_LOCATION    0x1FFF1FF1

// In Application Programming function entry
typedef void (*IAP)(unsigned int[], unsigned int[]);
IAP flashIapEntry = (IAP)IAP_LOCATION;

此代码用于各种函数,例如这两个:

// Prepare a sector for write operation.
void flashPrepareSectors(unsigned int startSec, unsigned int endSec){
   flashParamTab[0] = IAP_PREPARE_WRITE;
   flashParamTab[1] = startSec;
   flashParamTab[2] = endSec;
   flashIapEntry(flashParamTab, flashResultTab);
 }
// Erase a sector.
void flashEraseSectors(unsigned int startSec, unsigned int endSec) {
   flashParamTab[0] = IAP_ERASE_SECTORS;
   flashParamTab[1] = startSec;
   flashParamTab[2] = endSec;
   flashIapEntry(flashParamTab, flashResultTab);
 }

据我了解:

        typedef void (*IAP)(unsigned int[], unsigned int[]);

定义了一个 IAP 数据类型,其中 IAP 是一个指向函数的指针,该函数将两个整数数组作为参数,而不是return 随便什么。

下一步

    IAP flashIapEntry = (IAP)IAP_LOCATION;

声明了一个类型为 IAP 的变量 flashIapEntry,它是一个指向内存中特定位置的指针,其中包含一个函数的代码,该函数采用两个整数数组。

我真的搞不懂调用这个函数时会发生什么。 在我看来,在这两种情况下(准备和擦除扇区时)都调用了相同的函数(位于 0x1fff1ff1 的函数),唯一的区别是数组的第一个字段,flashParamTab[0],它定义了这个函数的行为。

我的理解正确吗?

正确。正如 this NXP 应用说明中所述,在您的示例中,flashIapEntry 成为指向函数的指针,该函数将两个整数数组作为参数,并且 returns 无效。这意味着您可以像调用 C 中的任何其他函数一样调用该函数,如下所示:

flashIapEntry(flashParamTab, flashResultTab);

“我真的搞不懂调用这个函数时会发生什么。”

将参数入栈,即当前程序执行位置,然后将程序执行指针设置为0x1FFF1FF1。 IAP 入口函数(位于 0x1FFF1FF1)要执行的命令由您在 flashParamTab 数组中设置的值定义,并且此函数产生的任何结果都将写入 flashResultTab 内存区域。最后,程序执行位置被设置为保存在堆栈中的值,堆栈指针被重置,程序流在调用 flashIapEntry() 后继续,允许您检查 flashResultTab 数组中的结果。

恩智浦控制器上的 IAP 库是硬编码的,ever-present 交付时 MCU 中已有库的源代码。此库未链接,因此您的编译器和链接器都不知道它。

IAP_LOCATION内存地址是函数所在的物理地址,NXP保证其具有一定的格式和调用约定。为了调用该函数,我们需要使用函数指针,因为实际函数在我们的源代码中的任何地方都不可用。

你可以这样做更具可读性(来自我曾经写过的 IAP 驱动程序):

typedef uint32_t iap_func_t (const uint32_t*, uint32_t*);

static iap_func_t* const iap_entry = (iap_func_t*) IAP_ADDRESS;

const 关键字确保函数指针存储在闪存中,所以这基本上就像我们正常声明函数一样。

然后根据您的需要使用不同的参数调用该函数。