名著阅读 > 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