C语言中可变参数的用法

上传人:文*** 文档编号:62429127 上传时间:2022-03-14 格式:DOCX 页数:11 大小:19.67KB
收藏 版权申诉 举报 下载
C语言中可变参数的用法_第1页
第1页 / 共11页
C语言中可变参数的用法_第2页
第2页 / 共11页
C语言中可变参数的用法_第3页
第3页 / 共11页
资源描述:

《C语言中可变参数的用法》由会员分享,可在线阅读,更多相关《C语言中可变参数的用法(11页珍藏版)》请在装配图网上搜索。

1、文档供参考,可复制、编制,期待您的好评与关注! C語言中可變参數的用法va_list、va_start、va_arg、va_end参數定義C語言可變参簡介 我們在C語言編程中會遇到一些参數個數可變的函數,例如printf()這個函數,它的定義是這样的: int printf C語言可變参簡介 我們在C語言編程中會遇到一些参數個數可變的函數,例如printf()這個函數,它的定義是這样的: int printf( const char* format, .);它除了有一個参數format固定以外,後面跟的参數的個數和類型是可變的,例如我們可以有以下不同的調用方法: printf(%d,i); p

2、rintf(%s,s); printf(the number is %d ,string is:%s, i, s); 寫一個簡單的可變参數的C函數下面我們來探討如何寫一個簡單的可變参數的C函數.寫可變参數的C函數要在程序中用到以下這些宏: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(可變参數)的意思.這些宏定義在stdarg.h中,所以用到可變参數的程序應該包含這個

3、頭文件.下面我們寫一個簡單的可變参數的函數,改函數至少有一個整數参數,第二個参數也是整數,是可選的.函數只是打印這兩個参數的值.void simple_va_fun(int i, .) va_list arg_ptr; int j=0; va_start(arg_ptr, i); j=va_arg(arg_ptr, int); va_end(arg_ptr); printf(%d %dn, i, j); return; 我們可以在我們的頭文件中這样聲明我們的函數: extern void simple_va_fun(int i, .); 我們在程序中可以這样調用: simple_va_fun(

4、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宏結束可變参數的獲取.然後你就可以在函數裏使用第二個参數了.如果函數有多個可變参數的,依次調用va_arg獲取各個参數. 如果

5、我們用下面三種方法調用的話,都是合法的,但結果卻不一样:1)simple_va_fun(100); 結果是:100 -123456789(會變的值)2) simple_va_fun(100,200); 結果是:100 2003)simple_va_fun(100,200,300); 結果是:100 200 我們看到第一種調用有錯誤,第二種調用正確,第三種調用盡管結果正確,但和我們函數最初的設計有沖突.下面我們探討出現這些結果的原因和可變参數在編譯器中是如何處理的。 可變参數在編譯器中的處理我們知道va_start,va_arg,va_end是在stdarg.h中被定義成宏的,由於1)硬件平台的

6、不同 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(t) - _INTSIZEOF(t) ) #define va_end(ap) ( a

7、p = (va_list)0 )定義_INTSIZEOF(n)主要是为了某些需要內存的對齊的系統.C語言的函數是從右向左壓入堆棧的,圖(1)是函數的参數在堆棧中的分布位置.我們看到va_list被定義成char*,有一些平台或操作系統定義为void*.再看va_start的定義,定義为&v+_INTSIZEOF(v),而&v是固定参數在堆棧的地址,所以我們運行va_start(ap, v)以後,ap指向第一個可變参數在堆棧的地址,如圖:高地址|-| |函數返回地址 | |-| |. | |-| |第n個参數(第一個可變参數) | |-| -va_start後ap指向 |第n-1個参數(最後一個

8、固定参數)|低地址|-| - &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指向 |第n個参數(第一個可變参數) | |-|

9、 -va_start後ap指向 |第n-1個参數(最後一個固定参數)|低地址|-| - &v 圖(2)最後要說的是va_end宏的意思,x86平台定義为ap=(char*)0;使ap不再指向堆棧,而是跟NULL一样.有些直接定義为(void*)0),這样編譯器不會为va_end產生代碼,例如gcc在linux的x86平台就是這样定義的.在這裏大家要注意一個問題:由於参數的地址用於va_start宏,所以参數不能聲明为寄存器變量或作为函數或數組類型.關於va_start, va_arg, va_end的描述就是這些了,我們要注意的是不同的操作系統和硬件平台的定義有些不同,但原理卻是相似的. 可變

10、参數在編程中要注意的問題因为va_start, va_arg, va_end等定義成宏,所以它顯得很愚蠢,可變参數的類型和個數完全在該函數中由程序代碼控制,它並不能智能地識別不同参數的個數和類型.有人會問:那麼printf中不是實現了智能識別参數嗎?那是因为函數printf是從固定参數format字符串來分析出参數的類型,再調用va_arg的來獲取可變参數的.也就是說,你想實現智能識別可變参數的話是要通過在自己的程序裏作判斷來實現的.另外有一個問題,因为編譯器對可變参數的函數的原型檢查不夠嚴格,對編程查錯不利.如果simple_va_fun()改为:void simple_va_fun(int

11、 i, .)va_list arg_ptr;char *s=NULL;va_start(arg_ptr, i);s=va_arg(arg_ptr, char*);va_end(arg_ptr);printf(%d %sn, i, s);return;可變参數为char*型,當我們忘記用兩個参數來調用該函數時,就會出現core dump(Unix) 或者頁面非法的錯誤(window平台).但也有可能不出錯,但錯誤卻是難以發現,不利於我們寫出高質量的程序.以下提一下va系列宏的兼容性.System V Unix把va_start定義为只有一個参數的宏:va_start(va_list arg_pt

12、r);而ANSI C則定義为:va_start(va_list arg_ptr, prev_param);如果我們要用system V的定義,應該用vararg.h頭文件中所定義的宏,ANSI C的宏跟system V的宏是不兼容的,我們一般都用ANSI C,所以用ANSI C的定義就夠了,也便於程序的移植. 小結: 1、標准C庫的中的三個宏的作用只是用來確定可變参數列表中每個参數的內存地址,編譯器是不知道参數的實際數目的。 2、在實際應用的代碼中,程序員必須自己考慮確定参數數目的辦法,如 在固定参數中設標志- printf函數就是用這個辦法。 在預先設定一個特殊的結束標記,就是說多輸入一個可

13、變参數,調用時要將最後一個可變参數的值設置成這個特殊的值,在函數體中根據這個值判斷是否達到参數的結尾。本文前面的代碼就是采用這個辦法. 無論采用哪種辦法,程序員都應該在文檔中告訴調用者自己的約定。 3、實現可變参數的要點就是想辦法取得每個参數的地址,取得地址的辦法由以下幾個因素决定: 函數棧的生長方向 参數的入棧順序 CPU的對齊方式 內存地址的表達方式 結合源代碼,我們可以看出va_list的實現是由决定的,_INTSIZEOF(n)的引入則是由决定的,他和又一起决定了va_start的實現,最後va_end的存在則是良好編程風格的體現,將不再使用的指針設为NULL,這样可以防止以後的誤操作。 4、取得地址後,再結合参數的類型,程序員就可以正確的處理参數了。理解了以上要點,相信稍有經驗的讀者就可以寫出适合於自己機器的實現來。可變参數的函數原理其實很簡單,而va系列是以宏定義來定義的,實現跟堆棧相關.我們寫一個可變函數的C函數時,有利也有弊,所以在不必要的場合,我們無需用到可變参數.如果在C+裏,我們應該利用C+的多態性來實現可變参數的功能,盡量避免用C語言的方式來實現.From:CSDN 11 / 11

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!