【例9.1】找出這個程序中的錯誤。
#include <stdio.h> void disp (struct LIST ); int main () { struct LIST{ int a ,b ; }d={3 ,9} ,e={2 ,8} ; LIST f={5 ,6} ; printf (\"%d ,%dn\" , f.a ,f.b ); disp (d ); disp (e ); return 0 ; } // 將結構作為參數,以傳數值的方式傳遞這個參數 void disp (struct LIST s ) { printf (\"%d ,%dn\" , s.a ,s.b ); }
【解答】把結構定義在主程序中,被調函數disp無法使用結構變量作為參數。結構跟數組不一樣,數組可以定義在主函數里,但結構不行。如果將結構定義在主函數里,結構具有封裝的特性,就只能被主函數自己使用。
這種定義結構變量的方法也不對,在C++語言中可以不重寫結構關鍵字struct,在C語言中必須重寫struct。下面是改正後的程序。
#include <stdio.h> void disp (struct LIST ); struct LIST{ int a ,b ; }d={3 ,9} ,e={2 ,8} ; int main () { struct LIST f={5 ,6} ; disp (d ); disp (e ); printf (\"%d ,%dn\" , f.a ,f.b ); return 0 ; } void disp (struct LIST s ) { printf (\"%d ,%dn\" , s.a ,s.b ); }
運行結果如下。
3 ,9 2 ,8 5 ,6
【例9.2】這個程序有時對,但大部分時間出錯。開始以為是讀d.name少「&」符號,但加上還是如此。請分析錯在哪裡。
#include <stdio.h> void disp (struct LIST ); struct LIST{ char name[10] ; char sex ; // 回答m 或者f int a ; }d ; int main () { printf (\" 輸入:\" ); scanf (\"%s%c%d\" ,d.name ,&d.sex ,&d.a ); disp (d ); return 0 ; } void disp (struct LIST s ) { printf (\"%s ,%c ,%dn\" , s.name ,s.sex ,s.a ); }
【解答】字符數組使用與不使用「&」是一樣的,因為字符數組的名字就是存儲它的首地址,語法上都是正確的,問題出在讀字符變量sex上。這裡使用的是男性的第1個字母m(male)和女性的第1個字母f(female)。這條語句很難正常,完全是因為無法保證輸入不產生干擾。讀入是不分順序的,可以將最難處理的放在最後,即改為
scanf (\"%s%d%c\" ,d.name ,&d.a ,&d.sex );
不過要注意,輸入數字和字母之間不能有空格。例如,下面是一個運行示範。
輸入: 王傳義 70m 王傳義,m ,70
注意不要使用那些提示符號,例如,想使用
scanf (\"%s ,%d ,%c\" ,d.name ,&d.a ,&d.sex );
的格式,使用輸入「王傳義,70,m」的方式解決來這個問題,也是徒勞的。
可以使用每次提示的方法,在scanf中增加空格來解決字符輸入問題。例如:
#include <stdio.h> void disp (struct LIST ); struct LIST{ char name[10] ; char sex ; // 回答m 或者f 男 male 女 female int a ; }d ; int main () { printf (\" 輸入姓名:\" ); scanf (\"%s\" ,d.name ); printf (\" 輸入年齡:\" ); scanf (\"%d\" ,&d.a ); printf (\" 輸入性別m/f :\" ); scanf (\" %c\" ,&d.sex ); // 注意留空格的方式 disp (d ); return 0 ; } void disp (struct LIST s ) { printf (\"%s ,%c ,%dn\" , s.name ,s.sex ,s.a ); }
運行示例如下。
輸入姓名:王傳義 輸入年齡:70 輸入性別m/f :m 王傳義,m ,70
還有一種方法是在scanf之前使用一條「getchar();」語句。一般情況下,這種方法很有效,但有時也會失效,不如留空格的方法可靠。
還有的程序員乾脆把性別也定義為字符串,其實有時也一樣會碰到這種問題,一般是在發現出現這種問題時,再採取增加一條「getchar();」語句的方法來解決。即使改用gets函數,有時也會碰到這類問題。
結論:給結構變量的字符域賦值時,一定要多次驗證並且要特別小心地驗證。
【例9.3】這個程序中的賦值語句有錯誤,請分析錯在哪裡。
#include <stdio.h> void disp (struct LIST ); struct LIST{ int a ,b ; } ; int main () { int i ; struct LIST s[3] ,t ; printf (\" 輸入:\" ); scanf (\"%d%d\" ,&t.a ,&t.b ); printf (\" 輸入:\" ); scanf (\"%d%d\" ,s[0].a ,s[0].b ); for (i=0 ;i<3 ;i++ ) { s[i].a=s[0].a+t.a +i ; s[i].b=s[0].b+t.b +i ; } return 0 ; } void disp (struct LIST s ) { int i ; for (i=0 ;i<3 ;i++ ) { printf (\"%d ,%dn\" , s[i].a ,s[i].b ); } }
【解答】結構變量的賦值是正確的,結構數組不對。改為
scanf (\"%d%d\" ,&s[0].a , &s[0].b );
即可。結構數組與普通的數組一樣,下標從0開始,為各個元素賦值需要使用地址符。
【例9.4】分析下面為結構變量賦值的程序是否能正常工作,並給出讀入數據的等效語句。
#include <stdio.h> struct List{ char c ; int num ; char name[12] ; float fnum[2] ; }a ; void disp (void ); int main ( ) {disp (); return 0 ;} void disp ( ) { printf (\" 輸入一個字符:\" ); scanf (\"%c\" ,&a.c ); printf (\" 輸入一個整數:\" ); scanf (\"%d\" ,&a.num ); printf (\" 輸入一個字符串:\" ); scanf (\"%s\" ,a.name ); { int i=0 ; printf (\" 輸入兩個浮點數:\" ); for (i=0 ;i<2 ;i++ ) scanf (\"%f\" ,(a.fnum+i )); } printf (\"%c ,%d ,%s ,%f ,%fn\" , a.c ,a.num ,a.name ,a.fnum[0] ,a.fnum[1] ); printf (\"%c ,%d ,%s ,%f ,%fn\" , a.c ,a.num ,&a.name[4] ,* (a.fnum ),* (a.fnum+1 )); }
【解答】這個程序巧妙地錯開字符和數值,而且把字符放在第一個讀取,所以避免了鍵盤抖動帶來的干擾,確保程序能正常工作。讀字符串name的等效語句為
scanf (\"%s\" ,&a.name );
讀實數的數據是賦給數組,所以等效語句為
scanf (\"%f\" ,&a.fnum[i] );
注意不能使用「a.fnum[i]」的方式,對數值數組的元素賦值必須使用地址。
運行示例如下:
輸入一個字符: M 輸入一個整數: 89 輸入一個字符串: 張一平 輸入兩個浮點數: 2.5 6.8 M ,89 ,張一平,2.500000 ,6.800000 M ,89 ,平,2.500000 ,6.800000
printf用數組首地址的方式輸出其值,第1個元素為*(a.fnum),第2個為*(a.fnum+1)。&a.name[4]是「平」的存儲地址,所以輸出「平」。
【例9.5】這個程序編譯無誤,運行出錯,是何原因?
#include <stdio.h> struct List{ char *name ; int num ; }a ; void disp (void ); int main ( ){ disp (); return 0 ;} void disp ( ) { printf (\" 輸入姓名:\" ); scanf (\"%s\" ,a.name ); printf (\" 輸入編號:\" ); scanf (\"%d\" ,&a.num ); printf (\"%s ,%dn\" , a.name ,a.num ); }
【解答】注意程序中結構指針變量沒有賦初值。a.num的含義是這個結構的指針變量的值,不是地址。這個程序中使用語句
scanf (\"%s\" ,a.name );
給結構a的字符串數組a.name賦值,顯然本例不能用此語句給結構的指針變量賦值。因此程序中使用了沒有初始化的指針變量,運行出錯。
由此看來,結構的指針變量和字符數組的表達形式一樣,所以只好用中間轉換的辦法給指針變量賦值。一種是將輸入讀入一個字符串中,然後賦給指針變量。另一種是定義一個指針變量並為它申請內存,將輸入存入這塊內存,然後賦給結構的指針變量。下面分別給出這兩種方法的完整程序。
// 將輸入讀入一個字符串中,然後賦給指針變量的程序清單 #include <stdio.h> struct List{ char *name ; int num ; }a ; void disp (void ); int main ( ) { disp (); return 0 ; } void disp ( ) { char c[12] ; printf (\" 輸入姓名:\" ); scanf (\"%s\" ,c ); // 注意字符串中不能有空格 a.name=c ; printf (\" 輸入編號:\" ); scanf (\"%d\" ,&a.num ); printf (\"%s ,%dn\" , a.name ,a.num ); } // 使用動態內存的方法的程序清單 #include <stdio.h> #include <stdlib.h> #include <string.h> struct List{ char *name ; int num ; }a ; void disp (void ); int main ( ) { disp (); return 0 ; } void disp ( ) { char *p= (char * )malloc (12 ); printf (\" 輸入姓名:\" ); scanf (\"%s\" ,p ); a.name=p ; printf (\" 輸入編號:\" ); scanf (\"%d\" ,&a.num ); printf (\"%s ,%dn\" , a.name ,a.num ); }
兩個程序等價,運行結果一樣。下面給出一個運行示範。
輸入姓名: 張一平 輸入編號: 2856 張一平,2856