主题1:结构体出现的缘由
对于一个学生来说,有姓名、学号、班级、年龄、性别、手机号等信息。那么,如何来存储这些信息呢?
要存储一个人的姓名,我们知道可以定义一个字符数组,把该姓名存入字符数组,然后就可以对该姓名进行查询、修改等操作。
如果要存储咱们班49个同学的姓名,我们知道可以定义一个二维字符数组或者一个字符指针数组来解决该问题。
那么,如果要存储一个人的年龄,我们知道可以定义一个整型变量,把该年龄存入该整型变量,然后就可以对该年龄进行你所需要的一切操作了。
如果要存储咱们班49个同学的年龄,我们知道可以定义一个二维整型数组来解决该问题。
。。。。。。。。。。。。。
如此这般,每个同学的姓名、学号、班级、年龄、性别、手机号等信息零散地存在于多个地方。既然这些信息归属于一个同学,它们就是一个整体,能不能把它们放在一起存储呢?进而执行操作的时候也方便哦。答案是肯定的,结构体类型就可以来解决该问题。
主题2:用户如何自定义一个结构体类型?
那么,什么是结构体?我的理解是结构体是人们自创的一种数据类型,根据业务处理的需要而自定义的一种数据类型,通常也称之为构造数据类型。切记,它也是一种数据类型。下来,我们来揭开它的面纱吧。
struct Student
{
char name[12];
char XH[12];
int age;
};
这就是一个结构体的定义,定义了一个结构体类型,struct是结构体类型的关键字,Student是结构体类型的名称(该名称为任意合法的标识符,但不能与关键字同名),{}里面的内容称为结构体的成员,结构体的定义以;结束。综上所述,我们定义了一个Student的结构体类型。
主题3:如何使用结构体类型?
众所周知,有了int类型,我们就可以定义int类型的变量、int类型的数组、int类型的指针变量等等。同理,有了结构体类型,我们当然可以定义结构体类型的变量、结构体类型的数组、结构体类型的指针变量等等。
struct Student{
char name[12];
char XH[12];
int age;
};
struct Student a,b[49],*t; 该语句定义了一个struct Student 类型的变量a,struct Student类型的数组b,struct Student类型指针变量t。
那么,定义完之后,如何对它们进行操作呢?
3.1结构体类型的变量
先来看一下如何对结构体类型的变量进行输入输出操作吧。
#include<stdio.h>
struct Student{
char name[12];
char XH[12];
int age;
};
int main()
{
struct Student a;
gets(a.name);
gets(a.XH);
scanf("%d",&a.age);
printf("output a\n");
puts(a.name);
puts(a.XH);
printf("%d\n",a.age);
return 0;
}
上述程序对struct Student类型的变量a进行了输入输出操作,需要告诉编译器给a的哪个成员进行输入输出操作,因而需要a.成员名的方式来对各个成员进行引用。
那么,如何对结构体类型的数组进行操作呢?
3.2结构体类型的数组
一提到数组,就离不开循环(除表示字符串的字符数组外,它有专属的字符串处理函数,得天独厚)。同理,对结构体类型的数组进行操作,也需要用到循环结构。
#include<stdio.h>
struct Student{
char name[12];
char XH[12];
int age;
};
int main()
{
struct Student b[3];
int i;
for(i=0;i<3;i++)
{
gets(b[i].name);
gets(b[i].XH);
scanf("%d",&b[i].age);
}
printf("output array b:\n");
for(i=0;i<3;i++)
{
puts(b[i].name);
puts(b[i].XH);
printf("%d\n",b[i].age);
}
return 0;
}
密切注意:在输入的时候,因为涉及到多种类型的成员,尤其是字符串,往往很容易出现错误,当i=0时,我们输完第1个同学的年龄之后,如果敲了回车,这个回车符会作为下一个同学的姓名,因为回车符也是字符或字符串。所以在输入年龄之后,不用回车直接输入下一个同学的姓名即可。具体如下图所示。
但是,不让敲回车,大多数同学会很不习惯,因而可以在年龄输入语句的后面加上ch=getchar();或者getchar();用getchar
来吃掉这个容易引起误解的回车符吧。
for(i=0;i<3;i++)
{
gets(b[i].name);
gets(b[i].XH);
scanf("%d",&b[i].age);
ch=getchar(); //也可以只有getchar();
}
其实,还有另一种处理方法也较常见,就是把int类型的变量也当作字符串,存放在char数组中。具体如下:
#include<stdio.h>
#include<string.h>
struct Student{
char name[12];
char XH[12];
char age[4];
};
int main()
{
struct Student a,b[49];
int i;
for(i=0;i<3;i++)
{
gets(b[i].name);
gets(b[i].XH);
gets(b[i].age);
}
printf("output array b:\n");
for(i=0;i<3;i++)
{
puts(b[i].name);
puts(b[i].XH);
printf("%d\n",atoi(b[i].age));
}
return 0;
}
把age也作为一个字符串来处理,因而用gets来输入,就可以自然地敲回车了,然后在对年龄进行操作时,采用函数atoi将该字符串转换为int即可。atoi是在头文件string.h中定义,因而需要引入该头文件。
3.3 结构体类型的指针变量
我们不要把结构体类型相像的很神秘,它和其它类型一样,只是一种数据类型而已,只不过我们在操作时需要指明它的成员。
如同int* p; p
指向的空间肯定存储的是一个int型的数据,因而结构体类型的指针变量指向的空间肯定是一个相同结构体类型的数据。如果把一个结构体类型的变量地址赋值给相同结构体类型的指针变量,则该指针变量指向该结构体变量的空间,继而可以通过该指针变量对其空间中的各个成员进行操作。
#include<stdio.h>
#include<string.h>
struct Student{
char name[12];
char XH[12];
char age[4];
};
int main()
{
struct Student a,*p;
int i;
p=&a;
gets((*p).name);
gets(p->XH);
gets(p->age);
printf("output a:\n");
puts(p->name);
puts(p->XH);
printf("%d\n",atoi(p->age));
return 0;
}
语句:p=&a;也就意味着p指向了a的空间,然后可以通过p对a中的成员进行操作,指针对成员的引用有两个形式:(*p).age或者p->age
,个人比较喜欢后者,因为后者更有指针的感觉!嘻嘻。
那么,结构体类型的指针如何来操作结构体类型的数组呢?显然,如果是一维数组的话,把数组名赋值给它就行了。然后采用指针操作一维数组的方法就可以了(共三种方法,你还记得吗?指针变量法,首地址+偏移量,下标法)。这里我们只讲指针变量法(不断改变指针的指向)。
#include<stdio.h>
#include<string.h>
struct Student{
char name[12];
char XH[12];
char age[4];
};
int main()
{
struct Student b[3],*p;
int i;
for(p=b;p<b+3;p++)
{
gets(p->name);
gets(p->XH);
gets(p->age);
}
printf("output array b:\n");
for(p=b;p<b+3;p++)
{
puts(p->name);
puts(p->XH);
printf("%d\n",atoi(p->age));
}
return 0;
}
主题4:结构体定义的进一步探讨
如果一个公司的人事部们需要我们做一个系统,所处理的信息包括;员工编号、员工姓名、所属部门、出生年月日。
我们可以定义结构体类型如下:
struct PeopleInfo{
char number[12];
char name[12];
char dept[12];
int year;
int month;
int day;
};
其实,还有另一种思路:
struct bir
{
int year;
int month;
int day;
};
struct PeopleInfo{
char number[12];
char name[12];
char dept[12];
struct bir t;
};
在这种思路中,结构体类型struct PeopleInfo中的一个数据成员t,其类型为struct bir。那么,如何对t中的数据进行引用呢?
#include<string.h>
struct bir
{
int year;
int month;
int day;
};
struct PeopleInfo{
char number[12];
char name[12];
char dept[12];
struct bir t;
};
int main()
{
struct PeopleInfo s;
gets(s.number);
gets(s.name);
gets(s.dept);
scanf("%d%d%d",&s.t.year,&s.t.month,&s.t.day);
printf("info output:\n");
puts(s.number);
puts(s.name);
puts(s.dept);
printf("%d-%d-%d",s.t.year,s.t.month,s.t.day);
return 0;
}
根据前面的经验我们知道,当一个结构体类型中有其它类型和字符类型时,输入时常常会因为回车符而出现状况,只有一个结构体变量时一切正常,当对数组操作时,会出现错误情况,因而可以把其它类型定义为字符型。操作时再转换为你想要的类型。
#include<stdio.h>
#include<string.h>
struct bir
{
char year[5];
char month[3];
char day[3];
};
struct PeopleInfo{
char number[12];
char name[12];
char dept[12];
struct bir t;
};
int main()
{
struct PeopleInfo s[2];
int i;
for(i=0;i<2;i++)
{
gets(s[i].number);
gets(s[i].name);
gets(s[i].dept);
gets(s[i].t.year);
gets(s[i].t.month);
gets(s[i].t.day);
}
printf("info output:\n");
for(i=0;i<2;i++)
{
puts(s[i].number);
puts(s[i].name);
puts(s[i].dept);
printf("%d-%d-%d\n",atoi(s[i].t.year),atoi(s[i].t.month),atoi(s[i].t.day));
}
return 0;
}
其实,当用户要求输出形式为:2009-07-08,将它们均定义为char型是非常合适的,输出时直接采用:
printf("%s-%s-%s\n",s[i].t.year,s[i].t.month,s[i].t.day);
有同学可能会问:采用atoi可以将字符串转换为整型,那如果数据成员为float或double呢?例如,如果我们要编写一个学生成绩管理系统呢,假设每个学生有三门课,该如何进行处理呢?
(1)采用getchar函数
#include<stdio.h>
#include<string.h>
struct StuScore{
char xh[12];
char name[12];
float score[3];
};
int main()
{
struct StuScore s[2];
int i,j;
for(i=0;i<2;i++)
{
gets(s[i].xh);
gets(s[i].name);
for(j=0;j<3;j++)
scanf("%f",&s[i].score[j]);
getchar();
}
printf("info output:\n");
for(i=0;i<2;i++)
{
puts(s[i].xh);
puts(s[i].name);
for(j=0;j<3;j++)
printf("%f\n",s[i].score[j]);
}
return 0;
}
采用getchar函数的目的,就是在输入字符串之前接收那个回车符,觉得也挺不错的。但是,根据应用的不同,有时候会出现n个getchar,瞅着都眼晕。利用,用户还需要输入年龄信息,为此,在结构体定义中需要引入该成员。
#include<stdio.h>
#include<string.h>
struct StuScore{
char xh[12];
int age;
char name[12];
float score[3];
};
int main()
{
struct StuScore s[2];
int i,j;
for(i=0;i<2;i++)
{
gets(s[i].xh);
scanf("%d",&s[i].age);
getchar();
gets(s[i].name);
for(j=0;j<3;j++)
scanf("%f",&s[i].score[j]);
getchar();
}
printf("info output:\n");
for(i=0;i<2;i++)
{
puts(s[i].xh);
printf("%d \t",s[i].age);
puts(s[i].name);
for(j=0;j<3;j++)
printf("%f\n",s[i].score[j]);
}
return 0;
}
在这个结构体类型的定义中,char之间有了int类型,那么在输入age时敲下的回车符也需要有个getchar来接收。
有的系统还要考虑实际用户的输入习惯,也即成员之间的顺序,可能会产生其它类型位于char之间,程序员就需要在其它类型的输入之后都添上getchar函数,才能保证数据的正确输入。