为何C风格结构体模拟类会产生额外汇编指令?
问题
为了模拟C类的开销,我用带“成员”函数指针的C风格结构体与C类做对比,原本以为二者指令数大致相当,结果发现C实现调用“成员”函数时会多一条额外指令。以下是测试代码及对应汇编指令(编译器:ARM GCC 12.2.0(linux)):
测试代码(main函数)
int main() { uint32_t a; rectangle_t r; /* struct */ r.set(2,3, &r); /* asm ldr r3, [r7, #12] adds r2, r7, #4 movs r1, #3 movs r0, #2 blx r3 */ a = r.getArea(&r); /* asm ldr r3, [r7, #16] adds r2, r7, #4 mov r0, r2 blx r3 str r0, [r7, #20] movs r3, #0 */ /* Class */ r.set(2, 3); /* asm adds r3, r7, #4 movs r2, #3 movs r1, #2 mov r0, r3 bl rectangle_t::set(unsigned int, unsigned int) */ a = r.getArea(); /* asm adds r3, r7, #4 mov r0, r3 bl rectangle_t::getArea() str r0, [r7, #12] movs r3, #0 */ }
声明代码
typedef struct rectangle rectangle_t; struct rectangle { uint32_t w; uint32_t l; void (*set)(uint32_t L, uint32_t W, rectangle_t *self); uint32_t (*getArea)(rectangle_t *self); }; void set(uint32_t L, uint32_t W, rectangle_t *self) { self->w = W; self->l = L; } uint32_t getArea(rectangle_t *self) { return self->l * self->w; } class rectangle_t { public: uint32_t w; uint32_t l; void set(uint32_t L, uint32_t W); uint32_t getArea(); }; void rectangle_t::set(uint32_t L, uint32_t W) { w = W; l = L; } uint32_t rectangle_t::getArea() { return l * w; }
请问:为何调用结构体“成员”函数比C++类成员函数多一条ldr指令?
回答
核心原因是两者的函数地址绑定机制完全不同:
C++非虚成员函数是静态绑定:编译阶段就已经确定了
rectangle_t::set、rectangle_t::getArea的绝对地址,调用时直接通过bl指令跳转到固定地址,不需要额外的内存读取操作。C结构体的函数指针是动态存储:你的C实现把函数指针存在结构体实例的内存中,调用时必须先从结构体的对应偏移位置(比如
[r7, #12])读取指针值到寄存器(也就是那条额外的ldr指令),再通过blx r3跳转执行。这一步内存读取是C实现无法避免的——因为函数指针的具体位置只有运行时才能确定。
如果想让C实现的指令数接近C++,可以直接硬编码调用函数(比如直接写set(2,3,&r)),但这样就失去了模拟类多态性的能力。
内容的提问来源于stack exchange,提问作者Buoy
相关产品推荐
相关产品推荐

