函数指针使用与直接函数调用对程序代码大小的影响
2010-03-12 16:35
525 查看
/**************************************************************
*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++
里面的虚函数表来实现多态。
*/
*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++
里面的虚函数表来实现多态。
*/
相关文章推荐
- 使用C#调用存储过程,用函数合理组织代码,使程序更加的清晰(示例)
- 在别人的程序的任意位置加上调用自己函数的代码,且不影响原程序执行的方法
- 使用C#调用存储过程,用函数合理组织代码,使程序更加的清晰(示例)
- SYD8801 UART使用说明【uart轮发数据代码实现】【 uart中断发数据代码实现】【RFSleep函数对串口的影响】【中断中调用串口打印函数有可能会造成死机】
- x86代码移植到x64下,如何访问虚表和虚函数指针,使用地址调用虚函数
- “Caculate::writehtml”: 函数调用缺少参数列表;请使用“&Caculate::writehtml”创建指向成员的指针
- linux下c程序调用reboot函数实现直接重启
- 使用Doxygen+graphviz+Sublime2来看代码,查看函数调用关系
- Js 使用new关键字调用函数和直接调用函数的区别
- C++指针直接调用类成员函数探讨
- 【Linux C代码分享二】UDP Socket示例程序:使用fgets函数获取屏幕输入
- c++程序代码使用指针调试运行出错分析
- Thrift框架中函数栈的大小对程序的影响
- delphi中用代码实现注册Ocx和Dll(有点怪异,使用CallWindowProc来调用指定函数DllRegisterServer)
- Javascript代码直接调用计算机中的程序
- TGraphicControl(自绘就2步,直接自绘自己,不需要调用VCL框架提供的函数重绘所有子控件,也不需要自己来提供PaintWindow函数让管理框架来调用)与TControl关键属性方法速记(Repaint要求父控件执行详细代码来重绘自己,还是直接要求Invalidate无效后Update刷新父控件,就看透明不透明这个属性,因为计算显示的区域有所不同)
- Thrift框架中函数栈的大小对程序的影响
- 使用CreateProcess函数实现隐秘的程序调用之二:使用匿名管道
- 怎样使用gprof和oprofile来分析 linux程序的性能(每个函数的调用次数与耗时)