http://blog.csdn.net/midion9/article/details/50605337
看nginx的代码时,发现有些函数返回值之后,还有一个ngx_cdecl关键字,如:
1
2
3
4
5
6
7
|
int ngx_cdecl main( int argc, char * const *argv) { ngx_int_t i; ngx_log_t * log ; ngx_cycle_t *cycle, init_cycle; ngx_core_conf_t *ccf; |
查看其定义处发现竟然是个空:
1
|
#define ngx_cdecl |
那么这个宏的作用是什么呢?如果了解函数调用的规则,则对这个宏基本上就能理解了:
在C语言中,假设我们有这样的一个函数:int function(int a,int b),调用时只要用result = function(1,2)这样的方式就可以使用这个函数。但是,当高级语言被编译成计算机可以识别的机器码时,有一个问题就凸现出来:在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈的数据结构来支持参数传递。不同CPU,不同编译器的堆栈布局、函数调用方法都可能不同,但堆栈的基本概念是一样的。
函数调用约定包括传递参数的顺序,谁负责清理参数占用的堆栈等,例如 :
调用方法 | 参数传递顺序 | 谁负责清理参数占用的堆栈 |
__pascal | 从左到右 | 调用者 |
__stdcall | 从右到左 | 被调函数 |
__cdecl | 从右到左 | 调用者 |
调用函数的代码和被调函数必须采用相同的函数的调用约定,程序才能正常运行。
如前表所示,__cdecl和__stdcall的区别是:__cdecl是调用者清理参数占用的堆栈,__stdcall是被调函数清理参数占用的堆栈。假设函数A是__stdcall,函数B调用函数A。你必须通过函数声明告诉编译器,函数A是__stdcall。编译器自然会产生正确的调用代码。如果函数A是__stdcall。但在引用函数A的地方,你却告诉编译器,函数A是__cdecl方式,编译器产生__cdecl方式的代码,与函数A的调用约定不一致,就会发生错误。
由于__stdcall的被调函数在编译时就必须知道传入参数的准确数目(被调函数要清理堆栈),所以不能支持变参数函数,例如printf。而且如果调用者使用了不正确的参数数目,会导致堆栈错误。
因此,nginx中使用这个宏是为了跨平台支持,方便调整函数调用方式。在遇到问题时,可以修改上面的定义为:
1
|
#define ngx_cdecl stdcall |