I am trying to implement an abstract interface in C using function pointers inside a struct.
Something like the following

typedef int (*fn_t)(int);
typedef struct
{
    int x;
    const fn_t fnp;
}struct_t;

__attribute__((optimize("O0"))) int square(int num) 
{
    return num * num;
}

static struct_t test = {.fnp = square};

int main(void)
{
    test.x = 1;

    int fnp_ret = test.fnp(3);

    return (fnp_ret);
}

当使用ARM—GCC—13.2.0 unknown—eabi使用—O3构建godbolt时,输出如下.

square:
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        sub     sp, sp, #12
        str     r0, [fp, #-8]
        ldr     r3, [fp, #-8]
        mov     r2, r3
        mul     r2, r3, r2
        mov     r3, r2
        mov     r0, r3
        add     sp, fp, #0
        ldr     fp, [sp], #4
        bx      lr
main:
        mov     r1, #1
        ldr     r3, .L5
        mov     r0, #3
        ldr     r2, [r3, #4]
        str     r1, [r3]
        bx      r2
.L5:
        .word   .LANCHOR0

这里可以看到,在main()中,程序集发出,首先在 struct 中定位函数指针,然后取消引用它.我觉得这很奇怪,因为函数指针是const,所以我希望编译器应该弄清楚它总是指向square函数,这样就相当于直接调用square函数.显然这里不是这样.

在实验过程中,我注意到,如果语句test.x = 1;被注释掉,程序集通过直接调用square函数,

square:
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        sub     sp, sp, #12
        str     r0, [fp, #-8]
        ldr     r3, [fp, #-8]
        mov     r2, r3
        mul     r2, r3, r2
        mov     r3, r2
        mov     r0, r3
        add     sp, fp, #0
        ldr     fp, [sp], #4
        bx      lr
main:
        mov     r0, #3
        b       square

What am I missing?
Is there any way to implement this reliably without paying the performance hit described above?

推荐答案

  1. 优化O0不是正确的.你要noinline
  2. 这是众所周知的gcc优化器缺陷.如果你touch struct 的任何成员,它会认为整个 struct 为非const
__attribute__((noinline)) int square(int num) 
{
    return num * num;
}

我错过了什么?有没有什么方法可以可靠地实现这一点, 支付上面描述的性能打击?

恐怕你无能为力.很可能永远不会被分类.如果对你很重要,你可以使用clang:https://godbolt.org/z/T4bznYE4h

C++相关问答推荐

在C中使用强制转换将uint16_t转换为uint8_t [2]是否有效?

警告:C++中数组下标的类型为‘char’[-Wchar-subpts]

变量>;-1如何在C中准确求值?

Can函数指针指向C++中具有不同参数连续性的函数

可以将C变量限制为特定的读/写速度吗?

如何捕捉只有换行符或空格字符缓冲区的边缘大小写

S将C语言宏定义为自身的目的是什么?(在glibc标题中看到)

为什么未初始化的 struct 的数组从另一个数组获取值?

链接到底是如何工作的,我在这里到底做错了什么

搜索使用int代替time_t的用法

初始成员、公共初始序列、匿名联合和严格别名如何在C中交互?

如何在c中使用具有不同变量类型的内存分配?

使用Open62541向OPCUA服务器发送读请求时内存泄漏

无法识别C编程语言的语法,如书中所示

我不知道为什么它不能正常工作,我用了get()和fget(),结果是一样的

从另一个宏函数调用C宏

将char*数组深度复制到 struct 中?

为什么写入关闭管道会返回成功

无法在线程内用 C 打印?

在带中断的循环缓冲区中使用 易失性