版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C语言可变参数详解1,首先,怎么得到参数的值。对于一般的函数,我们可以通过参数对应在参数列表里的标识符来得到。但是参数可变函数那些可变的参数是没有参数标识符的,它只有“”,所以通过标识符来得到是不可能的,我们只有另辟途径。我们知道函数调用时都会分配栈空间,而函数调用机制中的栈结构如下图所示:| 返回地址 |调用函数运行状态|可见,参数是连续存储在栈里面的,那么也就是说,我们只要得到可变参数的前一个参数的地址,就可以通过指针访问到那些可变参数。但是怎么样得到可变参数的前一个参数的地址呢? 不知道你注意到没有,参数可变函数在可变参数之前必有一个参数是固定的,并使用标识符, 而且通常被声明为 cha
2、r*类型,printf函数也不例外。这样的话,我们就可以通过这个参数对应 的标识符来得到地址,从而访问其他参数变得可能。我们可以写一个测试程序来试一下:#include <stdio.h>void va_test(char* fmt,);/参数可变的函数声明void main()int a=1,c=55;char b='b'va_test("",a,b,c);/ 用四个参数做测试void va_test(char* fmt,)参数可变的函数定义,注意第一个参数为char* fmtchar *p=NULL; |p=(char *)&fmt;
3、/注意不是指向fmt ,而是指向&fmt ,并且强制转化为char *,以便一个一个字节访问for(int i = 0;i<16;i+)/16是通过计算的值(参数个数*4个字节),只是为了测试,暂且将就一下printf("%.4d ",*p);/输出p指针指向地址的值P+;编译运行的结果为0056 0000 0066 0000 | 0001 0000 0000 0000 | 0098 0000 0000 0000 | 0055 0000 0000 0000由运行结果可见,通过这样方式可以逐一获得可变参数的值至于为什么通常被声明为char*类型,我们慢慢看来。2
4、,怎样确定参数类型和数量通过上述的方式,我们首先解决了取得可变参数值的问题,但是对于一个参数,值很重要,其类型同样举足轻重,而对于一个函数来讲参数个数也非常重要,否则就会产生了一系列的麻烦 来。通过访问存储参数的栈空间,我们并不能得到关于类型的任何信息和参数个数的任何信息。我想你应该想到了使用char *参数。Printf函数就是这样实现的,它把后面的可变参数类型都放到了 char *指向的字符数组里,并通过来标识以便与其它的字符相区别,从而确定了参数类型也确定了参数个数。其实,用何种方式来到达这样的效果取决于函数的实现。比如说,定义一个函数,预知它的可变参数类型都是int ,那么固定参数完全
5、可以用int类型来替换char*类型,因为只要得到参数个数就可以了。3,言归正传我想到了这里,大概的轮廓已经呈现出来了。本来想就此作罢的(我的惰性使然),但是一想到如果不具实用性便可能是一堆废物,枉费我打了这么些字,决定还是继续下去。我是比较抵制用那些不明所以的宏定义的,所以在上面的阐述里一点都没有涉及定义在stdarg.h的va(variable-argument) 宏。事实上,当时让我产生极大疑惑和好奇的正是这几个宏定义。但是现在我们不得不要去和这些宏定义打打交道,毕竟我们在讨生计的时候还得用上他们,这也是我曰之为官归正传”的理由。好了,我们来看一下那些宏定义。打开stdarg.h文件,找
6、一下 va_*的宏定义,发现不单单只有一组,但是在各组定义前都会 有宏编译。宏编译指示的是不同硬件平台和编译器下用怎样的va宏定义。比较一下,不同之处主要在偏移量的计算上。我们还是拿个典型又熟悉的一一X86的相关宏定义:1)typedef char * va_list;2)#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & (sizeof(int) -1)3)#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v)4)#define va_arg(ap,t) (
7、 *(t *)(ap += _INTSIZEOF(t) - _INTSIZEOF(t)5)#define va_end(ap) ( ap = (va_list)0 )我们逐一看来:第一个我想不必说了,类型定义罢了。第二个是颇有些来头的,我们也不得不搞懂它,因为后 面的两个关键的宏定义都用到了。不知道你够不够细心,有没有发现在上面的测试程序中,第 二个可变参数明明是char类型,可是在输出结果中占了4个byte。难道所有的参数都会占4个byte的空间?那如果是 double类型的参数,且不是会丢失数据!如果你不嫌麻烦的话,再 去做个测试吧,在上面的测试程序中用一个 double 类型(长度为 8
8、byte )和一个long double 类型(长度为 10byte )做可变参数。发现什么?double 类型占了 8byte,而long double 占了12byte o好像都是 4的整数倍哦。不得不引出另一个概念了对齐(alignment ) ”所谓对齐,对Intel80x86 机器来说就是要求每个变量的地址都是sizeof(int)的倍数。原来我们搞错了,char类型的参数只占了1byte ,但是它后面的参数因为对齐的关系只能跳过3byte存储,而那3byte也就浪费掉了。那为什么要对齐?因为在对齐方式下,CPU的运行效率要快得多(举个例子吧,要说明的是下面的例子是我从网上摘录下来的
9、,不记得出处了。示例:如下图,当一个long型数(如图中long1 )在内存中的位置正好与内存的字边界对齐时, CPU存取这个数只需访问一次内存,而当一个 long型数(如图中的10ng2 )在内存中的位置 跨越了字边界时,CPU存取这个数就需要多次访问内存,如i960cx访问这样的数需读内存三次(一个 BYTE、一个SHORT、一个BYTE ,由CPU的微代码执行,对软件透明),所以对齐方式下CPU的运行效率明显快多了g1 110ng1 110ng1 | long1 |110ng2 |110ng2 110ng2 110ng2 |.)。好像扯得有点远来,但是有助于对_
10、INTSIZEOF(n)的理解。位操作对于我来说是玄的东东。单个位运算还应付得来,而这样一个表达式摆在面前就晕了。怎么办?菜鸟自有菜的办法。(待续)Trackback: C语言中的可变参数函数 CSDN Blog 推出文章指数概念,文章指数是对 Blog文章综合评分后推算出的,综合评分项分别是该文章的点击量,回复次数,被网摘收录数量,文章长度和文 章类型;满分100,每月更新一次。|第一篇C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:int printf( const char* format,);它除了有一个参数format固定以外,后面跟的参数的个数
11、和类型是可变的(用三个点“彳故参数占位符),实际调用时可以有以下的形式:printf("%d",i);printf("%s",s);printf("the number is %d ,string is:%s", i, s);一个简单的可变参数的C函数先看例子程序。该函数至少有一个整数参数,其后占位符 ,表示后面参数的个数不定。在这个例子里,所有的输入参数必须都是整数,函数的功能只是打印所有参数的值。函数代码如 下:/示例彳码1:可变参数函数的使用#include "stdio.h"#include "s
12、tdarg.h"void simple_va_fun(int start,)va_list arg_ptr;int nArgValue =start;int nArgCout="0" /可变参数的数目va_start(arg_ptr,start); /以固定参数的地址为起点确定变参的内存起始地址。do+nArgCout;printf("the %d th arg: %d",nArgCout,nArgValue); /输出各参数的值nArgValue = va_arg(arg_ptr,int); /得到下一个可变参数的值 while(nArgVa
13、lue != -1);return;int main(int argc, char* argv口)simple_va_fun(100,-1);simple_va_fun(100,200,-1);return 0;下面解释一下这些代码。从这个函数的实现可以看到,我们使用可变参数应该有以下步骤:由于在程序中将用到以下这些宏:void va_start( va_list arg_ptr, prev_param );type va_arg( va_list arg_ptr, type );void va_end( va_list arg_ptr );va在这里是variable-argument(可变
14、参数)的意思。这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件。函数里首先定义一个va list型的变量,这里是arg ptr,这个变量是存储参数地址的指针.因为得到参数的地址之后,再结合参数的类型,才能得到参数的值。然后用va_start宏初始化中定义的变量arg_ptr,这个宏的第二个参数是可变参数列表的前一个参数,即最后一个固定参数。然后依次用 va_arg宏使arg_ptr返回可变参数的地址,得到这个地址之后,结合参数的类型, 就可以得到参数的值。设定结束条件,这里的条件就是判断参数值是否为-1 o注意被调的函数在调用时是不知道可变参数的正确数目的,程序员必须自
15、己在代码中指明结束条件。至于为什么它不会知道参数的 数目,在看完这几个宏的内部实现机制后,自然就会明白。第二篇C语言之可变参数问题C语言中有一种长度不确定的参数,形如: ”口,它主要用在参数个数不确定的函数中,我们 最容易想到的例子是printf函数。原型:int printf( const char *format , argument.);使用例:printf("Enjoy yourself everyday!n");printf("The value is %d!n", value);这种可变参数可以说是C语言一个比较难理解的部分,这里会由几个问题
16、引发一些对它的分析。注意:在 C+中有函数重载(overload )可以用来区别不同函数参数的调用,但它还是不 能表示任意数量的函数参数。问题:printf的实现请问,如何自己实现 printf函数,如何处理其中的可变参数问题?答案与分析:在标准C语言中定义了一个头文件专门用来对付可变参数列表,它包含了一组宏,和一个 va_list的typedef 声明。一个典型实现如下:typedef char* va_list;#define va_start(list) list = (char*)&va_alist#define va_end(list)#define va_arg(list,
17、 mode)(mode*) (list += sizeof(mode)-1自己实现printf :#includeint printf(char* format, )va_list ap;va_start(ap, format);int n = vprintf(format, ap);va_end(ap);return n;问题:运行时才确定的参数有没有办法写一个函数,这个函数参数的具体形式可以在运行时才确定?答案与分析:目前没有“正规”的解决办法,不过独门偏方倒是有一个,因为有一个函数已经给我们做出 了这方面的榜样,那就是main(),它的原型是:int main(int argc,char
18、 *argv口); 函数的参数是 argc和argv。深入想一下,”只能在运行时确定参数形式",也就是说你没办法从声明中看到所接受的参数,也即是参数根本就没有固定的形式。常用的办法是你可以通过定义一个void *类型的参数,用它来指向实际的参数区,然后在函数中根据根据需要任意解释它们的含义。这就是main函数中argv的含义,而argc,则用来表明实际的参数个数,这为我们使用提供了进一步的方便, 当然,这个参数不是必需的。虽然参数没有固定形式,但我们必然要在函数中解析参数的意义,因此,理所当然会有一 个要求,就是调用者和被调者之间要对参数区内容的格式,大小,有效性等所有方面达成一致,
19、 否则南辕北辙各说各话就惨了。问题:可变长参数的传递有时候,需要编写一个函数,将它的可变长参数直接传递给另外的函数,请问,这个要求 能否实现?答案与分析:目前,你尚无办法直接做到这一点,但是我们可以迂回前进,首先,我们定义被调用函数的参数为va_list类型,同时在调用函数中将可变长参数列表转换为va_list ,这样就可以进行变长参数的传递了。看如下所示:void subfunc (char *fmt, va_list argp)arg = va_arg (fmt, argp); /* 从argp中逐一取出所要的参数*/void mainfunc (char *fmt,)va_list ar
20、gp;va_start (argp, fmt); /*将可变长参数转换为 va_list */subfunc (fmt, argp); /*将 va_list 传递给子函数 */va_end (argp);.问题:可变长参数中类型为函数指针我想使用va arg来提取出可变长参数中类型为函数指针的参数,结果却总是不正确,为什么?答案与分析:这个与va_arg的实现有关。一个简单的、演示版的va_arg实现如下:#define va_arg(argp, type) (*(type *)(argp) += sizeof(type) - sizeof(type)其中,argp的类型是char *。如果
21、你想用va_arg从可变参数列表中提取出函数指针类型的参数,例如int (*)(),则 va_arg(argp, int (*)()被扩展为:(*(int (*)() *)(argp) += sizeof (int (*)() -sizeof (int (*)()显然,(int (*)() * )是无意义的。解决这个问题的办法是将函数指针用typedef定义成一个独立的数据类型,例如:typedef int (*funcptr)() ;这时候再调用 va_arg(argp, funcptr)将被扩展为:(* (funcptr *)(argp) += sizeof (funcptr) - siz
22、eof (funcptr)这样就可以通过编译检查了。问题:可变长参数的获取有这样一个具有可变长参数的函数,其中有下列代码用来获取类型为float的实参:va_arg (argp, float);这样做可以吗?答案与分析:不可以。在可变长参数中,应用的是“加宽"原则。也就是float类型被扩展成 double ; char,short被扩展成int。因此,如果你要去可变长参数列表中原来为float类型的参数,需要用va_arg(argp, double) 。对 char 和 short 类型的贝U用 va_arg(argp, int) 。问题:定义可变长参数的一个限制为什么我的编译器不
23、允许我定义如下的函数,也就是可变长参数,但是没有任何的固定参 数?int f (.) . 答案与分析:不可以。这是 ANSI C 所要求的,你至少得定义一个固定参数。这个参数将被传递给va start(),然后用va arg()和va end()来确定所有实际调用时可变长参数的类型和值。 如何判别可变参数函数的参数类型?函数形式如下:void fun(char* str,.) 若传的参数个数大于 1 ,如何判别第 2个以后传参的参数类型? ? ? 最好有源码说明!没办法判断的如楼上所说,例如printf( "%d%c%s ",.)是通过格式串中的 d,%c, %s来确定后面
24、参数的类型,其实你也可以参考这种方法来判断不定参数的类型无法判断。可变参数实现主要通过三个宏实现:va_start, va_arg, va_end六、扩展与思考个数可变参数在声明时只需""即可;但是,我们在接受这些参数时不能"."o va函数实现的关键就是如何得到参数列表中可选参数,包括参数的值和类型。以上的所有实现都是基于来自stdarg.h 的va_xxx的宏定义。思考 能不能不借助于va_xxx ,自己实现 VA呢?,我想到的方法是汇编。在 C中,我们当然就用C的嵌入汇编来实现,这应该是可以做得到的。至于能做到什么程度,稳定性和效率怎么样,主要要看
25、你对内存和指针的控制了。参考资料1.1 EEE 和 OpenGroup 联合开发的 Single Unix specification Ver3 ; BR>2 .Linux man 手册;3 .x86汇编,还有一些安全编码方面的资料。转帖对C/C+可变参数表的深层探索C/C+语言有一个不同于其它语言的特性,即其支持可变参数,典型的函数如printf、scanf等可以接受数量不定的参数。如:printf ( "I love you");printf ( "%d", a );printf ("%d,%d", a, b );第一、二、
26、三个 printf分别接受1、2、3个参数,让我们看看printf函数的原型:int printf ( const char *format,);从函数原型可以看出,其除了接收一个固定的参数format以外,后面的参数用"表示。在C/C+语言中,""表示可以接受不定数量的参数,理论上来讲,可以是 0或0以上的n个 参数本文将对C/C+可变参数表的使用方法及C/C+支持可变参数表的深层机理进行探索一.可变参数表的用法1、相关宏标准C/C+包含头文件stdarg.h ,该头文件中定义了如下三个宏:void va_start ( va_list arg_ptr, pre
27、v_param ); /返回值:求得的最大整数*/int max ( int num, .)int m = -0x7FFFFFFF; /* 32 系统中最小的整数 */ va_list ap;va_start ( ap, num );for ( int i= 0; i< num; i+ )/ .可以同数字一起显示,需设置标志(d、l、x、s)/extern void DrawText ( BYTE xPos, BYTE yPos, LPBYTE lpStr, .)BYTE lpData100; / 缓冲区 BYTE byIndex; ANSI version */ type va_arg
28、 ( va_list arg_ptr, type );void va_end ( va_list arg_ptr );在这些宏中,va就是variable argument(可变参数)的意思;arg_ptr是指向可变参数表的指针;prev param则指可变参数表的前一个固定参数;type为可变参数的类型。va list也是一个宏,其定义为typedef char * va list,实质上是一 char型指针。char型指针的特点是 +、操作对其作用的结果是增1和减1 (因为sizeof(char)为1),与之不同的是int等其它类型指针的+、-操作对其作用的结果是增sizeof(type)
29、 或减sizeof(type),而且sizeof (type) 大于1。通过va_start宏我们可以取得可变参数表的首指针,这个宏的定义为:#define va_start ( ap, v ) ( ap = (va_list)&v + _INTSIZEOF(v)显而易见,其含义为将最后那个固定参数的地址加上可变参数对其的偏移后赋值给ap,这样ap就是可变参数表的首地址。其中的 INTSIZEOF宏定义为:#define _INTSIZEOF(n) (sizeof ( n ) + sizeof ( int ) - 1 ) & ( sizeof( int ) - 1 )va_ar
30、g宏的意思则指取出当前arg_ptr所指的可变参数并将ap指针指向下一可变参数,其原型为:#define va_arg(list, mode) (mode *)(list =(char *) (int)list + (_builtin_alignof(mode)<=4?3:7)&(_builtin_alignof(mode)<=4?-4:-8)+sizeof(mode)-1对这个宏的具体含义我们将在后面深入讨论。而va_end宏被用来结束可变参数的获取,其定义为:#define va_end ( list )可以看出,va_end ( list )实际上被定义为空,没有任何
31、真实对应的代码,用于代码对称, 与va start对应;另外,它还可能发挥代码的"自注释”作用。所谓代码的"自注释",指的是代码能自己注释自己。下面我们以具体的例子来说明以上三个宏的使用方法。2、一个简单的例子#include <stdarg.h>/* 函数名:max*功能:返回n个整数中的最大值*参数:num :整数的个数 :num 个输入的整数int t = va_arg (ap, int);if ( t > m )m = t;va_end (ap);return m;/*主函数调用 max */int main ( int argc, ch
32、ar* argv口)int n = max ( 5, 5, 6 ,3 ,8 ,5); /* 求5个整数中的最大值*/cout << n;return 0;函数max中首先定义了可变参数表指针ap ,而后通过va_start ( ap, num ) 取得了参数表首地址(赋给了ap),其后的for循环则用来遍历可变参数表。这种遍历方式与我们在数据结构教材中经常看到的遍历方式是类似的。函数max看起来简洁明了,但是实际上printf的实现却远比这复杂。max函数之所以看起来简单,是因为:(1) max函数可变参数表的长度是已知的,通过 num参数传入;(2) max函数可变参数表中参数的
33、类型是已知的,都为 int型。而printf函数则没有这么幸运。首先,printf函数可变参数的个数不能轻易的得到,而可变参数的类型也不是固定的,需由格式字符串进行识别(由 f、d、s等确定),因此则涉及 到可变参数表的更复杂应用。下面我们以实例来分析可变参数表的高级应用。2 .高级应用下面这个程序是我们为某嵌入式系统(该系统中CPU的字长为16位)编写的在屏幕上显示格式字符串的函数 DrawText ,它的用法类似于int printf ( const char *format, .) 函数,但其输出的目标为嵌入式系统的液晶显示屏幕( LED )。/函数名称:DrawText/功能说明:在显
34、示屏上绘制文字/参数说明:xPos -横坐标的位置0 . 30/ yPos 纵坐标的位置0 . 64BYTE byLen;DWORD dwTemp;WORD wTemp;int i;va_list IpParam;memset( IpData, 0, 100);byLen = strlen( lpStr );byIndex = 0; |va_start ( lpParam, lpStr );for ( i = 0; i < byLen; i+ )if( lpStri !='喔是格式符开始lpDatabyIndex+ = lpStri;elseswitch (lpStri+1)/整
35、型case ' d':case ' U :wTemp = va_arg ( lpParam, int );byindex += IntToStr( IpData+bylndex, (DWORD)wTemp ); i+;break;/长整型case ' l':case ' L':dwTemp = va_arg ( lpParam, long );byindex += IntToStr ( lpData+byindex, (DWORD)dwTemp ); i+;break;/16进制(长整型)case ' x':case
36、39; X':dwTemp = va_arg ( lpParam, long );byindex += HexToStr ( lpData+bylndex, (DWORD)dwTemp ); i+;break;default:lpDatabyindex+ = lpStri; | break;va_end ( IpParam );lpData byindex = ' 0';DisplayString ( xPos, yPos, IpData, TRUE);/在屏幕上显示字符串IpData在这个函数中,需通过对传入的格式字符串(首地址为IpStr)进行识别来获知可变参数个数
37、及各个可变参数的类型,具体实现体现在for循环中。譬如,在识别为d后,做的是va_arg(IpParam, int ),而获知为 l和x后则进行的是 va arg ( IpParam, long )。格式字符串识别完成后,可变参数也就处理完了。一在项目的最初,我们一直苦于不能找到一个好的办法来混合输出字符串和数字,我们采用 了分别显示数字和字符串的方法,并分别指定坐标,程序条理被破坏。而且,在混合显示的时 候,要给各类数据分别人工计算坐标,我们感觉头疼不已。以前的函数为:/显示字符串showString ( BYTE xPos, BYTE yPos, LPBYTE IpStr )显示数字sho
38、wNum ( BYTE xPos, BYTE yPos, int num )|以16进制方式显示数字_|showHexNum ( BYTE xPos, BYTE yPos, int num )最终,我们用 DrawText ( BYTE xPos, BYTE yPos, LPBYTE IpStr, .)函数代替了原先所有的输出函数,程序得到了简化。就这样,兄弟们用得爽翻了。3 .运行机制探索通过第2节我们学会了可变参数表的使用方法,相信喜欢抛根问底的读者还不甘心,必然 想知道如下问题:(1)为什么按照第 2节的做法就可以获得可变参数并对其进行操作?_(2) C/C+在底层究竟是依靠什么来对这一
39、语法进行支持的,为什么其它语言就不能提供 可变参数表呢?我们带着这些疑问来一步步进行摸索。3.1调用机制反汇编反汇编是研究语法深层特性的终极良策,先来看看2.2节例子中主函数进行max ( 5, 5,6 ,3 ,8 ,5)调用时的反汇编:1.004010c8 push 52. 004010CA push 83. 004010CC push 34. 004010CE push 65. 004010D0 push 56. 004010D2 push 57. 004010D4 call ILT+5(max) (0040100a)从上述反汇编代码中我们可以看出,C/C+函数调用的过程中:第一步:将参数
40、从右向左入栈(第 16行);第二步:调用call指令进行跳转(第 7行)。这两步包含了深刻的含义,它说明C/C+默认的调用方式为由调用者管理参数入栈的操作,且入栈的顺序为从右至左,这种调用方式称为_cdecl调用。x86系统的入栈方向为从高地址到低地址,故第1至n个参数被放在了地址递增的堆栈内。在被调用函数内部,读取这些堆栈的内容就可获得各个参数的值,让我们反汇编到max函数的内部:int max ( int num,)1.00401020 push ebp2. 00401021 mov ebp,esp3. 00401023 sub esp,50h4. 00401026 push ebx5.
41、00401027 push esi6. 00401028 push edi7. 00401029 lea edi,ebp-50h8. 0040102C mov ecx,14h9. 00401031 mov eax,0CCCCCCCCh10. 00401036 rep stos dword ptr edi va_list ap;int m = -0x7FFFFFFF;/* 32 系统中最小的整数 */11. 00401038 mov dword ptr ebp-8,80000001hva_start ( ap, num );12. 0040103F lea eax,ebp+0Ch13. 0040
42、1042 mov dword ptr ebp-4,eaxfor ( int i= 0; i< num; i+ )14. 00401045 mov dword ptr ebp-0Ch,015. 0040104C jmp max+37h (00401057)16. 0040104E mov ecx,dword ptr ebp-0Ch17. 00401051 add ecx,118. 00401054 mov dword ptr ebp-0Ch,ecx19. 00401057 mov edx,dword ptr ebp-0Ch20. 0040105A cmp edx,dword ptr ebp+821. 0040105D jge max+61h (00401081)int t= va_arg (ap, int);22. 0040105F mov eax,dword ptr ebp-423. 00401062 add eax,424. 00401065 mov dword ptr ebp-4,eax25. 00401068 mov ecx,dword ptr
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 花字课件教学课件
- 吸墨白板课件教学课件
- 2024固定资产业权转让合同
- 2024年店铺买卖与租赁合同一本通
- 2024年广告装饰新篇章:工程合同全新范本
- 2024年办公室装修设计实施合同
- 2024年度供应链管理合同与物流服务协议
- 2024年工程项目人力资源配置与管理合同
- 2024年度国际广告传媒合作合同
- 2024光伏发电设备采购合同
- 银行业信息系统灾难恢复管理规范
- 医院重点岗位工作人员轮岗制度
- 2023光伏发电工程项目安全文明施工方案
- 带式输送机胶带安装
- 陈育民对FLAC3D常见问题的解答概要
- 专利文献检索方法与步骤课件
- 第5讲-申论大作文课件
- 大咯血的护理及急救课件
- 读《学生的精神》有感
- Module 5 Museums模块测试题二(含答案)(外研版九年级上册)
- 张家爷爷的小花狗2
评论
0/150
提交评论