C中的可变参数研究
一. 何谓可变参数
int printf( const char format, );
这是使用过C语言的人所再熟悉不过的printf函数原型,它的参数中就有固定参数format和可变参数(用”…”表示) 而我们又可以用各种方式来调用printf,如:
printf("%d",value);
printf("%s",str);
printf("the number is %d ,string is:%s", value, str);
二实现原理
C语言用宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可变参数的地址。下面我们来分析这些宏。在VC中的stdargh头文件中,针对不同平台有不同的宏定义,我们选取X86平台下的宏定义:
typedef char va_list;
/把va_list被定义成char,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void的/
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
/_INTSIZEOF(n)宏是为了考虑那些内存地址需要对齐的系统,从宏的名字来应该是跟sizeof(int)对齐。一般的sizeof(int)=4,也就是参数在内存中的地址都为4的倍数。比如,如果sizeof(n)在1-4之间,那么_INTSIZEOF(n)=4;如果sizeof(n)在5-8之间,那么_INTSIZEOF(n)=8。/
#define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) )
/va_start的定义为 &v+_INTSIZEOF(v) ,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在的内存地址/
#define va_arg(ap,t) ( (t )((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
/这个宏做了两个事情,
①用用户输入的类型名对参数地址进行强制类型转换,得到用户所需要的值
②计算出本参数的实际大小,将指针调到本参数的结尾,也就是下一个参数的首地址,以便后续处理。/
#define va_end(ap) ( ap = (va_list)0 )
/x86平台定义为ap=(char)0;使ap不再 指向堆栈,而是跟NULL一样有些直接定义为((void)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的 在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型 /
以下再用图来表示:
在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。
|——————————————————————————|
|最后一个可变参数 | ->高内存地址处
|——————————————————————————|
|——————————————————————————|
|第N个可变参数 | ->va_arg(arg_ptr,int)后arg_ptr所指的地方,
| | 即第N个可变参数的地址。
|——————————————— |
…………………………
|——————————————————————————|
|第一个可变参数 | ->va_start(arg_ptr,start)后arg_ptr所指的地方
| | 即第一个可变参数的地址
|——————————————— |
|———————————————————————— ——|
| |
|最后一个固定参数 | -> start的起始地址
|—————————————— —|
|—————————————————————————— |
| |
|——————————————— |-> 低内存地址处
三printf研究
下面是一个简单的printf函数的实现,参考了中的156页的例子,读者可以结合书上的代码与本文参照。
#include "stdioh"
#include "stdlibh"
void myprintf(char fmt, ) //一个简单的类似于printf的实现,//参数必须都是int 类型
{
char pArg=NULL; //等价于原来的va_list
char c;
pArg = (char) &fmt; //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值
pArg += sizeof(fmt); //等价于原来的va_start
do
{
c =fmt;
if (c != '%')
{
putchar(c); //照原样输出字符
}
else
{
//按格式字符输出数据
switch(++fmt)
{
case 'd':
printf("%d",((int)pArg));
break;
case 'x':
printf("%#x",((int)pArg));
break;
default:
break;
}
pArg += sizeof(int); //等价于原来的va_arg
}
++fmt;
}while (fmt != '\0');
pArg = NULL; //等价于va_end
return;
}
int main(int argc, char argv[])
{
int i = 1234;
int j = 5678;
myprintf("the first test:i=%d",i,j);
myprintf("the secend test:i=%d; %x;j=%d;",i,0xabcd,j);
system("pause");
return 0;
}
在intel+win2k+vc6的机器执行结果如下:
the first test:i=1234
the secend test:i=1234; 0xabcd;j=5678;
四应用
求最大值:
#include //不定数目参数需要的宏
int max(int n,int num,)
{
va_list x;//说明变量x
va_start(x,num);//x被初始化为指向num后的第一个参数
int m=num;
for(int i=1;i {
//将变量x所指向的int类型的值赋给y,同时使x指向下一个参数
int y=va_arg(x,int);
if(y>m)m=y;
}
va_end(x);//清除变量x
return m;
}
main()
{
printf("%d,%d",max(3,5,56),max(6,0,4,32,45,533));
}
1、电流。额定通态电流是稳定工作电流,通常称为电流。普通功率调节器的IT通常为1安培至数十安培。
2、耐压。反向重复峰值电压(VRRM)或关闭状态重复峰值电压,通常称为电力调整器的耐压。常用的可控硅晶闸管VRRM/VDRM通常为几百伏到一千伏。
3、触发电流。反向重复峰值电压(VRRM)或关闭状态重复峰值电压,通常称为电力调整器的耐压。常用的可控硅晶闸管VRRM/VDRM通常为几百伏到一千伏。
4、额定正向平均电流。在的环境温度和散热条件下,允许流过阴极和阳极的电流平均值。
5、额定容量。电力调整器的额定容量是在制造商规定的额定工作条件下变压器输出视在功率的保证值,SN表示:额定容量通常是指高压绕组的容量。当变压器容量因冷却方式而变化时,额定容量是指其容量。
6、额定电流。变压器的初级和次级额定电流是指在额定电压和额定环境温度下通过变压器的初级和次级绕组的长期允许线路电流,为了防止电力调整器变压器的部分部件超温。单位用A表示。
PID封装在博图系统中可通过参数配置设置,可以分为PID参数组和具体的PID参数,PID参数组是控制单元对外提供的可用PID参数,而具体的PID参数则是为某一特定应用系统进行设置。PID封装在博图系统中可通过参数配置设置,可以分为PID参数组和具体的PID参数,PID参数组是控制单元对外提供的可用PID参数,而具体的PID参数则是为某一特定应用系统进行设置。
Easy Sysprep现在已经相对完善,在通用调用接口这一步,如果你没有特殊需要安装的第三方软件(一般都是在第一阶段封装前装好所需的常用软件),直接下一步就可以了。封装基本是用来针对大量装机使用,所以,里面安装的软件都是通用基本工具,如:Office、360、输入法、浏览器、Adobe reader之类,这些都可以直接封装进去。如果是给自己用,就是一台电脑而已,不需要Easy Sysprep,XP直接GHOST就好。
jsp中的表单可以跟javabean建立映射关系,提交到servlet后就可以保存到javabean了。
例如:
1、jsp表单域:
<form name="login" action="loginResponsejsp">
请输入您的用户名:<br />
<input type="text" name="user_name"/><br />
请输入您的年龄:<br />
<input type="text" name="user_age"/><br />
请输入您的地址:<br />
<input type="text" name="user_address"/><br />
<input type="submit" value="OK"/>
<input type="reset" value="Reset"/>
</form>
2、javabean:
package javabean;
publicclass loginBean {
private String name;
privateint age;
private String address;
public String getAddress() {
return address;
}
publicvoid setAddress(String address) {
thisaddress = address;
}
publicint getAge() {
return age;
}
publicvoid setAge(int age) {
thisage = age;
}
public String getName() {
return name;
}
publicvoid setName(String name) {
thisname = name;
}
}
3、servlet处理封装
loginBean login=new loginBean();
loginsetName(requestgetParameter("name"));
loginsetAge(IntegerparseInt(requestgetParameter("age")));
loginsetAddress("xxxxxxx");
有OPGA封装、mPGA封装和CPGA封装。
1、OPGA封装
OPGA(Organic pin grid Array,有机管脚阵列)。这种封装的基底使用的是玻璃纤维,类似印刷电路板上的材料。此种封装方式可以降低阻抗和封装成本。
OPGA封装拉近了外部电容和处理器内核的距离,可以更好地改善内核供电和过滤电流杂波。AMD公司的AthlonXP系列CPU大多使用此类封装。
2、mPGA封装
mPGA,微型PGA封装,目前只有AMD公司的Athlon 64和英特尔公司的Xeon(至强)系列CPU等少数产品所采用,而且多是些高端产品,是种先进的封装形式。
3、CPGA封装
CPGA也就是常说的陶瓷封装,全称为Ceramic PGA。主要在Thunderbird(雷鸟)核心和“Palomino”核心的Athlon处理器上采用。
扩展资料封装发展进程:
结构方面: TO->DIP->LCC->QFP->BGA ->CSP->WLP;
材料方面:金属、陶瓷->陶瓷、塑料->塑料;
引脚形状:长引线直插->短引线或无引线贴装->球状凸点;
装配方式:通孔插装->表面组装->直接安装。
DIP--Double In-line Package--双列直插式封装。插装型封装之一,引脚从封装两侧引出,封装材料有塑料和陶瓷两种。DIP是最普及的插装型封装,应用范围包括标准逻辑IC,存贮器LSI,微机电路等。
PLCC--Plastic Leaded Chip Carrier--PLCC封装方式,外形呈正方形,32脚封装,四周都有管脚,外形尺寸比DIP封装小得多。PLCC封装适合用SMT表面安装技术在PCB上安装布线,具有外形尺寸小、可靠性高的优点。
参考资料来源:百度百科--封装技术
以上就是关于printf这样参数可变的函数如何封装全部的内容,包括:printf这样参数可变的函数如何封装、合泉电力调节器设置参数、博图怎么pid封装等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)