登录社区:用户名: 密码: 忘记密码 网页功能:加入收藏 设为首页 网站搜索  

文档

下载

图书

论坛

安全

源码

硬件

游戏
首页 信息 空间 VB VC Delphi Java Flash 补丁 控件 安全 黑客 电子书 笔记本 手机 MP3 杀毒 QQ群 产品库 分类信息 编程网站
  立华软件园 - 安全技术中心 - 技术文档 - 入门基础 技术文章 | 相关下载 | 电子图书 | 攻防录像 | 安全网站 | 在线论坛 | QQ群组 | 搜索   
 安全技术技术文档
  · 安全配制
  · 工具介绍
  · 黑客教学
  · 防火墙
  · 漏洞分析
  · 破解专题
  · 黑客编程
  · 入侵检测
 安全技术工具下载
  · 扫描工具
  · 攻击程序
  · 后门木马
  · 拒绝服务
  · 口令破解
  · 代理程序
  · 防火墙
  · 加密解密
  · 入侵检测
  · 攻防演示
 安全技术论坛
  · 安全配制
  · 工具介绍
  · 防火墙
  · 黑客入侵
  · 漏洞检测
  · 破解方法
 其他安全技术资源
  · 攻防演示动画
  · 电子图书
  · QQ群组讨论区
  · 其他网站资源
最新招聘信息

C语言基本功教程系列(3) - 快速的函数调用
发表日期:2007-03-28作者:[转贴] 出处:  

 我又来了,今天坎坎函数调用的问题。函数哪里都有,小的程序一两个函数,大的程序成百上千个函数。即使在游戏的关键循环中,调用几十个函数也是很常见的。所以函数调用代码的质量,在很大程度上影响着游戏的质量。

还是先说最基本的代码风格问题。首先,对于函数的参数(特别是指针),如果函数内部不会修改其指针的内容,一定要用const来定义参数类型
=========不好的风格==========
void function(char * ServerName)
{
   // 内部不允许对ServerName的内容进行修改
}

=========好的风格===========
void function(const char * ServerName)
{
   // 内部不允许对ServerName的内容进行修改
}

为什么这么做呢? 举个简单的例子: 在团队开发中程序员A写好了displayFunction,传了一个数据结构给displayFunction做图象显示,然后在接下来的程序中对数据进行计算。A认为displayFunction不会对数据进行修改,所以在以后的数据运算中,没有进行一致性检测。过了几天程序员B被派过来优化A的程序,因为不知道不能改数据,结果改了下,在displayFunction中改变了数据结构的内容,当时测试通过。但是在产品发布的Alpha测试阶段,用real data的时候出了问题。我想通宵debug去差这么点个小问题,不是很值得吧。只要稍微留点心,就可以避免了

==================分割线==================
下面谈谈函数的调用问题。我们都知道,在调用的一个函数的时候,传给函数的参数是要压到栈里,然后才能被函数访问。我们来看一下函数调用的汇编代码.(汇编代码是用Visual Studio .net 2003 编译, release version。优化参数 /0t /02)

=======printf("%s%d%d%d%d",haha,m,n,p,i);======
00401000 push ecx
00401001 push ebx
00401002 mov ebx, dword ptr [esp+04]
00401003 push ebp
00401004 mov ebp, dword ptr [esp+08]
00401005 push esi
00401006 push edi
00401007 mov edi, dword ptr [esp+10] 
00401008 xor esi, esi
00401009 push esi
0040100A push edi
0040100B push ebx
0040100C push ebp
0040100D push 00408040
0040100E push 004060FC
0040100F call 00401054

我的天哪,这是多少代码,只不过为了把参数push到栈里就用了15条。看我们看看另一段代码

===========printf("%s",haha);============
00401010 push 00408040
00401011 push 004060FC
00401012 call 00401054

现在我不用说大家都明白了吧。传递给函数的参数越少越好,最好就是一个指针,指向一个structure。这就是为什么大部分的directX的函数就是一个指针的大structure传过去。里边的参数好几十个。当然了 void fucntion(void)是最快的函数调用,也可以用inline来优化关键循环内的函数。不过在每一个frame的执行代码中,有成百上千个函数,不可能所有的都inline吧。所有能快点就快点喽。当然了,传递 structure的reference也是同样的效果,只要不把structure当参数就好。
============错误的方式===========
void function(struct OneStructure Parameter);
============正确的方式===========
void function(struct OneStructure & Parameter);
or
void function(struct OneStructure * pParameter);


==================分割线==================
这个例子不是很好,因为降低了代码的可读性,不过做为参考。。。。
很多人喜欢写代码的时候这么写:
char szName[] = "Aear";
int length;

length = strlen(szName);
if(length > 0)   // 这行的效率不考虑
{
   // do something
}
粗一看没什么问题,不过如果length在以后用不到的话,那么就浪费了。因为length占用了内存,而且浪费了cpu资源。让我们看带汇编代码(汇编代码是用Visual Studio .net 2003 编译, release version。优化参数 /0t /02)

length = strlen(szName);
if(length > 0)   {...}

0040101F       sub eax, edx
00401021       mov dword ptr [esp+4], eax // 把返回值存到length中
00401025       je 00401039                       // 判断跳转

========更快速的写法的代码========
if(strlen(szName)) {...}

0040101F       sub eax, edx
00401021       mov esi, eax   //把返回值放在个临时寄存器中
00401023       je 00401037

大家都知道寄存器之间进行数据操作是非常快的,而且是稳定的一个cpu clock cycle,至于 00401021       mov dword ptr [esp+4], eax 到底要花多少个clock cycle,那只有天知道了。因为这种从内存中读数据的指令,最少也是2个clock cycle,即使在L2 cache中,也不会比 mov esi, eax 快,而且浪费了栈空间。


==================再分割下吧,虽然不是很喜欢==================
最后说说一种类告诉的分枝判断参数传递。在有些情况下,我们经常要传很多参数,比如pixel shader等等,这些函数根据参数的设置,进行不同的操作。举个例子:

struct Parameter{
     bool bDrawWater;
     bool bDrawSkybox;
     bool bDrawTerrain;
     bool bDrawSepcialEffects;
} DrawParamter;

void DrawEnvironment( struct Parameter * pPara)
{
     if(pPara->bDrawWater) {....};
     if(pPara->bDrawSkybox) {....};
     if(pPara->bDrawTerrain) {....};
     if(pPara->bDrawSpecialEffects) {....};
}

对于这样的代码,还有更快速, 更节省内存的方法,那就是位操作。
const static UINT32 DRAW_WATER_FLAG               = 1;
const static UINT32 DRAW_SKYBOX_FLAG              = 1 << 1;
const static UINT32 DRAW_TERRAIN_FLAG             = 1 << 2;
const static UINT32 DRAW_SPECIALEFFECTS_FLAG  = 1 << 3;

void DrawEnvironment(UINT32 DrawFlag)
{
    //注意了,这里不需要 pPara->,也就是节省了内存访问,速度至少提高了1到2个clock cycle
     if( DrawFlag & DRAW_WATER_FLAG ) {.....};
     if( DrawFlag & DRAW_SKYBOX_FLAG) {.....};

    //甚至还可以进行各种不同组合的判断,比如
     if( DrawFlag & (DRAW_WATER_FLAG | DRAW_SKYBOX_FLAG) ) {....};
}

在调用的时候,代码更加简洁明了:
DrawEnvironment( DRAW_WATER_FLAG | DRAW_TERRAIN_FLAG );

就说这么多,累了,下次见


我来说两句】 【发送给朋友】 【加入收藏】 【返加顶部】 【打印本页】 【关闭窗口
中搜索 C语言基本功教程系列(3) - 快速的函数调用

 ■ [欢迎对本文发表评论]
用  户:  匿名发出:
您要为您所发的言论的后果负责,故请各位遵纪守法并注意语言文明。

最新招聘信息

关于我们 / 合作推广 / 给我留言 / 版权举报 / 意见建议 / 广告投放 / 友情链接  
Copyright ©2001-2006 Lihuasoft.net webmaster(at)lihuasoft.net
网站编程QQ群   京ICP备05001064号 页面生成时间:0.00198