讀古今文學網 > C語言解惑 > 9.1 結構定義和賦值錯誤 >

9.1 結構定義和賦值錯誤

【例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