第一篇:對于C語言中的scanf函數(shù)的使用問題總結(jié)
Scanf函數(shù)總結(jié)
對于C語言中的scanf函數(shù)的小結(jié)
對于scanf函數(shù)的作用大家應該都知道吧。在任何一本C語言的教材中都有過介紹,它的一般形式是(格式控制,地址表列)
我今天想說說使用這個scanf函數(shù)的時候,應該注意哪些問題吧。
1.scanf函數(shù)中的格式控制應該是變量名的地址,而不是變量名,例如a和b為整形變量,如果寫成scanf(“%d%d”,a,b);就會出錯的,應該將這個,a,b改成&a,&b;(表示地址)2我們都知道C語言中的字符串是存放到字符數(shù)組中的,如果我們定義了一個字符數(shù)組,如char a[20];我們想把字符串輸入到這個字符數(shù)組中通常有兩種方式,第一種.逐個字符輸入,采用for循環(huán)的形式。第二種,直接輸入字符串的形式,用格式控制符%s.第一種我們這樣輸入,for(i=0;i<=19;i++){ } 第二種我們用這種方式輸入 Scanf(“%s”,a);注意第二種這種形式,我們沒有加&,這是為什么呢,這是 Scanf(“%c”,&a[i]);
Scanf函數(shù)總結(jié)
因為,C語言中把這個數(shù)組名當做該數(shù)組的起始地址。但是這種數(shù)組僅限于字符數(shù)組,不要企圖,利用數(shù)值型數(shù)組的名。來整體輸入,一個數(shù)值型的數(shù)組,這就是大錯誤了,比如這樣寫是不正確的 Int a[10];Scanf(“%d”,a);數(shù)值型數(shù)組的輸入只能采用上面的第一種方法。3對于指針問題,大家需要注意一點,指針就是地址,這是在任何條件下都成立的,(請允許我極端一點),比方看下面的例子。Char *p;Char a[20];P=a;For(i=0;i<20;i++,p++){ } 這是一個采用指針變量輸入數(shù)據(jù)的例子,這里的指針變量p在for循環(huán)之前就指向了這個數(shù)組a,也就是指向了數(shù)組的首元素,也就是是說指針變量p里存放的是,數(shù)組首元素的地址,所以在用這個scanf函數(shù)進行輸出時,指針變量的前方就不用再加取地址符號了。Scanf(“%c”,p);
Scanf函數(shù)總結(jié)
4.這個問題是整個scanf函數(shù)使用過程中最容易出錯的一個地方。就是關于用scanf函數(shù)在用“%c”格式聲明輸入字符的時候,是最容易出錯的一個地方。一點一點進行,下面請看一個簡單的例子: 看下面的一個程序,(1)
#include
} 我們想要輸入三個整數(shù),輸入的時候我們要注意了,這里的scanf函數(shù)中的格式控制是三個連續(xù)的%d,除了格式聲明以外中間沒有任何其他的字符,這個時候我們輸入數(shù)據(jù)應該加入空格,否則執(zhí)行不了。1 2 3(1,2之間有空格,2,3之間有空格,(具體原因會在例題2中闡明)加入其他符號也是不可以的)
(2)經(jīng)過第一道的例題,似乎讓我明白了這樣的一個規(guī)律,如果中間都是除了格式聲明以外中間沒有任何其他的字符,int a,b,c;scanf(“%d%d%d”,&a,&b,&c);printf(“%d%d%dn”,a,b,c);return 0;
Scanf函數(shù)總結(jié)
那我就可以用空格,來隔開我的數(shù)據(jù)了??纯聪旅娴睦} #include
} 我的輸入還是仿照上面,進行截圖如下所示 char a,b,c;scanf(“%c%c%c”,&a,&b,&c);printf(“%c%c%cn”,a,b,c);return 0;
我們發(fā)現(xiàn)輸出并不是我們想象的那樣,而是出現(xiàn)了錯誤,下面我們來解釋一下原因,因為我們原來輸入的位數(shù)值型的數(shù)據(jù),所以我們要區(qū)分數(shù)值的個數(shù),以及位數(shù),比方我想輸入
Scanf函數(shù)總結(jié) 234 456這三個數(shù),我要是連續(xù)的把這8個數(shù)輸入進去,計算機也無法區(qū)分,這到底是幾個數(shù),所以我們用空格加以區(qū)分,這樣計算機遇到空格就知道這個數(shù)值型的數(shù)已經(jīng)輸入完畢了(這是例題1中我們?yōu)槭裁醇涌崭竦脑颍菫槭裁丛诶}2中我還是這樣做,就不可以了呢,這是因為我們現(xiàn)在輸入的是字符型數(shù)據(jù),而且字符型數(shù)據(jù)不是像數(shù)值型數(shù)據(jù)那樣,有幾位數(shù)字,字符型的數(shù)據(jù)只有一位,所以你輸入一位數(shù)據(jù)他就給一個變量,所以不需要像數(shù)值型的數(shù)據(jù)那樣隔開了,所以我們直接輸入三個連續(xù)的字母asd就可以了,就不用空格分開了,如果我們輸入a空格s空格d,那么他就會默認的把前三個字符送給三個變量了,也就是a空格s(因為空格也是一個字符)。(3)下面大家看一下這道例題。
//下面程序很簡單,就是輸入兩個數(shù)和一個運算符號,#include“stdio.h” void main(){
int;float z,x,y;char cc;printf(“please enter date:n”);scanf(“%f%c%f”,&x,&cc,&y);
Scanf函數(shù)總結(jié)
} if(cc=='+')z=x+y;if(cc=='-')z=x-y;if(cc=='*')z=x*y;if(cc=='/')z=x/y;printf(“%fn”,z);我們連續(xù)輸入三個1+2 結(jié)果輸出3,截圖如下
這里要注意的是,輸入1之后不能有空格,因為下面是一個字符型數(shù)據(jù),輸入字符型數(shù)據(jù)之后,就可以有空格了,因為
Scanf函數(shù)總結(jié)
后面是一個數(shù)值型數(shù)據(jù)。(所以按照這種方式輸入也可以 1+ 2)(4),如果在“格式控制字符串中”除了格式聲明還有其它字符,則在輸入數(shù)據(jù)的時候,在對應的位置上應該輸入與這些字符相同的字符。#include
} 我們在輸入數(shù)據(jù)的時候應該這樣輸入,1,2,3 我要講的內(nèi)容就要講完了,下面和大家說說在輸入這個數(shù)據(jù)的時候,盡量加入,分隔,scanf(“%d,%d,%d”,&a,&b,&c);這樣不容易出錯,如果什么符號也不用直接這樣的話scanf(“%d%d%d”,&a,&b,&c);大家一定要注意我上面說的三個例子。int a,b,c;scanf(“%d,%d,%d”,&a,&b,&c);printf(“%d%d%dn”,a,b,c);return 0;7
第二篇:C語言中的輸入輸出函數(shù)總結(jié)
putchar():把變量中的一個字符常量輸出到顯示器屏幕上;getchar();從鍵盤上輸入一個字符常量,此常量就是該函數(shù)的值;printf();把鍵盤中的各類數(shù)據(jù),加以格式控制輸出到顯示器屏幕上;scanf();從鍵盤上輸入各類數(shù)據(jù),并存放到程序變量中;puts():把數(shù)組變量中的一個字符串常量輸出到顯示器屏幕上;gets():從鍵盤上輸入一個字符串常量并放到程序的數(shù)組中.sscanf();從一個字符串中提取各類數(shù)據(jù)。putchar()輸出一個字符
getchar()輸入流中獲取一個字符 例如:
char c = getchar();putchar(c);格式化輸入輸出scanf()和printf()是最有用的,所以重點講一下。printf(): 一般形式: printf(“格式控制”.輸出列表);eg : printf(“a=%d,b=%f,c=%cn”,a,b,c);1;格式控制.格式控制是用雙引號括起來的字符串,也稱“轉(zhuǎn)換控制字符串”,它包含以下兩部分信息.格式說明:由“%”和格式字符組成,如%d,%f,%c,他的作用是把輸出數(shù)據(jù)轉(zhuǎn)換為指定格式輸出,格式的說明總是由“%”字符開始的.普通字符:需要原樣輸出的字符,或者是一些有特殊含義的字符,如n,t。2;輸出列表
就是需要輸出的一些數(shù)據(jù),也可以是表達式,如果在函數(shù)中需要輸出多個變量或表達式,則要用逗號隔開.一些特殊字符的輸出:
單引號,雙引號,和反斜杠的輸出在前面加轉(zhuǎn)義字符”” 如:”’” , “”” , “”
%的輸出用兩個連在一起的%%,即printf(“%%”);
常用的格式說明如下: 格式字符
d 以十進制形式輸出帶符號整數(shù)(正數(shù)不輸出符號)o 以八進制形式輸出無符號整數(shù)(不輸出前綴O)x 以十六進制形式輸出無符號整數(shù)(不輸出前綴OX)u 以十進制形式輸出無符號整數(shù) f 以小數(shù)形式輸出單精度實數(shù) lf 以小數(shù)形式輸出雙精度實數(shù)
e 以指數(shù)形式輸出單、雙精度實數(shù)
g 以%f%e中較短的輸出寬度輸出單、雙精度實數(shù) c 輸出單個字符 s 輸出字符串
這里強調(diào)一下:網(wǎng)上很多文章都說f 和lf是一樣的,即不管單精度,雙精度浮點數(shù),都可以用f, 但我在POJ上做過測試,輸出Double時用f確實也可以,但讀入時,用f就報WA,所以大家如果對Double進行讀寫的話,都用lf吧。
說到Double,再啰嗦一句,建議大家要用到浮點數(shù)時都用Double,不要用float,因為在很多情況下,float精度不夠會導致WA。
特殊:
對64位整數(shù)的輸入輸出,在POJ上的C++環(huán)境下(即VC),64位整數(shù)是: __int64(注意int前面是兩個下劃線)輸入輸出格式為”%I64d”.在G++環(huán)境下(即Dev C++)64位整數(shù)是 long long 輸入輸出格式為”%lld”.輸出寬度
用十進制整數(shù)來表示輸出的最少位數(shù)。注意若實際位數(shù)多于定義的寬度,則按實際位數(shù)輸出,若實際位數(shù)少于定義的寬度則補以空格或0。
精度
精度格式符以“.”開頭,后跟十進制整數(shù)。意義是:如果輸出數(shù)字,則表示小數(shù)的位數(shù);如果輸出的是字符,則表示輸出字符的個數(shù);若實際位數(shù)大于所定義的精度數(shù),則截去超過的部分。
標志格式字符
-結(jié)果左對齊,右邊填空格
+ 輸出符號(正號或負號)空格輸出值為正時冠以空格,為負時冠以負號 例如:
double c=24212345.24232;printf(“%020.4”);表示輸出精確到小數(shù)點后4位,輸出占20位,若有空余的位補0.scanf:
scanf的很多用法都是和printf對應的,故不再贅述。
說一下scanf一個特別好用的地方,就是可以濾去一些不想要的東西。舉例說明如下:
比如輸入為日期 yyyy-mm-dd,就可以這樣寫: int year,moth,day;scanf(“%d-%d-%d”,&year,&moth,&day);再比如:
scanf(“%3d %*3d %2d”,&m,&n);輸入113 118 69回車(系統(tǒng)將113賦予m,將69賦予n,因為*號表示跳過它相應的數(shù)據(jù)所以118不賦予任何變量)puts()用的不多,且基本都能用printf()代替,故不再多說。gets()是從輸入流中獲取一行字符串放入字符數(shù)組中: char in[100];gets(in);大家可能最容易出錯的地方就是字符串的輸入,所以強調(diào)一下: 能進行字符,字符串輸入的有:
getchar(), scanf(“%c”);scanf(“%s”), gets()
其中getchar()和 scanf(“%c”)的功能是一樣的。
需要注意的是,這兩個函數(shù)讀入的是輸入流中當前位置的字符,比如:
scanf(“%d”,&n);c = getchar();假設輸入 67/(假設“/”代表回車),則第一個scanf讀入一個整數(shù)67后,當前輸入流的位置是67之后,即指向回車符,所以第二個getchar()讀入的就是一個回車符了,即 c = ‘n’。
同樣,gets()也是從當前位置讀入一行字符串。比如:
scanf(“%d”,&n);gets(str);此時讀入字符數(shù)組中的字符串就是“n” 了
所以通常在用scanf讀入一個非字符串的類型之后,如果要讀入字符,或字符數(shù)組,都用一個額外的getchar()把回車符讀掉,若后面跟的不止一個回車符,可能還有多余的空格的話,就用gets()讀掉。
和以上不同的是,scanf(“%s”)讀入的時候是會忽略掉空格,回車和制表符的。并且以空格,回車和制表符作為字符串結(jié)束的標志。
經(jīng)常會有這樣的題,輸入第一行是一個整數(shù),接下來每行的第一個是一個字符,用來表示某種操作,后面再跟一些數(shù)據(jù),比如: 4 A 100 2 B 23 A 23 89 B 34
像這種輸入就需要小心,讀入字符時不要讀成回車符。為了防止意外,我一般是這樣處理這類輸入的: char model[2];Scanf(“%d”,&n);for(?,?,?){
scanf(“%s”,model);
if(model[0] == ‘A’){ } else{ } } sscanf(): sscanf()經(jīng)常用來分解字符串,功能非常強大,但很多功能都需要正則表達式的知識,所以就介紹一下最簡單的幾種用法,大家如果想了解更多的話,自己去網(wǎng)上找吧。1.char str[100],str1[100],str2[100];gets(str);sscanf(str,”%s%s”,str1,str2);
將讀入的一整行字符串按空格,制表符或回車符分割成兩個字符串。2 取指定長度的字符串。如在下例中,取最大長度為4字節(jié)的字符串。
sscanf(“123456 ”, “%4s”, str);
對于C++的輸入輸出就不再詳細的講了,因為cin,cout的速度實在太慢,不推薦使用,我一般都是到萬不得已時才用。
比如當你要讀入字符串到string 對象中時,就只能用cin了,這時候還有一個常見的問題,就是如何將一整行字符串讀入一個string 中,這就要用到getline函數(shù)了。用法為: getline(cin, str);第一個參數(shù)就是標準輸入流cin,第二個參數(shù)是接收讀入數(shù)據(jù)的string對象,本來還有第三個參數(shù),是結(jié)束符的標志,但通常用它默認的就可以了,所以不用管。
注意區(qū)分這個getline和cin.getline的區(qū)別: cin.getline的用法如下: char str[20];cin.getline(str,20);表示從讀入的一行字符串中,取最多20各字符放入字符數(shù)組str中,注意此處的str是字符數(shù)組,而上面的str是string對象。
另外需要注意的是,千萬不要把cout和printf混用,因為cout是帶緩沖的而printf不帶,所以會使得輸出的數(shù)據(jù)順序混亂。
第三篇:c語言中swap問題小結(jié)
#include
printf(“swap1: x:%d,y:%dn”,x,y);//形參傳值,不能交換,實際傳過去是拷貝的一份,沒改變主函數(shù)中x,y swap2(&x,&y);
printf(“swap2: x:%d,y:%dn”,x,y);//不能交換,函數(shù)中只是地址交換了下,地址指向的內(nèi)容沒有交換 swap3(&x,&y);
printf(“swap3: x:%d,y:%dn”,x,y);//能交換,地址指向的內(nèi)容進行了交換 swap4(&x,&y);
printf(“swap4: x:%d,y:%dn”,x,y);//能交換,地址指向的內(nèi)容進行交換 swap5(&x,&y);
printf(“swap5: x:%d,y:%dn”,x,y);//能交換,地址指向的內(nèi)容進行交換 return 0;} swap1: x:4,y:3 swap2: x:4,y:3 swap3: x:3,y:4 swap4: x:4,y:3 swap5: x:3,y:4
第四篇:C語言中結(jié)構體的使用
腳踏實地,心無旁騖,珍惜分分秒秒。緊跟老師,夯實基礎。什么是結(jié)構體?
簡單的來說
結(jié)構體就是一個可以包含不同數(shù)據(jù)類型的一個結(jié)構 它是一種可以自己定義的數(shù)據(jù)類型 它的特點和數(shù)組主要有兩點不同
首先結(jié)構體可以在一個結(jié)構中聲明不同的數(shù)據(jù)類型 第二相同結(jié)構的結(jié)構體變量是可以相互賦值的 而數(shù)組是做不到的
因為數(shù)組是單一數(shù)據(jù)類型的數(shù)據(jù)集合 它本身不是數(shù)據(jù)類型(而結(jié)構體是)數(shù)組名稱是常量指針
所以不可以做為左值進行運算
所以數(shù)組之間就不能通過數(shù)組名稱相互復制了 即使數(shù)據(jù)類型和數(shù)組大小完全相同
定義結(jié)構體使用struct修飾符 例如:
struct test { float a;int b;};
上面的代碼就定義了一個名為test的結(jié)構體 它的數(shù)據(jù)類型就是test 它包含兩個成員a和b 成員a的數(shù)據(jù)類型為浮點型 成員b的數(shù)據(jù)類型為整型
由于結(jié)構體本身就是自定義的數(shù)據(jù)類型
定義結(jié)構體變量的方法和定義普通變量的方法一樣
test pn1;
這樣就定義了一test結(jié)構體數(shù)據(jù)類型的結(jié)構體變量pn1 結(jié)構體成員的訪問通過點操作符進行
pn1.a=10 就對結(jié)構體變量pn1的成員a進行了賦值操作
注意:結(jié)構體生命的時候本身不占用任何內(nèi)存空間
只有當你用你定義的結(jié)構體類型定義結(jié)構體變量的時候計算機才會分配內(nèi)存
結(jié)構體
同樣是可以定義指針的
那么結(jié)構體指針就叫做結(jié)構指針
結(jié)構指針通過->符號來訪問成員
下面我們就以上所說的看一個完整的例子: #include
struct test//定義一個名為test的結(jié)構體 { int a;//定義結(jié)構體成員a int b;//定義結(jié)構體成員b };
void main(){ test pn1;//定義結(jié)構體變量pn1 test pn2;//定義結(jié)構體變量pn2
pn2.a=10;//通過成員操作符.給結(jié)構體變量pn2中的成員a賦值 pn2.b=3;//通過成員操作符.給結(jié)構體變量pn2中的成員b賦值
pn1=pn2;//把pn2中所有的成員值復制給具有相同結(jié)構的結(jié)構體變量pn1 cout<
test *point;//定義結(jié)構指針
point=&pn2;//指針指向結(jié)構體變量pn2的內(nèi)存地址 cout<
a=99;//通過結(jié)構指針修改結(jié)構體變量pn2成員a的值 cout<
a<<“|”<
b< 總之 結(jié)構體可以描述數(shù)組不能夠清晰描述的結(jié)構 它具有數(shù)組所不具備的一些功能特性 下面我們來看一下 結(jié)構體變量是如何作為函數(shù)參數(shù)進行傳遞的 #include struct test { char name[10];float socre;}; void print_score(test pn)//以結(jié)構變量進行傳遞 { cout< void print_score(test *pn)//一結(jié)構指針作為形參 { cout< name<<“|”< socre< void main(){ test a[2]={{“marry” 88.5} {“jarck” 98.5}};int num = sizeof(a)/sizeof(test);for(int i=0;i void print_score(test *pn)的效率是要高過void print_score(test pn)的 因為直接內(nèi)存操作避免了??臻g開辟結(jié)構變量空間需求 節(jié)省內(nèi)存 下面我們再說一下 傳遞結(jié)構引用的例子 利用引用傳遞的好處很多 它的效率和指針相差無幾 但引用的操作方式和值傳遞幾乎一樣 種種優(yōu)勢都說明善用引用可以做到程序的易讀和易操作 它的優(yōu)勢尤其在結(jié)構和大的時候 避免傳遞結(jié)構變量很大的值 節(jié)省內(nèi)存 提高效率 #include struct test { char name[10];float socre;}; void print_score(test &pn)//以結(jié)構變量進行傳遞 { cout< void main(){ test a[2]={{“marry” 88.5} {“jarck” 98.5}};int num = sizeof(a)/sizeof(test);for(int i=0;i #include struct test { char name[10];float socre;}; void print_score(test &pn){ cout< test get_score(){ test pn;cin>>pn.name>>pn.socre;return pn;} void main(){ test a[2];int num = sizeof(a)/sizeof(test);for(int i=0;i //------例程2-- #include struct test { char name[10];float socre;}; void print_score(test &pn){ cout< void get_score(test &pn){ cin>>pn.name>>pn.socre;} void main(){ test a[2];int num = sizeof(a)/sizeof(test);for(int i=0;i 第一: 例程1中的 test get_score(){ test pn;cin>>pn.name>>pn.socre;return pn;} 調(diào)用的時候在內(nèi)部要在棧空間開辟一個名為pn的結(jié)構體變量 程序pn的時候又再次在棧內(nèi)存空間內(nèi)自動生成了一個臨時結(jié)構體變量temp 在前面的教程中我們已經(jīng)說過 它是一個copy 而例程2中的: void get_score(test &pn){ cin>>pn.name>>pn.socre;} 卻沒有這一過程 不開辟任何新的內(nèi)存空間 也沒有任何臨時變量的生成第二: 例程1在mian()中 必須對返回的結(jié)構體變量進行一次結(jié)構體變量與結(jié)構體變量直接的相互賦值操作 for(int i=0;i 而例程2中由于是通過內(nèi)存地址直接操作 所以完全沒有這一過程 提高了效率 for(int i=0;i 函數(shù)也是可以返回結(jié)構體應用的 例子如下: #include struct test { char name[10];float socre;}; test a; test &get_score(test &pn){ cin>>pn.name>>pn.socre;return pn;} void print_score(test &pn){ cout< void main(){ test &sp=get_score(a);cin.get();cout< 調(diào)用get_score(a);結(jié)束并返回的時候 函數(shù)內(nèi)部沒有臨時變量的產(chǎn)生 返回直接吧全局結(jié)構變量a的內(nèi)存地址賦予結(jié)構引用sp 最后提一下指針的引用 定義指針的引用方法如下: void main(){ int a=0;int b=10;int *p1=&a;int *p2=&b;int *&pn=p1;cout < pn就是一個指向指針的引用 它也可以看做是指針別名 總之使用引用要特別注意它的特性 它的操作是和普通指針一樣的 在函數(shù)中對全局指針的引用操作要十分小心 避免破壞全局指針! c語言中可變參數(shù)函數(shù)的設計 c語言中可變參數(shù)函數(shù)的設計 c語言中可變參數(shù)函數(shù)的設計 -----最近想好好學學這個, 先把網(wǎng)上搜集得資料貼上.======================== 參數(shù)可變函數(shù)的實現(xiàn)(上)CSDN Blog推出文章指數(shù)概念,文章指數(shù)是對Blog文章綜合評分后推算出的,綜合評分項分別是該文章的點擊量,回復次數(shù),被網(wǎng)摘收錄數(shù)量,文章長度和文章類型;滿分100,每月更新一次。 此文獻給如我一般還在探索C語言之路的朋友們。 注:本文中測試程序的編譯環(huán)境為win2000和VC6.0 緣起: 作為一個程序員,我沒有寫過參數(shù)可變的函數(shù),我相信大部分朋友也沒有涉及過,或者我的境界層次太低了。那么緣何我要去揭這一層面紗呢?因為好奇! 我是個思維具有極大惰性的人,曾經(jīng)識得參數(shù)可變函數(shù),也懶得去深究,但是它的三點(函數(shù)聲明時參數(shù)列表中的“…”)卻深刻的映入/ 20 了我的記憶里,而且是帶著若干個閃耀的問號??墒蔷驮谧蛱?,在拜讀某君的高論時,它再一次出現(xiàn)了。我的資質(zhì)真的是不太夠,因為某君在談到它時只是給出了 破題: 但凡所謂“實現(xiàn)”都是從沒有到有的過程,但是我只是想去解惑它的實現(xiàn),因為它原本就是好端端的正為成千上萬的程序員們服務。 還是從我們熟悉的printf說起: 如果你是個C語言的程序員,無論你是初學者還是高高手,對于printf都不會陌生,甚至你已經(jīng)用了無數(shù)次了。我已經(jīng)說過我是個有極大惰性的人,所以每次用printf都是照本宣科,規(guī)規(guī)矩矩的按教科書上說的做,從來沒有問過一個為什么,這就是所謂的“熟視無睹”吧。 其實,printf函數(shù)是一個典型的參數(shù)可變的函數(shù)。在保證它的第一個參數(shù)是字符串的條件下,你可以輸任意數(shù)量任意合法類型的參數(shù)。只要你在第一個字符串參數(shù)中使用了對應的格式化字符串,你就可以輸出正確的值。這難道不是件很有趣的事嗎?那它是怎么做到的? 1,首先,怎么得到參數(shù)的值。對于一般的函數(shù),我們可以通過參數(shù)對應在參數(shù)列表里的標識符來得到。但是參數(shù)可變函數(shù)那些可變的參數(shù)是沒有參數(shù)標識符的,它只有“…”,所以通過標識符來得到是不可能的,我們只有另辟途徑。/ 20 我們知道函數(shù)調(diào)用時都會分配??臻g,而函數(shù)調(diào)用機制中的棧結(jié)構如下圖所示: |......| ------------------ | 參數(shù)2 | ------------------ | 參數(shù)1 | ------------------ | 返回地址 | ------------------ |調(diào)用函數(shù)運行狀態(tài)| ------------------ 可見,參數(shù)是連續(xù)存儲在棧里面的,那么也就是說,我們只要得到可變參數(shù)的前一個參數(shù)的地址,就可以通過指針訪問到那些可變參數(shù)。但是怎么樣得到可變參數(shù)的前一個參數(shù)的地址呢?不知道你注意到?jīng)]有,參數(shù)可變函數(shù)在可變參數(shù)之前必有一個參數(shù)是固定的,并使用標識符,而且通常被聲明為char*類型,printf函數(shù)也不例外。這樣的話,我們就可以通過這個參數(shù)對應的標識符來得到地址,從而訪問其他參數(shù)變得可能。我們可以寫一個測試程序來試一下: / 20 #include void va_test(char* fmt,...);//參數(shù)可變的函數(shù)聲明 void main(){ int a=1,c=55; char b='b'; va_test(“",a,b,c);//用四個參數(shù)做測試 } void va_test(char* fmt,...)//參數(shù)可變的函數(shù)定義,注意第一個參數(shù)為char* fmt { char *p=NULL;/ 20 p=(char *)&fmt;//注意不是指向fmt,而是指向&fmt,并且強制轉(zhuǎn)化為char *,以便一個一個字節(jié)訪問 for(int i = 0;i<16;i++)//16是通過計算的值(參數(shù)個數(shù)*4個字節(jié)),只是為了測試,暫且將就一下 { printf(”%.4d “,*p);//輸出p指針指向地址的值 p++;} } 編譯運行的結(jié)果為 0056 0000 0066 0000 | 0001 0000 0000 0000 | 0098 0000 0000 0000 | 0055 0000 0000 0000 由運行結(jié)果可見,通過這樣方式可以逐一獲得可變參數(shù)的值。 至于為什么通常被聲明為char*類型,我們慢慢看來。 2,怎樣確定參數(shù)類型和數(shù)量 / 20 通過上述的方式,我們首先解決了取得可變參數(shù)值的問題,但是對于一個參數(shù),值很重要,其類型同樣舉足輕重,而對于一個函數(shù)來講參數(shù)個數(shù)也非常重要,否則就會產(chǎn)生了一系列的麻煩來。通過訪問存儲參數(shù)的??臻g,我們并不能得到關于類型的任何信息和參數(shù)個數(shù)的任何信息。我想你應該想到了——使用char *參數(shù)。Printf函數(shù)就是這樣實現(xiàn)的,它把后面的可變參數(shù)類型都放到了char *指向的字符數(shù)組里,并通過%來標識以便與其它的字符相區(qū)別,從而確定了參數(shù)類型也確定了參數(shù)個數(shù)。其實,用何種方式來到達這樣的效果取決于函數(shù)的實現(xiàn)。比如說,定義一個函數(shù),預知它的可變參數(shù)類型都是int,那么固定參數(shù)完全可以用int類型來替換char*類型,因為只要得到參數(shù)個數(shù)就可以了。 3,言歸正傳 我想到了這里,大概的輪廓已經(jīng)呈現(xiàn)出來了。本來想就此作罷的(我的惰性使然),但是一想到如果不具實用性便可能是一堆廢物,枉費我打了這么些字,決定還是繼續(xù)下去。 我是比較抵制用那些不明所以的宏定義的,所以在上面的闡述里一點都沒有涉及定義在 好了,我們來看一下那些宏定義。 打開 1)typedef char * va_list; 2)#define _INTSIZEOF(n)((sizeof(n)+ sizeof(int)1)) 3)#define va_start(ap,v)(ap =(va_list)&v + _INTSIZEOF(v)) 4)#define va_arg(ap,t)(*(t *)((ap += _INTSIZEOF(t))sizeof(type))) 其中,argp的類型是char *。 如果你想用va_arg從可變參數(shù)列表中提取出函數(shù)指針類型的參數(shù),例如 int(*)(),則va_arg(argp, int(*)())被擴展為: (*(int(*)()*)(((argp)+= sizeof(int(*)()))-sizeof(int(*)()))) 顯然,(int(*)()*)是無意義的。 解決這個問題的辦法是將函數(shù)指針用typedef定義成一個獨立的數(shù)據(jù)類型,例如: typedef int(*funcptr)(); / 20 這時候再調(diào)用va_arg(argp, funcptr)將被擴展為: (*(funcptr *)(((argp)+= sizeof(funcptr))-sizeof(funcptr))) 這樣就可以通過編譯檢查了。 問題:可變長參數(shù)的獲取 有這樣一個具有可變長參數(shù)的函數(shù),其中有下列代碼用來獲取類型為float的實參: va_arg(argp, float); 這樣做可以嗎? 答案與分析: 不可以。在可變長參數(shù)中,應用的是”加寬“原則。也就是float類型被擴展成double;char, short被擴展成int。因此,如果你要去可變長參數(shù)列表中原來為float類型的參數(shù),需要用va_arg(argp, double)。對char和short類型的則用va_arg(argp, int)。 問題:定義可變長參數(shù)的一個限制 為什么我的編譯器不允許我定義如下的函數(shù),也就是可變長參數(shù),但是沒有任何的固定參數(shù)? int f(...) { / 20 ...} 答案與分析: 不可以。這是ANSI C 所要求的,你至少得定義一個固定參數(shù)。 這個參數(shù)將被傳遞給va_start(),然后用va_arg()和va_end()來確定所有實際調(diào)用時可變長參數(shù)的類型和值。 第一篇 C語言編程中有時會遇到一些參數(shù)個數(shù)可變的函數(shù),例如printf()函數(shù),其函數(shù)原型為: int printf(const char* format,...); 它除了有一個參數(shù)format固定以外,后面跟的參數(shù)的個數(shù)和類型是可變的(用三個點“…”做參數(shù)占位符),實際調(diào)用時可以有以下的形式: printf(”%d“,i);printf(”%s“,s);printf(”the number is %d ,string is:%s“, i, s); 一個簡單的可變參數(shù)的C函數(shù) 先看例子程序。該函數(shù)至少有一個整數(shù)參數(shù),其后占位符…,表示后面參數(shù)的個數(shù)不定。在這個例子里,所有的輸入?yún)?shù)必須都是整/ 20 數(shù),函數(shù)的功能只是打印所有參數(shù)的值。函數(shù)代碼如下: //示例代碼1:可變參數(shù)函數(shù)的使用 #include ”stdio.h“ #include ”stdarg.h“ void simple_va_fun(int start,...){ va_list arg_ptr;int nArgValue =start;int nArgCout=”0“;//可變參數(shù)的數(shù)目 va_start(arg_ptr,start);//以固定參數(shù)的地址為起點確定變參的內(nèi)存起始地址。do { ++nArgCout;printf(”the %d th arg: %d",nArgCout,nArgValue);//輸出各參數(shù)的值 nArgValue = va_arg(arg_ptr,int);//得到下一個可變參數(shù)的值 } while(nArgValue!=-1);return;} int main(int argc, char* argv[]){ simple_va_fun(100,-1);simple_va_fun(100,200,-1);return 0;} 下面解釋一下這些代碼。從這個函數(shù)的實現(xiàn)可以看到,我們使用可變參數(shù)應該有以下步驟: / 20 ⑴由于在程序中將用到以下這些宏: 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 / 20第五篇:c語言中可變參數(shù)函數(shù)設計方案