当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]不定参数在C语言中的应用

刚学C语言的时候,一般人都会首先接触printf函数。通过这个函数,你可以打印不定个数的变量到屏幕,如:  

printf("%d", 3);  

printf("%d,%d",3,4);  

上述代码看似简单,实际上却需要我们解决许多问题。在我们设计printf的时候,我们是不知道到底会传入几个参数的。在这种未知的情况下,我们需要解决下面几个问题:  

怎么告诉printf我们会传入几个参数  

printf怎么去访问这些参数  

函数调用完成后,系统怎么把参数从传递用的堆栈中释放  

为了解决这些问题,我们首先要解释cdecl调用约定,所有使用不定参数的函数必须是使用cdecl(全局函数)或者this call(类成员函数)调用约定。该约定对于参数传递规定如下:  

参数从右向左入栈(也就是如果你调用f(a,b,c),则c先入栈,然后是b,最后是a入栈)  

调用者负责清理堆栈  

其中第二点直接解决了前面三个问题中的第三个问题。我们来详细说说其他两个问题。  

确定参数的个数  

在一个函数中,一般有如下prelog代码:  

00401020 push ebp  

00401021 mov ebp,esp  

00401023 sub esp,48h  

执行上述代码之后,func(a,b,c)函数所处的堆栈上下文就变成如下布局:  


  
其中,ebp指向保存旧的ebp的堆栈内存的下一个字的地址,ebp+8指向eip地址,ebp+12则指向函数调用的第一个参数,而ebp和esp之间是用于临时变量(也就是堆栈变量)的空间。  

注意,由于上述prelog代码的存在,我们很容易通过ebp得到第一个参数的地址,对于不定参数列表之前的类型固定的参数,我们也可以根据类型信息得到其实际的位置(例如,第一个参数的位置偏移第一个参数的大小,就是第二个参数的地址)。  

注意不定参数函数有个限制,就是不定参数的列表必须在整个函数的参数列表的最后。我们不可以定义如下的函数:  

void func(int a, ……, int c)  

所有类型固定的参数都必须出现在参数列表的开始。这样根据前面的论述,我们就可以得到所有类型固定的参数。  

在设计具有不定参数列表的函数的时候,我们有两种方法来确定到底多少参数会被传递进来。  

方法1是在类型固定的参数中指明后面有多少个参数以及他们的类型。printf就是采用的这种方法,它的format参数指明后面每个参数的类型。  

方法2是指定一个结束参数。这种情况一般是不定参数拥有同样的类型,我们可以指定一个特定的值来表示参数列表结束。下面这个sum函数就是一个例子:  [!--empirenews.page--]

int sumi(int c, ...)  

{  

va_list ap;  

va_start(ap,c);  

int i;  

int sum = c;  

c = va_arg(ap,int);  

while(0!=c)  

{  

sum = sum+c;  

c = va_arg(ap,int);  

}  

return sum;  

}  

使用这个函数的代码为:  

int main(int argc, char* argv[])  

{  

int i=sumi(1,2,3,4,5,6,7,8,9,0);  

return 0;  

}  

访问各个参数  

其实前文已经告诉我们怎么去访问不定参数。va_start和va_arg函数可以被结合起来用于依次访问每个函数,他们实际上都是宏函数。  

在vc6,va_start函数定义为:  

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & "(sizeof(int) - 1) )  

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )  

其中_INTSIZEOF(n)计算比n大的sizeof(int)的最小倍数,如果n=101,则_INTSIZEOF(n)为104.  

va_start执行完毕后,ap指向变量v后第一个4字节对齐的地址。例如,v的地址为0x123456, v的大小为13,则v后面的下一个与字边界对齐的地址为0x123456+0x0D=0x123463再调整为与4字节对齐的下一个地址,也就是0x123464.  

va_arg函数定义为:  

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )  

分析与va_start一样,它的结果是使ap指向当前变量的下一个变量。  

这样,我们只要在开始时使用va_start把不定参数列表赋值给ap,然后依次用va_arg获得不同参数即可。  

潜在问题  [!--empirenews.page--]

使用不定参数列表,有两个问题特别需要注意。  

问题1的理解相对简单:我们在重载一个函数的时候,不能依赖不定参数列表部分对函数进行区分。  

假定我们定义两个重载函数如下:  

int func(int a, int b, ……)  

int func(int a, int b, float c);  

则上述函数会导致编译器不知道怎么去解释func(1,2, 3.3),因为当第三个参数为浮点数时,两个实现都可以满足匹配要求。一般情况,个人建议对于不定参数函数不要去做重载。  

另外一个问题是关于类型问题。绝大多数情况下,C和C++的变量都是强类型的,而不定参数列表属于一个特例。  

当我们调用va_arg的时候,我们指明下一个参数的类型,而在执行的时候,va_arg正是根据这个信息在堆栈上来找到对应的参数的。如果我们需要的类型和真实传递进来的参数完全一致时自然没有问题,但是假如类型不一样,则会有大麻烦。  

假如上面的的sumi函数,我们用下面方法调用:  

int sum = sumi(1, 2.2, 3, 0)  

注意第二个参数我们传入了一个double类型的2.2,我们希望sumi在做加法时可以做隐式类型转换,转换为int进行计算。但是实际情况时,当我们分析到这个参数时,调用的是:  

c=va_arg(ap,int)  

据前文va_arg的定义,这个宏被翻译成:  

#define va_arg(ap,t) ( *(int *)((ap += _INTSIZEOF(int)) - _INTSIZEOF(int)) )  

如果后面的+=计算出正确的地址,最后就变成  

*(int*)addr  

如果希望能得到正确的整数值,必须要求addr所在的地址是一个真实的int类型。但是当我们传入double时,实际上其内存布局和int完全不同,因此我们得不到需要的整数。感兴趣的朋友可以用下面简单的代码做测试:  

double a;  

a=1.1;  

int b = *(int*) & a;  

因此,当我们调用有不定参数列表的函数时,不要期望系统做隐式类型转换,系统不会做这种检查或者转换,你给的参数类型必须严格和你希望的值一样。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

苏州2024年5月27日 /美通社/ -- 微软 Microsoft Start Networks 中国内容生态伙伴峰会(Microsoft Start Partner Summit)在微软苏州新园区成功举办。应微软全球...

关键字: MICROSOFT START NETWORKS 微软

作者 Mohamad Ali| IBM咨询首席运营官 北京2024年5月24日 /美通社/ -- 生成式AI的兴起几乎在所有面向上给业务带来改变。根据 IBM 商业价值研究院最新的年度 CEO 研究,近60%...

关键字: IBM AI BSP 模型

台北2024年5月21日 /美通社/ -- 提供针对AMD WRX90和TRX50主板优化的DDR5 OC R-DIMM 提供容量128GB(16GBx8)到768GB(96GBx8),速度5600MHz到8...

关键字: AMD 内存 BSP GB

上海2024年5月20日 /美通社/ -- 2024年5月16日,世界知名的生命科学公司 Eppendorf 集团于第二十三届生物制品年会上成功举办了"疫路超越 推流出新"的产品发布会,正式推出大规模...

关键字: RF PEN BSP IMAC

北京2024年5月20日 /美通社/ -- 过去五年里,支付和收款方式日新月异,其发展和变化比过去五十年都要迅猛。从嵌入式数字商务的出现,到"一拍即付"的...

关键字: VI BSP PAY COM

华钦科技集团(纳斯达克代码: CLPS ,以下简称"华钦科技"或"集团")近日宣布致敬 IBM 大型机 60 载辉煌历程,并将继续实施集团大型机人才培养计划。

关键字: IBM BSP 研发中心 PS

助力科研与检测新突破 上海2024年5月15日 /美通社/ -- 全球知名的科学仪器和服务提供商珀金埃尔默公司今日在上海举办了主题为"创新不止,探索无界"的新品发布会,集中展示了其在分析仪器领域的最...

关键字: 质谱仪 BSP DSC 气相色谱

上海2024年5月16日 /美通社/ -- 2024年5月10日至5月13日,富士胶片(中国)投资有限公司携旗下影像产品创新力作亮相北京P&E 2024。在数码相机展览区域,全新制定的集团使命"为世界绽...

关键字: 富士 数码相机 影像 BSP

北京2024年5月16日 /美通社/ -- 5月10日晚,2024中国品牌日晚会在新华社全媒体矩阵播出,50个入选品牌在当晚揭晓,极氪名列其中,成为首度上榜的中国造车新势力代表。 图为北京时间5月10日 21:30 ,...

关键字: 吉利 COM HTML 代码

贝克曼库尔特目前已成为MeMed Key免疫分析平台和MeMed BV检测技术的授权经销商 在原有合作的基础上,继续开发适用于贝克曼库尔特免疫分析仪的MeMed BV检测 加州布瑞亚和以色列海法2024年5月16日...

关键字: BSP IO 检测技术 免疫分析仪
关闭
关闭