欧美色欧美亚洲高清在线观看,国产特黄特色a级在线视频,国产一区视频一区欧美,亚洲成a 人在线观看中文

  1. <ul id="fwlom"></ul>

    <object id="fwlom"></object>

    <span id="fwlom"></span><dfn id="fwlom"></dfn>

      <object id="fwlom"></object>

      對于C語言中的scanf函數(shù)的使用問題總結(jié)

      時間:2019-05-13 19:44:59下載本文作者:會員上傳
      簡介:寫寫幫文庫小編為你整理了多篇相關的《對于C語言中的scanf函數(shù)的使用問題總結(jié)》,但愿對你工作學習有幫助,當然你在寫寫幫文庫還可以找到更多《對于C語言中的scanf函數(shù)的使用問題總結(jié)》。

      第一篇:對于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 int main(){

      } 我們想要輸入三個整數(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 int main(){

      } 我的輸入還是仿照上面,進行截圖如下所示 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 int main(){

      } 我們在輸入數(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 #include void swap1(int x,int y){ int temp;temp=x;x=y;y=temp;} void swap2(int *x,int *y){ int *temp;temp=x;x=y;y=temp;} void swap3(int *x,int *y){ int temp;temp=*x;*x=*y;*y=temp;} void swap4(int a[],int b[]){ int temp;temp=a[0];a[0]=b[0];b[0]=temp;} void swap5(int a[],int b[]){ int temp;temp=*a;*a=*b;*b=temp;} int main(){ int x,y;x=4;y=3;swap1(x,y);

      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 #include using namespace std;

      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 #include using namespace std;

      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 #include using namespace std;

      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 #include using namespace std;

      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 #include using namespace std;

      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 #include using namespace std;

      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ù)的設計

      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ì)真的是不太夠,因為某君在談到它時只是給出了中關于它的宏定義,我想大概在高手眼里,點這一下就神會了吧??墒撬@么輕輕一點卻使留在記憶里曾經(jīng)的那幾個問號無限的膨脹,以至于我這個又菜又懶的所謂程序員也萌生了莫大的好奇。

      破題:

      但凡所謂“實現(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ù)下去。

      我是比較抵制用那些不明所以的宏定義的,所以在上面的闡述里一點都沒有涉及定義在的va(variable-argument)宏。事實上,當時讓我產(chǎn)生極大疑惑和好奇的正是這幾個宏定義。但是現(xiàn)在我們不得不要去和這些宏定義打打交道,畢竟我們在討生計的時候還得用上他們,這也是我曰之為“言歸正傳”的理由。

      好了,我們來看一下那些宏定義。

      打開文件,找一下va_*的宏定義,發(fā)現(xiàn)不單單只有一組,但是在各組定義前都會有宏編譯。宏編譯指示的是不同硬件平臺和編譯器下用怎樣的va宏定義。比較一下,不同之處主要在偏移量的計算上。我們還是拿個典型又熟悉的——X86的相關宏定義: / 20

      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語言中的scanf函數(shù)的使用問題總結(jié)word格式文檔
      下載對于C語言中的scanf函數(shù)的使用問題總結(jié).doc
      將本文檔下載到自己電腦,方便修改和收藏,請勿使用迅雷等下載。
      點此處下載文檔

      文檔為doc格式


      聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻自行上傳,本網(wǎng)站不擁有所有權,未作人工編輯處理,也不承擔相關法律責任。如果您發(fā)現(xiàn)有涉嫌版權的內(nèi)容,歡迎發(fā)送郵件至:645879355@qq.com 進行舉報,并提供相關證據(jù),工作人員會在5個工作日內(nèi)聯(lián)系你,一經(jīng)查實,本站將立刻刪除涉嫌侵權內(nèi)容。

      相關范文推薦

        C語言中union應用總結(jié)

        C語言中union應用總結(jié) 定義共用體的類型變量的一般形式為: union 共用體名 { 成員列表; }變量列表; 例如: union data { int i; unsigned char c; float f; }; union da......

        C語言中的文本Txt操作

        對于文件使用方式有以下幾點說明: 1)文件使用方式由r,w,a,t,b,+六個字符拼成,各字符的含義是: r(read): 讀 w(write): 寫 +: 讀和寫 a(append): 追加 t(text): 文本文件,可......

        C語言中的EOF(精選5篇)

        C語言中的EOF EOF是指文件的結(jié)束符,是一個宏定義. 對于鍵盤輸入來說,getchar只有在遇到文本結(jié)束標記(ASCII編碼為26)時才會返回EOF,其它情況都會返回一個輸入符號值。所以對于......

        c語言中的正則表達式regex.h

        c語言中的正則表達式regex.h 如果用戶熟悉Linux下的sed、awk、grep或vi,那么對正則表達式這一概念肯定不會陌生。由于它可以極大地簡化處理字符串時的復雜度,因此現(xiàn)在已經(jīng)在許......

        C語言中extern關鍵字詳解大全

        C語言中extern關鍵字詳解作者:華清遠見武漢華嵌 技術支持 曹偉東內(nèi)容清單:1. 用extern聲明外部變量在一個文件內(nèi)聲明的外部變量在多個文件中聲明外部變量在多個文件......

        c語言中if語句知識點總結(jié)(5篇范例)

        If語句知識點總結(jié) 一. if語句的三種基本形式 if (表達式)語句; 例: if(x>y) printf(“%d”,x); if(表達式) 語句1; else語句2; 例:if (x>y) printf(“%d”,x); else p......

        C語言中的邏輯運算符和位運算符總結(jié)

        老分不清一個&號和倆的用法,今天專門拿出來整理整理: 一、邏輯運算符: 包括:1。&&邏輯與 2。||邏輯或 3。!邏輯非 邏輯運算符用于對包含關系運算符的表達式進行合并或取非 對......

        C語言中變量的存儲類別

        一.C語言中,從變量的作用域角度來分,可以分為全局變量和局部變量。 二.變量值存在的時間角度來分,可以分為靜態(tài)存儲方式和動態(tài)存儲方式。所謂靜態(tài)存儲方式是指在程序運行期間有......