c 语言的函数快写法,C语言基本功教程系列(3) - 快速的函数调用

本文探讨了C语言函数调用中的最佳实践,包括使用const定义参数以防止意外修改、优化参数传递减少栈开销、以及利用位操作优化大量参数判断。通过实例讲解了正确传递指针和引用的重要性,并提倡高效的数据结构和内存管理技巧。
摘要由CSDN通过智能技术生成

C语言基本功教程系列(3) - 快速的函数调用

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

还是先说最基本的代码风格问题。首先,对于函数的参数(特别是指针),如果函数内部不会修改其指针的内容,一定要用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 );

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值