/**************************************************************
*File Name: size_test.c
*Description:
* 测试使用函数指针间接调用函数,以及直接
* 调用函数对程序代码大小的影响。
*Author : Chryice
*Date : 2010-2-26
*Modify List:
***************************************************************/
#include <stdio.h>
int max(int v1, int v2);
void test_entry1(void);
void test_entry2(void);
typedef int (*fun_ptr)(int, int);
fun_ptr gFunPtr = NULL;
int main(int argc ,char** argv)
{
gFunPtr = max;
test_entry1();
test_entry2();
return 0;
}
int max(int v1, int v2)
{
return ((v1 > v2) ? v1 : v2);
}
void test_entry1(void)
{
int v1 = 23, v2 = 30, m;
m = max(v1, v2);
printf("Max value: %d/n", m);
}
void test_entry2(void)
{
int v1 = 56, v2 = 21, m;
m = gFunPtr(v1, v2);
printf("Max Value: %d/n", m);
}
//对应汇编代码如下
/*
从左至右分为三列,分别为:
程序在RAM 中的内存地址 机器代码 汇编代码
因为ARM 体系结构采用的是精简指令系统,每条指令代码固定为 4 字节,即 32bits.
*/
test_entry2(000080db) [0xb580] push {r7,r14}
000080de [0x2038] mov r0,#0x38 ;参数传递设置
000080e0 [0x2115] mov r1,#0x15
000080e2 [0x4a10] ldr r2,0x00008124 ; = #0x00009ff4;把全局函数指针变量 gFunPtr 的存储地址放到 r2 寄存器中
000080e4 [0x6812] ldr r2,[r2,#0] ;获取 gFunPtr 中保存的数据,也即把 gFunPtr 所指向函数的内存地址保存到寄存器 r2 中。
000080e6 [0xf8f9f000] bl __call_via_r2 ;通过 r2 寄存器,进行函数调用。
000080ea [0x1c01] mov r1,r0
000080ec [0xa00e] adr r0,0x8128
000080ee [0xf8d7f000] bl _printf
000080f2 [0xbd80] pop {r7,pc} ;pc也即 r14 寄存器, pop pc 能实现函数调用的返回
max [0x4288] cmp r0,r1
000080f6 [0xdc00] bgt 0x80fa ; (max + 0x6)
000080f8 [0x1c08] mov r0,r1
000080fa [0x4770] bx r14
test_entry1 [0xb580] push {r7,r14}
000080fe [0x2017] mov r0,#0x17
00008100 [0x211e] mov r1,#0x1e
00008102 [0xfff7f7ff] bl max
00008106 [0x1c01] mov r1,r0
00008108 [0xa00b] adr r0,0x8138
0000810a [0xf8c9f000] bl _printf
0000810e [0xbd80] pop {r7,pc}
main [0xb580] * push {r7,r14}
00008112 [0x480d] ldr r0,0x00008148 ; = #0x000080f5 ;???按道理应该是 0x000080f4 ,也即函数 max 的内存地址 ???
00008114 [0x4903] ldr r1,0x00008124 ; = #0x00009ff4
00008116 [0x6008] str r0,[r1,#0]
00008118 [0xfff0f7ff] bl test_entry1
0000811c [0xffdef7ff] bl test_entry2
00008120 [0x2000] mov r0,#0
00008122 [0xbd80] pop {r7,pc}
//全局数据 : 从全局数据可以看出,对于传递给 printf 函数的格式化字符串,对于每个调用都保存了一份,所以,如果格式化字符串相同的话,最好
//提取出来保存到一个全局常量变量中。这样就可以减少重复,节省空间。
00008124 [0x00009ff4] dcd 0x00009ff4 ....
00008128 [0x2078614d] dcd 0x2078614d Max
0000812c [0x756c6156] dcd 0x756c6156 Valu
00008130 [0x25203a65] dcd 0x25203a65 e: %
00008134 [0x00000a64] dcd 0x00000a64 d...
00008138 [0x2078614d] dcd 0x2078614d Max
0000813c [0x756c6176] dcd 0x756c6176 valu
00008140 [0x25203a65] dcd 0x25203a65 e: %
00008144 [0x00000a64] dcd 0x00000a64 d...
00008148 [0x000080f5] dcd 0x000080f5 ....
/*_testentry1和 test_entry2 比较 , 使用函数指针进行函数调用的情况多了两行指令:
000080e2 [0x4a10] ldr r2,0x00008124 ; = #0x00009ff4;把全局函数指针变量 gFunPtr 的存储地址放到 r2 寄存器中
000080e4 [0x6812] ldr r2,[r2,#0] ;获取 gFunPtr 中保存的数据,也即把 gFunPtr 所指向函数的内存地址保存到寄存器 r2 中。
使用函数指针进行函数间接调用一次,代码大小(仅考虑函数调用的代码片段)多了0x000080e6 - 0x000080e2 = 4 个字节 ;
使用函数指针的好处就是动态解析,诸如C++ 里面的虚函数表来实现多态。
*/