C/C++中不定參數(shù)的使用詳解
C語(yǔ)言的不定參
C語(yǔ)言的不定參數(shù)最常見(jiàn)的應(yīng)用示例就是printf函數(shù),如下,參數(shù)列表中的...表示不定參數(shù)列表
#include <stdio.h> int printf(const char *format, ...);
試著模擬實(shí)現(xiàn)C語(yǔ)言的printf函數(shù)
void myprintf(const char *fmt, ...)
{
//TODO
}
C語(yǔ)言中,對(duì)于...不定參列表,要用va_*系列宏函數(shù)操作
#include <stdarg.h> ???????void va_start(va_list ap, last); type va_arg(va_list ap, type); void va_end(va_list ap); void va_copy(va_list dest, va_list src);
va_list
va_list可以理解為一個(gè)指針類(lèi)型,開(kāi)始時(shí),通過(guò)調(diào)用va_start,它會(huì)指向不定參列表的第一個(gè)參數(shù)
va_start
我們知道,C/C++的函數(shù)參數(shù)通過(guò)壓棧的方式傳入,不定參列表的多個(gè)參數(shù)也需要壓棧。

va_start的作用是使ap指向不定參列表的第一個(gè)參數(shù)。通過(guò)不定參數(shù)列表前的最后一個(gè)函數(shù)參數(shù),找到不定參列表的首元素,并使ap(va_list類(lèi)型的指針)指向首元素。因此,C函數(shù)使用...不定參數(shù),前面至少包含一個(gè)其它參數(shù),由它提供不定參數(shù)的起始位置。
va_arg
va_arg的作用是在不定參數(shù)列表中,從ap指向的參數(shù)開(kāi)始,逐個(gè)返回type類(lèi)型的數(shù)據(jù),一般需要循環(huán)調(diào)用va_arg,在此過(guò)程中ap會(huì)不斷往后走
va_end
ap使用結(jié)束后就失效了,為了避免野指針的引用,va_end可以銷(xiāo)毀ap指針
va_copy
拷貝src到dest,使得二者指向同一個(gè)不定參數(shù)列表,src不用再次調(diào)用va_start,但要調(diào)用va_end
Test Demo:
void myprintf(size_t num, ...)
{
va_list ap1;
va_start(ap1, num);
va_list ap2;
va_copy(ap2, ap1);
for (int i = 0; i < num; i++)
{
char ch = va_arg(ap1, int);
std::cout << ch;
}
std::cout << std::endl;
for (int i = 0; i < num; i++)
{
char ch = va_arg(ap2, int);
std::cout << ch;
}
std::cout << std::endl;
va_end(ap1);
va_end(ap2);
}
int main()
{
myprintf(3, 'L', 'O', 'L');
return 0;
}
輸出結(jié)果
[ckf@VM-8-12-centos 1.before]$ ./vargs
LOL
LOL
注意:va_arg對(duì)于可變參數(shù)的處理是有一些規(guī)則的,特別是對(duì)于小整數(shù)類(lèi)型(如char和float)。這些類(lèi)型在傳遞給va_arg時(shí)會(huì)被默認(rèn)轉(zhuǎn)換為int和double,而不是它們的實(shí)際類(lèi)型。因此,在Test Demo中,對(duì)于傳入的char類(lèi)型參數(shù),在調(diào)用va_arg時(shí)也應(yīng)該指定type為int,獲得返回值后再轉(zhuǎn)換為實(shí)際類(lèi)型char。
以下函數(shù)可以對(duì)不定參數(shù)列表進(jìn)行格式化操作。
#include <stdarg.h> int vprintf(const char *format, va_list ap); int vfprintf(FILE *stream, const char *format, va_list ap); int vsprintf(char *str, const char *format, va_list ap); int vsnprintf(char *str, size_t size, const char *format, va_list ap);
它們的作用與printf相同,都可以通過(guò)參數(shù)format,對(duì)不定參數(shù)進(jìn)行格式化。區(qū)別在于:printf使用不定參...作為格式化的數(shù)據(jù),而上面以v開(kāi)頭printf結(jié)尾的函數(shù),使用ap指向的不定參數(shù)列表作為格式化的數(shù)據(jù)。除此之外,內(nèi)部的區(qū)別就在于輸出的方向了,vprintf將格式化后的字符串輸出到stdout,vfprintf輸出到指定的文件流,vsprintf和vsprintf輸出到指定的內(nèi)存空間str,且后者可以指定輸出的字節(jié)個(gè)數(shù)。
Test Demo:
void myprintf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
char str[64] = {0};
int len = vsnprintf(str, sizeof(str), fmt, ap);
str[len] = 0;
std::cout << str << std::endl;
va_end(ap);
}
int main()
{
myprintf("%s %c %d", "你好", '!', 2024);
return 0;
}
輸出結(jié)果
[ckf@VM-8-12-centos 1.before]$ ./vargs
你好 ! 2024
C++的不定參
補(bǔ)充:
- args參數(shù)包作為參數(shù)傳遞給另一個(gè)函數(shù)時(shí),記得加...,表示這個(gè)args參數(shù)是不定參數(shù)包
- 函數(shù)接收Args...類(lèi)型的參數(shù)包時(shí),通??梢詫?shù)設(shè)置為&&萬(wàn)能引用,減少拷貝的成本
- C++11標(biāo)準(zhǔn)庫(kù)中,很多構(gòu)造方法都使用了以不定參數(shù)包為參數(shù)的條目,方便一些無(wú)法缺點(diǎn)參數(shù)個(gè)數(shù)的實(shí)例的構(gòu)造
例如:
std::make_shared,由于并不確定構(gòu)造智能指針指向的對(duì)象類(lèi)型需要傳入多少參數(shù),C++11使用了Args不定參數(shù)包
?

emplace函數(shù),C++11很多容器都重載了這個(gè)函數(shù),emplace支持可變參數(shù)包,并且用參數(shù)包中的參數(shù)作為新插入元素構(gòu)造函數(shù)的參數(shù)。

Test Demo
template <class T>
void myprintfcpp(T val) // 終止函數(shù)
{
std::cout << val;
}
template <class T, class... Args>
void myprintfcpp(T val, Args &&...args)
{
std::cout << val;
myprintfcpp(std::forward<Args>(args)...);
}
int main()
{
myprintfcpp(3, 'L', 'O', 'L');
std::cout << std::endl;
return 0;
}
輸出結(jié)果
[ckf@VM-8-12-centos 1.before]$ ./vargs
3LOL
到此這篇關(guān)于C/C++中不定參數(shù)的使用詳解的文章就介紹到這了,更多相關(guān)C++不定參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)二叉樹(shù)遍歷的迭代算法
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)二叉樹(shù)遍歷的迭代算法,包括二叉樹(shù)的中序遍歷、先序遍歷及后序遍歷等,是非常經(jīng)典的算法,需要的朋友可以參考下2014-09-09
詳解vs2022創(chuàng)建及調(diào)用.lib的方法
這篇文章主要介紹了vs2022創(chuàng)建及調(diào)用.lib的方法,調(diào)用Lib的原則就是可以讓編譯器找到頭文件和庫(kù)文件的目錄,并正確引入,本文給大家詳細(xì)講解需要的朋友可以參考下2022-11-11
C++控制臺(tái)循環(huán)鏈表實(shí)現(xiàn)貪吃蛇
這篇文章主要為大家詳細(xì)介紹了C++控制臺(tái)循環(huán)鏈表實(shí)現(xiàn)貪吃蛇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04
C/C++實(shí)現(xiàn)獲取硬盤(pán)序列號(hào)的示例代碼
獲取硬盤(pán)的序列號(hào)、型號(hào)和固件版本號(hào),此類(lèi)功能通常用于做硬盤(pán)綁定或硬件驗(yàn)證操作,下面我們就來(lái)學(xué)習(xí)一下如何使用C/C++實(shí)現(xiàn)獲取硬盤(pán)序列號(hào)吧2023-11-11

