IAR 编译器:使用带函数指针的结构,映射文件显示结构中声明的所有函数,无论是否使用函数

IAR Compiler: using struct with function pointers, map file shows all the function declared in struct irrespective of function used or not

我有一个包含 10 个函数指针的结构。该结构的全局实例使用指向局部函数的函数指针进行初始化。 在我的应用程序中,使用任何一个或多个函数引用都会导致链接器在映射文件中包含所有 10 个函数。

为了节省内存,我需要链接器只包含应用程序中使用的那些函数。并非结构中引用的所有函数。

文件中的结构 r_adc_api.h(来自制造商)

typedef struct st_adc_api
    {
        ssp_err_t (* open)(adc_ctrl_t * const p_ctrl,  adc_cfg_t const * const      p_cfg);
        ssp_err_t (* scanCfg)(adc_ctrl_t * const p_ctrl,  adc_channel_cfg_t const * const p_channel_cfg);
        ssp_err_t (* scanStart)(adc_ctrl_t * const p_ctrl);
        ssp_err_t (* scanStop)(adc_ctrl_t * const p_ctrl);
        ssp_err_t (* scanStatusGet)(adc_ctrl_t * const p_ctrl);
        ssp_err_t (* read)(adc_ctrl_t  * const p_ctrl, adc_register_t const  reg_id,  adc_data_size_t * const p_data);
        ssp_err_t (* sampleStateCountSet)(adc_ctrl_t * const p_ctrl,  adc_sample_state_t  * p_sample);
        ssp_err_t (* close)(adc_ctrl_t * const p_ctrl);
        ssp_err_t (* infoGet) (adc_ctrl_t * const p_ctrl, adc_info_t * const p_adc_info);
        ssp_err_t (* versionGet)(ssp_version_t * const p_version);
    } adc_api_t;

**Header r_adc.h **(来自制造商)

extern const adc_api_t g_adc_on_adc;

赋值和函数定义在文件 r_adc.c(来自制造商)

const adc_api_t g_adc_on_adc =
{
    .open                   = R_ADC_Open,
    .scanCfg                = R_ADC_ScanConfigure,
    .infoGet                = R_ADC_InfoGet,
    .scanStart              = R_ADC_ScanStart,
    .scanStop               = R_ADC_ScanStop,
    .scanStatusGet          = R_ADC_CheckScanDone,
    .sampleStateCountSet    = R_ADC_SetSampleStateCount,
    .read                   = R_ADC_Read,
    .close                  = R_ADC_Close,
    .versionGet             = R_ADC_VersionGet
};

和##函数定义##

** 地图文件 ** MAP file

在我的应用程序中,如果我使用 adc_on_g_adc.open(...);链接器将所有 10 个符号拉到映射文件,而不是仅仅拉 R_ADC_Open();

什么可能导致 .map 文件从结构中提取所有函数,即使调用一个函数也是如此?

编辑: 将所有这些放在一个文件中即可得到我需要的地图文件。 IAR 编译器省略未使用的函数。当我使用多个 header 和来源时,它就不起作用了。

如果您用函数指针填充结构,那么就链接器而言,函数已经 "used" 了。

考虑一下链接器应该用什么替换 R_ADC_ScanConfigure。它不能选择将 g_adc_on_adc.scanCfg 设置为 NULL,可以吗?它不知道目标代码实际上并没有调用 g_adc_on_adc.scanCfg

基本上,唯一的选择是在输出中包含 R_ADC_ScanConfigure

但是,如果您 没有在结构 中分配 R_ADC_ScanConfigure,而是将 g_adc_on_adc.scanCfg 设置为 NULL,则链接器将能够省略 R_ADC_ScanConfigure 从输出。

const adc_api_t g_adc_on_adc =
{
    .open                   = R_ADC_Open,
    .scanCfg                = NULL,
    .infoGet                = R_ADC_InfoGet,
    .scanStart              = R_ADC_ScanStart,
    .scanStop               = R_ADC_ScanStop,
    .scanStatusGet          = R_ADC_CheckScanDone,
    .sampleStateCountSet    = R_ADC_SetSampleStateCount,
    .read                   = R_ADC_Read,
    .close                  = R_ADC_Close,
    .versionGet             = R_ADC_VersionGet
};

当然,如果您的代码确实最终调用了 g_adc_on_adc.scanCfg,您将尝试 运行 在地址 0 处编写代码(可能),这确实是一件非常糟糕的事情。你可以让 .scanCfg 指向一个函数,它只是在你的控制台上/在你的串行端口上/其他任何地方打印一条讨厌的错误消息,这样至少你知道出了什么问题。

您正在寻找的功能称为虚函数消除,它可以满足您的需求,但仅适用于 C++ 中的虚函数。

在您的代码中,编译器看到的只是一个用一些指向函数的指针初始化的结构。如果此结构是全局的或以某种方式从此模块导出,则编译器必须假定使用了结构的所有初始化部分。

当链接器掌握所有这些时,它会看到对结构的引用,仅此而已。那里没有信息告诉链接器使用了结构的哪一部分。

当你把所有东西都放在同一个模块中时,编译器可以找出你实际引用的函数并直接转到那里,这将使结构没有引用,这意味着它可以被丢弃,所有对另一个的引用也将被丢弃函数。