最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C/C++中可變參數(shù)的用法詳細解析

 更新時間:2013年09月22日 09:08:14   投稿:jingxian  
可變參數(shù)的使用方法遠遠不止以下介紹的幾種,不過在C,C++中使用可變參數(shù)時要小心,在使用printf()等函數(shù)時傳入的參數(shù)個數(shù)一定不能比前面的格式化字符串中的’%’符號個數(shù)少,否則會產(chǎn)生訪問越界,運氣不好的話還會導(dǎo)致程序崩潰

可變參數(shù)即表示參數(shù)個數(shù)可以變化,可多可少,也表示參數(shù)的類型也可以變化,可以是int,double還可以是char*,類,結(jié)構(gòu)體等等。可變參數(shù)是實現(xiàn)printf(),sprintf()等函數(shù)的關(guān)鍵之處,也可以用可變參數(shù)來對任意數(shù)量的數(shù)據(jù)進行求和,求平均值帶來方便(不然就用數(shù)組或每種寫個重載)。在C#中有專門的關(guān)鍵字parame,但在C,C++并沒有類似的語法,不過幸好提供這方面的處理函數(shù),本文將重點介紹如何使用這些函數(shù)。
 
第一步 可變參數(shù)表示
用三個點…來表示,查看printf()函數(shù)和scanf()函數(shù)的聲明:
int printf(const char *, ...);
int scanf(const char *, ...);

這三個點用在宏中就是變參宏(Variadic Macros),默認名稱為__VA_ARGS__。如:
#define WriteLine(...) { printf(__VA_ARGS__); putchar('\n');}
再WriteLine("MoreWindows");

考慮下printf()的返回值是表示輸出的字節(jié)數(shù)。將上面宏改成:
#define WriteLine (...) printf(__VA_ARGS__) + (putchar('\n') != EOF ? 1: 0);
這樣就可以得到WriteLine宏的返回值了,它將返回輸出的字節(jié)數(shù),包括最后的’\n’。如下例所示i和j都將輸出12。

復(fù)制代碼 代碼如下:

       int i = WriteLine("MoreWindows");
       WriteLine("%d", i);
       int j = printf("%s\n", "MoreWindows");
       WriteLine("%d", j);

第二步 如何處理va_list類型
函數(shù)內(nèi)部對可變參數(shù)都用va_list及與它相關(guān)的三個宏來處理,這是實現(xiàn)變參參數(shù)的關(guān)鍵之處。
在<stdarg.h>中可以找到va_list的定義:
typedef char *  va_list;
再介紹與它關(guān)系密切的三個宏要介紹下:va_start(),va_end()和va_arg()。

同樣在<stdarg.h>中可以找到這三個宏的定義:
#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_end(ap)      ( ap = (va_list)0 )
#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
其中用到的_INTSIZEOF宏定義如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

來分析這四個宏:
va_end(ap)這個最簡單,就是將指針置成NULL。
va_start(ap,v)中ap = (va_list)&v + _INTSIZEOF(v)先是取v的地址,再加上_INTSIZEOF(v)。_INTSIZEOF(v)就有點小復(fù)雜了。( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )全是位操作,看起來有點麻煩,其實不然,非常簡單的,就是取整到sizeof(int)。比如sizeof(int)為4,1,2,3,4就取4,5,6,7,8就取8。對x向n取整用C語言的算術(shù)表達就是((x+n-1)/n)*n,當n為2的冪時可以將最后二步運算換成位操作——將最低 n - 1個二進制位清 0就可以了。

va_arg(ap,t)就是從ap中取出類型為t的數(shù)據(jù),并將指針相應(yīng)后移。如va_arg(ap, int)就表示取出一個int數(shù)據(jù)并將指針向移四個字節(jié)。

因此在函數(shù)中先用va_start()得到變參的起始地址,再用va_arg()一個一個取值,最后再用va_end()收尾就可以解析可變參數(shù)了。
 
第三步 vfprintf()函數(shù)和vsprintf()函數(shù)
vfprintf()這個函數(shù)很重要,光從名字上看就知道它與經(jīng)常使用的printf()函數(shù)有很大的關(guān)聯(lián)。它有多個重載版本,這里講解最常用的一種:
函數(shù)原型

復(fù)制代碼 代碼如下:

int vfprintf(

   FILE *stream,

   const char *format,

   va_list argptr

);


第一個參數(shù)為一個FILE指針。FILE結(jié)構(gòu)在C語言的讀寫文件必不可少。要對屏幕輸出傳入stdout。
第二個參數(shù)指定輸出的格式。
第三個參數(shù)是va_list類型,這個少見,但其實就是一個char*表示可變參參數(shù)的起始地址。
返回值:成功返回輸出的字節(jié)數(shù)(不包括最后的’\0’),失敗返回-1。
vsprintf()與上面函數(shù)類似,就只列出函數(shù)原型了:
復(fù)制代碼 代碼如下:

int vsprintf(

   char *buffer,

   const char *format,

   va_list argptr

);


還有一個int _vscprintf(const char *format, va_list argptr );可以用來計算vsprintf()函數(shù)中的buffer字符串要多少字節(jié)的空間。

代碼范例
下面就給出了自己實現(xiàn)的printf()函數(shù)(注1)與WriteLine()函數(shù)
 

復(fù)制代碼 代碼如下:

 int Printf(char *pszFormat, ...) 
{
       va_list   pArgList;
      
       va_start(pArgList, pszFormat);
       int nByteWrite = vfprintf(stdout, pszFormat, pArgList);
       va_end(pArgList);
 
       return nByteWrite;
}
 
int WriteLine(char *pszFormat, ...)
{
       va_list   pArgList;
      
       va_start(pArgList, pszFormat);
       int nByteWrite = vfprintf(stdout, pszFormat, pArgList);
       if (nByteWrite != -1)
              putchar('\n'); //注2
       va_end(pArgList);
      
       return (nByteWrite == -1 ? -1 : nByteWrite + 1);
}

調(diào)用與printf()函數(shù)相同。
再給出一個用可變參數(shù)來求和,遺憾的在C,C++中無法確定傳入的可變參數(shù)的個數(shù)(printf()中是通過掃描'%'個數(shù)來確實參數(shù)的個數(shù)的),因此要么就要指定個數(shù),要么在參數(shù)的最后要設(shè)置哨兵數(shù)值:
設(shè)置哨兵數(shù)值:
復(fù)制代碼 代碼如下:

const int GUARDNUMBER = 0; //哨兵標識
//變參參數(shù)的個數(shù)無法確定,在printf()中是通過掃描'%'個數(shù),在這通過設(shè)置哨兵標識來確定變參參數(shù)的終止
int MySum(int i, ...)
{
       int sum = i;
       va_list argptr;
      
       va_start(argptr, i);
       while ((i = va_arg(argptr, int)) != GUARDNUMBER)
              sum += i;
       va_end(argptr);
      
       return sum;
}

可以這樣的調(diào)用:   printf("%d\n", MySum(1, 3, 5, 7, 9, 0));
但不可以直接傳入一個0:   printf("%d\n", MySum(0)); //error
指定個數(shù):
復(fù)制代碼 代碼如下:

int MySum(int nCount, ...)
{
       if (nCount <= 0)
              return 0;
 
       int sum = 0;
       va_list argptr;
      
       va_start(argptr, nCount);
       for (int i = 0; i < nCount; i++)
              sum += va_arg(argptr, int);
       va_end(argptr);
      
       return sum;
}

調(diào)用時第一個參數(shù)表示后面參數(shù)的個數(shù)如:
       printf("%d\n", MySum(5, 1, 3, 5, 7, 9));
       printf("%d\n", MySum(0));

代碼所用的頭文件:
#include <stdarg.h>
#include <stdio.h>
 
可變參數(shù)的使用方法遠遠不止上述幾種,不過在C,C++中使用可變參數(shù)時要小心,在使用printf()等函數(shù)時傳入的參數(shù)個數(shù)一定不能比前面的格式化字符串中的’%’符號個數(shù)少,否則會產(chǎn)生訪問越界,運氣不好的話還會導(dǎo)致程序崩潰。
 
可變參數(shù)的原形理涉及到調(diào)用函數(shù)時參數(shù)的入棧問題,這個下次再開一篇進行專門的探討。
 
注1.網(wǎng)上有不用vfprintf()自己解析參數(shù)來實現(xiàn)printf()的,但很少能將功能做到與printf()相近(實際上能完全熟悉printf()的人已經(jīng)就不多,不信的話可以先看看《C陷阱與缺陷》了解printf()很多不太常用的參數(shù),再去Microsoft Visual Studio\VC98\CRT\SRC中查看OUTPUT.C對printf()的實現(xiàn))。

注2.如果輸出單個字符 putchar(ch)會比printf(“%c”, ch)效率高的多。在字符串不長的情況下,多次調(diào)用putchar()也會比調(diào)用printf(“%s\n”, szStr);的效率高。在函數(shù)大量調(diào)用時非常明顯。

相關(guān)文章

  • C語言指針的圖文詳解

    C語言指針的圖文詳解

    這篇文章主要為大家介紹了C語言指針,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • C++ Primer 標準庫vector示例詳解

    C++ Primer 標準庫vector示例詳解

    該文章主要介紹了C++標準庫中的vector類型,包括其定義、初始化、成員函數(shù)以及常見操作,文章詳細解釋了如何使用vector來存儲和操作對象集合,并提供了代碼示例來說明vector的使用方法,感興趣的朋友一起看看吧
    2025-03-03
  • MFC控件之CListCtrl的應(yīng)用實例教程

    MFC控件之CListCtrl的應(yīng)用實例教程

    這篇文章主要介紹了MFC控件中CListCtrl的應(yīng)用方法,包括了針對表格的一些操作,是MFC中比較重要的一個控件類,需要的朋友可以參考下
    2014-08-08
  • Linux下用C語言實現(xiàn)推箱子游戲

    Linux下用C語言實現(xiàn)推箱子游戲

    這篇文章主要為大家詳細介紹了Linux下用C語言實現(xiàn)小老鼠推箱子的游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • VS2022添加代碼模板的實現(xiàn)步驟(圖文)

    VS2022添加代碼模板的實現(xiàn)步驟(圖文)

    使用代碼模板即可實現(xiàn)像內(nèi)置函數(shù)那樣,只需寫幾個字母,便能提示自動補全,本文主要介紹了VS2022添加代碼模板的實現(xiàn)步驟,感興趣的可以了解一下
    2024-06-06
  • C++?opencv實現(xiàn)幾何圖形繪制

    C++?opencv實現(xiàn)幾何圖形繪制

    這篇文章主要為大家介紹了C++?opencv實現(xiàn)幾何圖形的繪制示例實現(xiàn)代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • Qt多版本共存使用實現(xiàn)組件增刪

    Qt多版本共存使用實現(xiàn)組件增刪

    本文主要介紹了Qt多版本共存使用實現(xiàn)組件增刪,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • C++實現(xiàn)LeetCode(139.拆分詞句)

    C++實現(xiàn)LeetCode(139.拆分詞句)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(139.拆分詞句),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C++讀取文本文件中的漢字亂碼情況原因及解決

    C++讀取文本文件中的漢字亂碼情況原因及解決

    本文介紹簡體中文Windows操作系統(tǒng)中,C++讀取文本文件中的漢字亂碼情況原因及解決,文中通過代碼和圖文給大家介紹的非常詳細,具有一定的參考價值,需要的朋友可以參考下
    2024-01-01
  • 利用C++和QT實現(xiàn)Log自定義日志系統(tǒng)

    利用C++和QT實現(xiàn)Log自定義日志系統(tǒng)

    這篇文章主要為大家詳細介紹了如何利用C++和QT實現(xiàn)Log自定義日志系統(tǒng),文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考下
    2023-12-12

最新評論

兴隆县| 沧源| 宜章县| 敖汉旗| 鹤庆县| 莱西市| 柳河县| 宁津县| 富民县| 平舆县| 江孜县| 拉萨市| 怀柔区| 北票市| 康马县| 红河县| 衡水市| 黔东| 波密县| 台东县| 望都县| 安阳县| 连州市| 鹤岗市| 临朐县| 凉城县| 扬州市| 固始县| 邯郸县| 吴川市| 淳安县| 自贡市| 仪征市| 茶陵县| 泰来县| 克拉玛依市| 淅川县| 从江县| 屏南县| 交口县| 大同县|