欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
【洛谷日報#226】C 可變參數入門(mén)

由于本人是個(gè)蒟蒻,并且語(yǔ)文不好,所以如果本文有任何錯誤,請指出,我將盡快修正。

1.可能需要的知識

  • 子函數的編寫(xiě)(必需)

  • C++的類(lèi)型

  • 強制類(lèi)型轉換

  • 指針/迭代器

  • auto

  • 模板

2.引言

假如wenge問(wèn)你:

如何不用for,求出2個(gè)數的最大值?

你說(shuō),這還不簡(jiǎn)單,

ans=max(a,b);

wenge又問(wèn)你:

如何不用for,求出3個(gè)數的最大值?

你說(shuō),這還不簡(jiǎn)單,

ans=max(a,max(b,c));

wenge又問(wèn)你:

如何不用for,求出10個(gè)數的最大值?

你說(shuō),這還不簡(jiǎn)單,

ans=max(a[1],max(a[2],max(a[3],max(a[4],max(a[5],max(a[6],max(a[7],max(a[8],max(a[9],a[10])))))))));

wenge說(shuō)這太亂了。

你說(shuō),這個(gè)總行了吧,

ans=max(ans,a[1]);
ans=max(ans,a[2]);
ans=max(ans,a[3]);
ans=max(ans,a[4]);
ans=max(ans,a[5]);
ans=max(ans,a[6]);
ans=max(ans,a[7]);
ans=max(ans,a[8]);
ans=max(ans,a[9]);
ans=max(ans,a[10]);

能不能一行寫(xiě)完呢?

能。

ans=max({a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10]});//C++11

但是庫函數max為什么可以為什么可以這樣使用呢?

好了,現在讓我們進(jìn)入正題——C++的兩種可變參數。

3.C++ STL的可變參數(initializer_list)

注意:此節為C++11的新特性,編譯時(shí)請增加命令行選項-std=c++11(這個(gè)貌似大家都懂)

1)什么是initializer_list

首先讓我們看看max的函數模板聲明。

注意到用紅色框起來(lái)的函數模板聲明。

這個(gè)函數以initializer_list為參數。initializer_list是啥?

就是一個(gè)初始化表。初始化表是啥?讓我們看一個(gè)例子。

{1,2,3,4,5}

什么?你告訴我這個(gè)就是initializer_list?讓我們再看一個(gè)例子。

vector<int> a={1,2,3,4,5};

如果你還不知道initializer_list是啥,那就再看一個(gè)例子。

int a[5]={1,2,3,4,5};

你可能會(huì )問(wèn)了,這不是數組賦值嗎?

但是,這個(gè)數組賦值用了初始化表,所以{1,2,3,4,5}就是一個(gè)initializer_list。

initializer_list是C++11的一個(gè)新特性。這玩意除了能給數組賦值以外,還可以給自己定義的結構體賦值。比如

#include <iostream>
#include <initializer_list>
struct point{
    int x,y;
};

int main(){
    point a={1,2};
    cout<<a.x<<' '<<a.y;
    return 0;
}

程序會(huì )輸出1 2。然而我們要講的是initializer_list實(shí)現可變參數。

2)initializer_list實(shí)現可變參數

initializer_list實(shí)現可變參數的方式,就是把initializer_list作為函數的參數。

現在首先看一看本人實(shí)現的max函數:

#include <iostream>
#include <algorithm>
#include <initializer_list>
using namespace std;
//C++ STL的可變參數 

int mymax(initializer_list<int> a){
    int ans=-2147483648;//int的最小值
    for(auto i:a){
        ans=max(i,ans);
    }
    return ans;
}

int main(){
    int a=1,b=2,c=3,d=4,e=5;
    cout<<mymax({a,b,c,d,e});
    return 0;
}

程序輸出了5,因為a,b,c,d,e的最大值就是5。

現在讓我們看看mymax這個(gè)函數是如何實(shí)現的。

首先定義了一個(gè)臨時(shí)變量ans存儲答案。

現在來(lái)看for。也許可能有人不明白這個(gè)for是什么意思。實(shí)際上這個(gè)for的用途是遍歷整個(gè)initializer_list。

比較麻煩的一點(diǎn)是,簡(jiǎn)單的遍歷一個(gè)initializer_list只有兩種方式,一種是使用C++11的auto新特性(也就是代碼里的for(auto i:a)),另外一個(gè)是使用迭代器。使用迭代器的方法是這樣的:

int mymax(initializer_list<int> a){
    int ans=-2147483648;//int的最小值
    for(initializer_list<int>::iterator i=a.begin();i!=a.end();i++){
        ans=max(*i,ans);
    }
    return ans;
}

看上去很麻煩。但是initializer_list<int>::iterator這東西可以換成auto。但是即使initializer_list<int>::iterator可以換成auto,個(gè)人還是推薦使用for(auto i:a)。

但是上述方法只能遍歷整個(gè)initializer_list容器。如何遍歷一個(gè)initializer_list容器的一部分?一個(gè)簡(jiǎn)單的方法是建立一個(gè)數組,把initializer_list中所有的數據拷貝進(jìn)這個(gè)數組里。

一個(gè)initializer_list的大小可以用initializer_list.size()函數來(lái)獲取。initializer_list類(lèi)型的size()函數與stringvectorsize()的作用完全一致,即返回initializer_list中的元素個(gè)數??纯聪旅娴睦樱?/p>

#include <iostream>
#include <algorithm>
#include <initializer_list>
using namespace std;

int b[10];

int average(initializer_list<int> a){
    int ans=0;
    int j=1;
    for(auto i:a){
        b[j]=i;
        j++;
    }
    sort(b+1,b+a.size()+1);
    for(int i=2;i<a.size();i++){
        ans+=b[i];
    }
    ans/=(a.size()-2);
    return ans;
}
struct point{
    int x,y;
};

int main(){
    int a=80,b=10,c=40,d=40,e=70;
    cout<<average({a,b,c,d,e});
    return 0;
}

這個(gè)例子是求多個(gè)數的去除一個(gè)最大值和一個(gè)最小值的平均值??梢钥吹?,我們將initializer_list類(lèi)型的a拷貝進(jìn)了預先定義的數組b,再在數組b上執行sort()。

你可以在單個(gè)函數使用多個(gè)initializer_list作為參數。比如

void print(initializer_list<int> a,initializer_list<char> b);//print()的具體定義略
int main(){
    print({1,2,3},{'a','b','c'});
    return 0;
}

這樣使用是合法的。

并且你可以增加一個(gè)模板,使一個(gè)initializer_list可以支持同種不同類(lèi)型的數據:

template<typename T>
void print(initializer_list<T> a);//print()的具體定義略
int main(){
    print({114514,1919,810});
    print({'c','h','a','r'});
    print({'xyzzy','plugh','abracadabra'});
    return 0;
}

3)類(lèi)型轉換問(wèn)題以及其他需要注意的地方

首先,因為initializer_list是拿大括號括起來(lái)的,所以傳入一個(gè)initializer_list參數的時(shí)候要拿大括號括起來(lái)。

C++標準要求“initializer_list的元素類(lèi)型都必須相同,但編譯器將進(jìn)行必要的轉換”、“但不能進(jìn)行隱式的窄化轉換”(C++ Primer)。比如下面的代碼:

double a[5]={1.14514,2.33,-3.456789,4.0,5};
//int類(lèi)型的5被編譯器隱式轉換成double類(lèi)型的5.0 

long long b[5]={114514ll,233ll,-3456789ll,4ll,5};
//int類(lèi)型的5被編譯器隱式轉換成long long類(lèi)型的5 

int c[5]={114514,233,-3456789,4,5.0};
//double類(lèi)型的5.0被編譯器轉換成int,屬于隱式窄化轉換,錯誤 

但是在C++ Primer里所說(shuō)的這個(gè)錯誤并不一定導致CE,比如本人在gcc4.9.2編譯,只出現了一個(gè)warning。但是為了避免潛在的CE,應該盡量避免類(lèi)型轉換問(wèn)題。

除了類(lèi)型轉換問(wèn)題,initializer_list的元素類(lèi)型都必須相同,所以不能將不同類(lèi)型的元素裝進(jìn)initializer_list里。例如下列代碼將會(huì )導致CE:

void print(initializer_list<int> a);//print()的具體定義略
int main(){
    print('string');
    return 0;
}

另外,即使你使用了多個(gè)initializer_list,由于每個(gè)initializer_list的元素類(lèi)型都必須相同,每個(gè)initializer_list只能處理一種類(lèi)型的數據。這導致了一般的initializer_list實(shí)現的可變參數只能支持單種類(lèi)型的數據。

4.C的可變參數(va_list)

1)從scanf和printf說(shuō)起

另外,說(shuō)起可變參數,我們可能想到最多的例子就是scanfprintf。實(shí)際上,它們的確是真真正正的可變參數函數。而scanfprintf是C的原生函數,其誕生早在initializer_list之前。那么scanfprintf是如何實(shí)現的呢?

還是看一下函數聲明。

那個(gè)...是什么?

沒(méi)錯,那就是可變參數的標志。

但是那些參數叫什么呢?

沒(méi)有名字。

那怎么讀取它們呢?

va_list。

2)va_list實(shí)現可變參數

C/C++函數可以通過(guò)在其普通參數后添加逗號和三個(gè)點(diǎn)(,...)的方式來(lái)接受數量不定的附加參數,而無(wú)需相應的參數聲明。

特別的,雖然直接使用三個(gè)點(diǎn)作為函數的參數是合法的,但是這些參數并不能被讀?。ê竺鏁?huì )說(shuō)明原因)。不推薦使用這樣的函數。

注意三個(gè)點(diǎn)必須加在參數列表的最后。

例如:

void print(int count,...);//合法
void print(...);//合法

void print(int count,...,int count2);//非法,CE

C/C++頭文件<stdarg.h><cstdarg>提供了對可變參數的支持。va_list是一個(gè)類(lèi)型,定義在頭文件<stdarg.h><cstdarg>中。<stdarg.h>中定義了3個(gè)宏,與va_list配套使用,分別是va_list,va_start,va_argva_end。在C++中,C++11標準又新增了宏va_copy。所以,va_list,va_start,va_argva_end是C與C++通用的。

讓我們看一個(gè)使用va_list實(shí)現可變參數的例子:

#include <iostream>
#include <cstdarg>
using namespace std;

void printint(int count,...){
    va_list a; 
    va_start(a,count);
    for(int i=1;i<=count;i++){
        int b=va_arg(a,int);
        cout<<b<<' ';
    }
    va_end(a);
}
int main(){
    printint(5,114514,233,-3456789,4,5);
    return 0;
}

這個(gè)函數從參數讀取數量等同于參數count的整數并輸出這個(gè)程序將會(huì )輸出114514 233 -3456789 4 5。

  1. 在函數體內,我們首先定義了一個(gè)va_list類(lèi)型的a。a即是printint()的參數表,即其包含了包括count在內的所有參數。你可以把va_list看做一個(gè)棧(實(shí)際上也是這么存儲的),參數從右至左入棧。

  2. 然后,我們調用了va_start宏。va_start宏有兩個(gè)參數,第一個(gè)需要寫(xiě)我們之前創(chuàng )建的va_list的名字(在這個(gè)例子里是a),第二個(gè)參數則寫(xiě)...之前的上一個(gè)參數(在這個(gè)例子里是count)。你可以想象有一個(gè)指針,一開(kāi)始什么都不指向(即指向NULL),我們調用va_start宏,則這個(gè)指針就指向了棧頂,并且一直彈棧,直到指針指向了...之前的上一個(gè)參數(在這個(gè)例子里是count)的位置。

  3. 然后,我們使用va_arg宏讀取函數的參數。va_arg宏有兩個(gè)參數,第一個(gè)需要寫(xiě)我們之前創(chuàng )建的va_list的名字,第二個(gè)填寫(xiě)當前所要讀取的參數的類(lèi)型(這也是scanf為什么有那么多占位符的原因,因為它必須獲取參數的類(lèi)型)。你可以認為這個(gè)宏先進(jìn)行彈棧,然后讀取棧頂內容并返回。注意,va_arg的第二個(gè)參數中,char,char_16t,wchar_t,short以及其signed,unsigned版本要寫(xiě)成int,float要寫(xiě)成double,char_32t要寫(xiě)成unsigned long。原因是C/C++的默認類(lèi)型轉換。否則必定RE。這個(gè)東西gcc會(huì )給出warning。除此之外,如果第二個(gè)參數的實(shí)際類(lèi)型不同于va_arg的第二個(gè)參數,這個(gè)參數會(huì )被吃掉。

  4. 最后,使用va_end宏結束函數參數的讀取。你可以認為這個(gè)宏使指針重新指向NULL,并且刪除賦予va_list的內存,使可變參數函數能正確返回。有助于代碼的健壯。

同時(shí)這個(gè)函數也可以使用while完成:

#include <iostream>
#include <cstdarg>
using namespace std;

void printint(int first,...){
    va_list a; 
    int b=first;
    va_start(a,first);
    while(b!=-1){
        cout<<b<<' ';
        b=va_arg(a,int);
    }
    va_end(a);
}
int main(){
    printint(114514,233,-3456789,4,5,-1);
    return 0;
}

實(shí)際上,va_list的優(yōu)點(diǎn)在于其可以接受不同類(lèi)型的參數,前提是你知道這些參數的類(lèi)型。下面是一個(gè)例子,是本人實(shí)現的printf,只實(shí)現了%d%c

#include <iostream>
#include <cstdarg>
using namespace std;

void myprintf(string f,...){
    va_list a; 
    va_start(a,f);
    for(int i=0;i<f.size();i++){
        if(f[i]=='%'){
            i++;
            if(f[i]=='d'){;
                cout<<va_arg(a,int);
            }
            if(f[i]=='c'){
                cout<<char(va_arg(a,int));
            }
        }
        else cout<<f[i];
    }
    va_end(a);
}
int main(){
    myprintf('test\n%d%c',114514,'A');
    return 0;
}

這東西也可以有模板。但是由于類(lèi)型轉換方面的問(wèn)題,要想支持int以下的類(lèi)型,恐怕是得寫(xiě)一大堆的特化了。(想一想,怎么寫(xiě))由于作者太懶,就不寫(xiě)了

那么va_copy呢?

實(shí)際上,如果你定義多個(gè)va_list,只有你定義的第一個(gè)va_list里面有那些參數的數據。va_copy有兩個(gè)參數,類(lèi)型都是va_list,用途是把第二個(gè)va_list的數據拷貝進(jìn)第一個(gè)va_list里。

如果參數類(lèi)型都相同的話(huà)誰(shuí)會(huì )用這東西呢?還不如把這些參數拷貝進(jìn)數組里(滑稽)所以這個(gè)東西用來(lái)重復處理具有多個(gè)數據類(lèi)型的可變參數時(shí)會(huì )很方便。比如下面這個(gè)輸出兩遍的printf

#include <iostream>
#include <cstdarg>
using namespace std;

void myprintf(string f,...){
    va_list a,b; 
    va_copy(b,a);
    va_start(a,f);
    for(int i=0;i<f.size();i++){
        if(f[i]=='%'){
            i++;
            if(f[i]=='d'){;
                cout<<va_arg(a,int);
            }
            if(f[i]=='c'){
                cout<<char(va_arg(a,int));
            }
        }
        else cout<<f[i];
    }
    va_end(a);
    va_start(b,f);
    for(int i=0;i<f.size();i++){
        if(f[i]=='%'){
            i++;
            if(f[i]=='d'){;
                cout<<va_arg(b,int);
            }
            if(f[i]=='c'){
                cout<<char(va_arg(b,int));
            }
        }
        else cout<<f[i];
    }
    va_end(b);
}
int main(){
    myprintf('test\n%d%c',114514,'A');
    return 0;
}

3)其他

之前我們已經(jīng)知道了,直接使用三個(gè)點(diǎn)作為函數的參數是合法的,但是這些參數并不能被讀取。為什么呢?因為如果有一個(gè)直接使用三個(gè)點(diǎn)作為參數的函數,則沒(méi)有...之前的上一個(gè)參數,va_start將無(wú)法使用。然而親測不使用va_start會(huì )RE,所以這種函數的參數并不能被讀取。

之前我們已經(jīng)知道了,va_arg的第二個(gè)參數中,char,char_16t,wchar_t,short以及其signed,unsigned版本要寫(xiě)成int,float要寫(xiě)成double,char_32t要寫(xiě)成unsigned long。原因是C/C++的默認類(lèi)型轉換。否則必定RE。這個(gè)東西gcc會(huì )給出warning。除此之外,如果第二個(gè)參數的實(shí)際類(lèi)型不同于va_arg的第二個(gè)參數,這個(gè)參數會(huì )被吃掉。然而,寫(xiě)成int,va_arg讀取的也是int。比如下面的手寫(xiě)printf的錯誤例子,不會(huì )輸出char類(lèi)型的A,而會(huì )輸出int類(lèi)型的65A的ASCII碼)。 所以想出默認類(lèi)型轉換這個(gè)餿主意的人真是個(gè)大銻。

#include <iostream>
#include <cstdarg>
using namespace std;

void myprintf(string f,...){
    va_list a; 
    va_start(a,f);
    for(int i=0;i<f.size();i++){
        if(f[i]=='%'){
            i++;
            if(f[i]=='d'){;
                cout<<va_arg(a,int);
            }
            if(f[i]=='c'){
                cout<<va_arg(a,int);
                //沒(méi)有轉換,正確寫(xiě)法實(shí)際上是用強制類(lèi)型轉換
                //cout<<char(va_arg(a,int));
            }
        }
        else cout<<f[i];
    }
    va_end(a);
}
int main(){
    myprintf('%c','A');
    return 0;
}

所以,如果有使用更低等的類(lèi)型(比如char)的必要,使用強制類(lèi)型轉換。

在函數最后,一定要調用va_end。

不要越界!不要越界??!不要越界?。?!

你們的程序里有千萬(wàn)塊內存。只要不越界,這個(gè)系統就無(wú)法檢測到非法讀寫(xiě)。

如果越界,非法讀寫(xiě)將被檢測到,系統的保護模塊將會(huì )觸發(fā),你們的程序將會(huì )RE!

不要越界!不要越界??!不要越界?。?!

然后就沒(méi)了。

5.參考資料

http://www.cplusplus.com/

C++ Primer

va_start和va_end使用詳解

C可變參數的實(shí)現


洛谷日報接受投稿,采用后有薄禮奉送,請戳 

https://www.luogu.org/discuss/show/47327 .
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
C和指針之函數之求參數列表中的最大值
av_list
C |可變參數函數的實(shí)現及其不安全性
va_list、va_start、va_arg、va_end
C語(yǔ)言之可變參數問(wèn)題 - 歡迎閣下光臨我的網(wǎng)絡(luò )日志-safeking‘s blog - 博...
變參函數的實(shí)現
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久