ImageVerifierCode 换一换
格式:DOCX , 页数:10 ,大小:27.01KB ,
资源ID:16215655      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/16215655.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(c可变参数Word下载.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

c可变参数Word下载.docx

1、%ld,Sum);Sum = 6二、写一个简单的可变参数的C函数 先看例子程序。该函数至少有一个整数参数,其后占位符,表示后面参数的个数不定. 在这个例子里,所有的输入参数必须都是整数,函数的功能只是打印所有参数的值.函数代码如下:/示例代码1:可变参数函数的使用#include stdio.hstdarg.hvoid simple_va_fun(int start, .) va_list arg_ptr;int nArgValue =start;int nArgCout=0;/可变参数的数目va_start(arg_ptr,start); /以固定参数的地址为起点确定变参的内存起始地址。do

2、 +nArgCout;the %d th arg: %d,nArgCout,nArgValue);/输出各参数的值nArgValue = va_arg(arg_ptr,int);/得到下一个可变参数的值 while(nArgValue != -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_

3、ptr, prev_param );type va_arg( va_list arg_ptr, type );void va_end( va_list arg_ptr );va在这里是variable-argument(可变参数)的意思. 这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件.函数里首先定义一个va_list型的变量,这里是arg_ptr,这个变量是存储参数地址的指针.因为得到参数的地址之后,再结合参数的类型,才能得到参数的值。然后用va_start宏初始化中定义的变量arg_ptr,这个宏的第二个参数是可变参数列表的前一个参数,即最后一个固定参数. 然后依

4、次用va_arg宏使arg_ptr返回可变参数的地址,得到这个地址之后,结合参数的类型,就可以得到参数的值。设定结束条件,这里的条件就是判断参数值是否为-1。注意被调的函数在调用时是不知道可变参数的正确数目的,程序员必须自己在代码中指明结束条件。至于为什么它不会知道参数的数目,读者在看完这几个宏的内部实现机制后,自然就会明白。 (二)可变参数在编译器中的处理 我们知道va_start,va_arg,va_end是在stdarg.h中被定义成宏的, 由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下面看一下VC+6.0中stdarg.h里的代码(文件的路径为VC安装目录下的vc

5、98includestdarg.h)typedef char *va_list;#define _INTSIZEOF(n) (sizeof(n) + sizeof(int) - 1) & (sizeof(int) - 1) )#define va_start(ap,v)( ap = (va_list)&v + _INTSIZEOF(v) )#define va_arg(ap,t)( *(t *)(ap += _INTSIZEOF(t) - _INTSIZEOF(t) )#define va_end(ap)( ap = (va_list)0 )下面我们解释这些代码的含义:1、首先把va_list

6、被定义成char*,这是因为在我们目前所用的PC机上,字符指针类型可以用来存储内存单元地址。而在有的机器上va_list是被定义成void*的2、定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.这个宏的目的是为了得到最后一个固定参数的实际内存大?gt;谖业幕 魃现苯佑胹izeof运朔 创 妫 猿绦虻脑诵薪峁挂裁挥杏跋臁笪慕 吹轿易约旱氖迪郑 ?3、va_start的定义为 &v+_INTSIZEOF(v) ,这里&v是最后一个固定参数的起始地址,再加上其实际占用大小后,就得到了第一个可变参数的起始内存地址。所以我们运行va_start(ap, v)以后,ap指向第一个可变参数

7、在的内存地址,有了这个地址,以后的事情就简单了。这里要知道两个事情:在intel+windows的机器上,函数栈的方向是向下的,栈顶指针的内存地址低于栈底指针,所以先进栈的数据是存放在内存的高地址处。(2)在VC等绝大多数C编译器中,默认情况下,参数进栈的顺序是由右向左的,因此,参数进栈以后的内存模型如下图所示:最后一个固定参数的地址位于第一个可变参数之下,并且是连续存储的。|最后一个可变参数-高内存地址处.第N个可变参数va_arg(arg_ptr,int)后arg_ptr所指的地方,即第N个可变参数的地址。| |.第一个可变参数va_start(arg_ptr,start)后arg_ptr

8、所指的地方即第一个可变参数的地址| |最后一个固定参数 start的起始地址| |.| | 低内存地址处(4) va_arg():有了va_start的良好基础,我们取得了第一个可变参数的地址,在va_arg()里的任务就是根据指定的参数类型取得本参数的值,并且把指针调到弦桓霾问 钠鹗嫉刂贰?因此,现在再来看va_arg()的实现就应该心中有数了:这个宏做了两个事情,用用户输入的类型名对参数地址进行强制类型转换,得到用户所需要的值计算出本参数的实际大小,将指针调到本参数的结尾,也就是下一个参数的首地址,以便后续处理。(5)va_end宏的解释:x86平台定义为ap=(char*)0;使ap不再

9、 指向堆栈,而是跟NULL一样.有些直接定义为(void*)0),这样编译器不会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的. 在这里大家要注意一个问题:由于参数的地址用于va_start宏,所以参数不能声明为寄存器变量或作为函数或数组类型. 关于va_start, va_arg, va_end的描述就是这些了,我们要注意的 是不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.(三)可变参数在编程中要注意的问题 因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢, 可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能

10、地识别不同参数的个数和类型. 有人会问:那么printf中不是实现了智能识别参数吗?那是因为函数 printf是从固定参数format字符串来分析出参数的类型,再调用va_arg 的来获取可变参数的.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的. 例如,在C的经典教材the c programming language的7.3节中就给出了一个printf的可能实现方式,由于篇幅原因这里不再叙述。(四)小结:1、标准C库的中的三个宏的作用只是用来确定可变参数列表中每个参数的内存地址,编译器是不知道参数的实际数目的。2、在实际应用的代码中,程序员必须自己考虑确定参数数

11、目的办法,如在固定参数中设标志 printf函数就是用这个办法。后面也有例子。在预先设定一个特殊的结束标记,就是说多输入一个可变参数,调用时要将最后一个可变参数的值设置成这个特殊的值,在函数体中根据这个值判断是否达到参数的结尾。本文前面的代码就是采用这个办法.无论采用哪种办法,程序员都应该在文档中告诉调用者自己的约定。3、实现可变参数的要点就是想办法取得每个参数的地址,取得地址的办法由以下几个因素决定:函数栈的生长方向参数的入栈顺序CPU的对齐方式内存地址的表达方式结合源代码,我们可以看出va_list的实现是由决定的,_INTSIZEOF(n)的引入则是由决定的,他和又一起决定了va_sta

12、rt的实现,最后va_end的存在则是良好编程风格的体现,将不再使用的指针设为NULL,这样可以防止以后的误操作。4、取得地址后,再结合参数的类型,程序员就可以正确的处理参数了。理解了以上要点,相信稍有经验的读者就可以写出适合于自己机器的实现来。下面臼且桓隼 ?(五)扩展自己实现简单的可变参数的函数。下面是一个简单的printf函数的实现,参考了中的156页的例子,读者可以结合书上的代码与本文参照。stdlib.hvoid myprintf(char* fmt, .)/一个简单的类似于printf的实现,/参数必须都是int 类型char* pArg=NULL;/等价于原来的va_list c

13、har c;pArg = (char*) &fmt;/注意不要写成p = fmt !因为这里要对/参数取址,而不是取值pArg += sizeof(fmt);/等价于原来的va_startdoc =*fmt;if (c != %)putchar(c);/照原样输出字符else/按格式字符输出数据switch(*+fmt) case d:,*(int*)pArg);break;x%#xdefault: pArg += sizeof(int);/等价于原来的va_arg+fmt;while (*fmt !0);pArg = NULL;/等价于va_endint i = 1234;int j = 5

14、678;myprintf(the first test:i=%d,i,j);the secend test:i=%d; %x;j=%d;,i,0xabcd,j);system(pause在intel+win2k+vc6的机器执行结果如下:i=1234i=1234; 0xabcd;j=5678;#include /不定数目参数需要的宏int max(int n,int num,.)va_list x;/说明变量xva_start(x,num);/x被初始化为指向num后的第一个参数int m=num;for(int i=1;im)m=y;va_end(x);/清除变量xreturn m;main

15、()%d,%d,max(3,5,56),max(6,0,4,32,45,533);第二个:C/C+语言中可变参数的用法 我们在C语言编程中会遇到一些参数个数可变的函数,例如printf() 这个函数,它的定义是这样的:它除了有一个参数format固定以外,后面跟的参数的个数和类型是 可变的,例如我们可以有以下不同的调用方法:究竟如何写可变参数的C函数以及这些可变参数的函数编译器是如何实 现的呢?本文就这个问题进行一些探讨,希望能对大家有些帮助.会C+的 网友知道这些问题在C+里不存在,因为C+具有多态性.但C+是C的一个 超集,以下的技术也可以用于C+的程序中.限于本人的水平,文中如果有 不当

16、之处,请大家指正. (一)写一个简单的可变参数的C函数 下面我们来探讨如何写一个简单的可变参数的C函数.写可变参数的 C函数要在程序中用到以下这些宏:这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个 头文件.下面我们写一个简单的可变参数的函数,改函数至少有一个整数 参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值. void simple_va_fun(int i, .) int j=0;va_start(arg_ptr, i);j=va_arg(arg_ptr, int);va_end(arg_ptr);%d %dn, i, j);我们可以在我们的头文件中这样

17、声明我们的函数:extern void simple_va_fun(int i, .);我们在程序中可以这样调用:simple_va_fun(100);simple_va_fun(100,200);1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变 量是指向参数的指针. 2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第 一个可变参数的前一个参数,是一个固定的参数. 3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个 参数是你要返回的参数的类型,这里是int型. 4)最后用va_end宏结束可变参数的获取.然后你就可

18、以在函数里使 用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获 取各个参数. 如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:1)simple_va_fun(100);结果是:100 -123456789(会变的值) 2)simple_va_fun(100,200);100 200 3)simple_va_fun(100,200,300);我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果 正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果 的原因和可变参数在编译器中是如何处理的. (二)可变参数在编译器中的处理 我们知道va_start,v

19、a_arg,va_end是在stdarg.h中被定义成宏的, 由于1)硬件平台的不同 2)编译器的不同,所以定义的宏也有所不同,下 面以VC+中stdarg.h里x86平台的宏定义摘录如下(号表示折行):typedef char * va_list;#define _INTSIZEOF(n) (sizeof(n)+sizeof(int)-1)&(sizeof(int) - 1) ) #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) ) #define va_arg(ap,t) ( *(t *)(ap += _INTSIZEOF(

20、t) - _INTSIZEOF(t) ) #define va_end(ap) ( ap = (va_list)0 ) 定义_INTSIZEOF(n)主要是为了某些需要内存的对齐的系统.C语言的函 数是从右向左压入堆栈的,图(1)是函数的参数在堆栈中的分布位置.我 们看到va_list被定义成char*,有一些平台或操作系统定义为void*.再 看va_start的定义,定义为&v+_INTSIZEOF(v),而&v是固定参数在堆栈的 地址,所以我们运行va_start(ap, v)以后,ap指向第一个可变参数在堆 栈的地址,如图:高地址|-| |函数返回地址 | |-| |. | |第n个参

21、数(第一个可变参数) | |-|-va_start后ap指向 |第n-1个参数(最后一个固定参数)| 低地址|-|- &v 图( 1 ) 然后,我们用va_arg()取得类型t的可变参数值,以上例为int型为例,我 们看一下va_arg取int型的返回值:j= ( *(int*)(ap += _INTSIZEOF(int)-_INTSIZEOF(int) );首先ap+=sizeof(int),已经指向下一个参数的地址了.然后返回 ap-sizeof(int)的int*指针,这正是第一个可变参数在堆栈里的地址 (图2).然后用*取得这个地址的内容(参数值)赋给j. -va_arg后ap指向 图( 2 ) 最后要说的是va_end宏的意思,x86平台定义为ap=(char*)0;使ap不再 指向堆栈,而是跟NULL一样.有些直接定义为(void*)0),这样编译器不 会为va_end产生代码,例如gcc在linux的x86平台就是这样定义的. 在这里大家要注意一个问题:由于参数的地址用于va_start宏,所 以参数不能声明为寄存器变量或作为函数或数组类型. 关于va_start, va_arg, va_end的描

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1