C语言基础:结构体(五)
结构体(Structure)由若干成员组成,各成员可以有不同的类型。在程序中要使用结构体类型,必须先对结构体的组成进行描述。由于每个成员的数据类型不一样,所以没法用序号(下标)访问数据元素。
结构体类型定义格式如下:
struct 类型名
{
成员类型1 成员1;
成员类型2 成员2;
成员类型3 成员3;
...
};
注:
- 结构体定义的名字是类型名不是变量名;
- 结构体的花括号不是复合语句,定义结束要有分号;
- 结构体成员在内存中有序排列;
- 结构体成员可以是任何类型,包括别的结构体类型(嵌套),但不能包括自己(递归);
- 结构体类型的长度为各个成员长度之和。
文章目录
1. 结构体类型变量
1.1 定义
定义结构体类型的变量,可采用以下3种方法:
- 先定义结构体类型,再定义结构体变量。
#define Stu_type struct student//宏定义
Stu_type//Stu_type与struct student完全等效
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[40];
};
Stu_type stu1,stu2;//用Stu_type用定义变量
- 在定义结构体类型的同时定义结构体变量。
struct student//定义结构体类型
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[40];
}stu1,stu2;//定义结构体变量
- 直接定义结构类型变量(无名类型)。
struct date{
int year;
int month;
int day;
};
struct{//定义时不出现结构体类型名,这个结构体类型名后面不会被用到。
int num;
char name[20];
char sex;
int age;
struct date birthday;
char addr[20];
}stu1,stu2;//直接定义结构体变量,但不能再次需要时使用该定义的结构体类型。
注:
- 结构体类型(Struct Type):结构体类型定义了一个数据结构的模板,这个模板描述了数据成员的类型、数量和顺序。
- 结构体变量(Struct Variable):结构体变量是结构体类型的一个实例,用于存储该类型定义的数据。
- 对结构体变量的操作:只能对结构体变量进行操作(如赋值、存储和运算),而不能直接对结构体类型进行操作。
- 存储空间分配:在编译时,编译器只为结构体变量分配存储空间,而不是为结构体类型本身分配存储空间。
- 结构体中的成员:结构体中的成员可以单独使用,它们的作用与普通变量相当, 结构体中的成员也可以是另一个结构体类型的变量。
- 成员名与变量名的冲突:成员名可以与程序中的其他变量名相同,但它们在不同的作用域中,因此它们不代表同一对象,如
struct MemberType{ int num; } member; member.num = 10;
和int num = 20;
。
1.2 使用
引用一个结构体变量,有以下两种方式:
- 由结构体变量名引用其成员的标记形式为:
结构体变量名.结构体成员名
。
struct t1{
int num;
char name[20];
}stu1;
stu1.num=1001;
- 由指向结构体的指针变量引用结构体成员的标记形式为:
指针变量名—>结构体成员名
。
struct node{
float x;
struct node *next;//指向自身结构体的指针成员
}p,q,*pt;//结构体变量
pt=&p;
p.x=6.6;
p.next=&q;
p.next->x=8.8;
q.next=NULL;
1.3 初始化
结构体变量和其他变量一样,可以在定义结构体变量的同时进行初始化。格式如下:
struct 结构体类型名 变量名={成员1的值,成员2的值,...};
- 对外部存储类型的结构体变量进行初始化。
struct t1{
int x;
float y;
};
struct t2{
char c;
struct t1 b;
};
struct t1 a={10,6.6};
struct t2 b={'a',10,6.6};//b.c='a',b.b={10,6.6},a.x=10,a.y=6.6
- 对静态存储类型的结构体变量进行初始化。
static struct t2{
int m;
float n;
}a={20,8.8};
1.4 输入与输出
C语言不允许把一个结构体变量作为一个整体进行输入输出,只能对结构体中的每个成员逐步进行输出。
void main()
{
struct
{
int num;
char name[30];
char addr[40];
}stu1;
scanf("%d%s%s",&stu1.num,stu1.name,stu1.addr);
printf("%d,%s,%s\n",stu1.num,stu1.name,stu1.addr);
}
运行结果:
输入:101 Kangkang ChongQing
输出:101,Kangkang,ChongQing
注:也可以用
gets
和puts
函数输入输出一个结构体变量中的字符数组成员,如gets(stu1.name); puts(stu1.name);
。
2. 结构体类型数组
2.1 定义
结构体类型数组即一个数组,每个成员是一个结构体。格式与定义结构体变量类似,只是要加上[长度]
,定义为数组。结构体数组的访问是数组和结构体的组合。如a[1].x
表示a
数组下标为1的元素的成员x
。格式如下:
结构体数组名[元素下标].结构体成员名;
例:
struct t1{
int x[10];
float y;
};
struct t1 a,b[3];
注:ASNI(美国国家标准学会)标准定义
int
是占2个字节,并且在16位机当中也是占2个字节,但在VC或GCC里int
是占4个字节,在32位或64位机也是占4个字节。
2.2 初始化
一个成员一个成员的进行初始化(类似于两维数组)。
void main()
{
struct student
{
int num;
char name[20];
}students[3]={//初始化
{101,"KangKang"},
{102,"LiMing"},
{103,"ZhangSan"}
};
printf("%d,%s\n",students[0].num,students[0].name);
printf("%d,%s\n",students[1].num,students[1].name);
printf("%d,%s\n",students[2].num,students[2].name);
}
运行结果:
101,KangKang
102,LiMing
103,ZhangSan
2.3 使用
一个结构体数组的元素相当于一个结构体变量。引用结构体数组元素有以下规则:
- 引用某一元素的一个成员。
student[i].num;
- 可以将一个结构体数组元素赋给同一结构体类型数组中的另一个元素,或赋给同一类型的变量。
struct student students[3],stu1;
stu1=students[0];
students[2]=students[1];
students[1]=stu1;
- 不能把结构体数组元素作为一个整体直接进行输入输出,只能以单个成员对象进行输入输出。
scanf("%s".students[0].name);
printf("%s",students[0].name);
3. 结构体类型指针
一个结构体变量的指针就是该变量所占内存段的首地址。可以定义一个指针变量,用来指向一个结构体变量,此时该指针变量的值是结构体变量的首地址。指针变量也可以用来指向结构体数组中的元素。
3.1 指向结构体变量的指针
指向结构体变量的指针定义的一般形式为:
struct 类型名 *指针变量名;
通过指向结构体的指针变量引用结构体成员的标记方法为:
指针变量->结构体成员名
*指针变量
表示指针变量所指对象,所以通过指向结构体的指针变量引用结构体成员也可以写成以下形式:
(*指针变量).结构体成员名
void main(){
struct node{
int a;
float b;
}x,*p;//指向结构体变量的指针定义
p=&x;//*p等价于x
x.a=10;
x.b=6.6;
printf("x:%d,%0.1f\n",x.a,x.b);
printf("*p:%d,%0.1f\n",p->a,p->b);//->优先级1,结合性自左向右。
printf("*p:%d,%0.1f\n",(*p).a,(*p).b);
}
运行结果:
x:10,6.6
*p:10,6.6
*p:10,6.6
注:
.
(点运算符), 双目运算符,优先级1,运算符左边为结构体变量名,右边为结构体成员名,如x.a
、x.b
;->
(箭头运算符),双目运算符,优先级1,运算符左边为指向结构体变量的指针,右边为结构体成员名,如p->a
、p->b
;*
(解引用运算符),单目运算符,优先级2,运算对象为指针,访问指针指向的内容,如(*p).a
、(*p).b
。
3.2 指向结构体数组元素的指针
一个指针变量可以指向一个结构体数组元素,也就是将该结构体数组的数组元素地址赋给此指针变量。
例1:
void main(){
struct node{
int a;//成员名a
float b;
}a[5]={{1,1.1},{2,2.2},{3,3.3},{4,4.4},{5,5.5}},*p;
p=a;//结构体数组a
printf("%d\n",p->a);//p->a <=> (*p).a <=> a[0].a
printf("%d\n",a[0].a);
printf("%d\n",*p);
printf("\n");
printf("%d\n",*p++);//p先引用再自增
printf("%d\n",++p->a);//++p->a <=> ++((*p).a) <=> ++(a[1].a)
//p->a先自增再引用,这里p没有自增,因为“->”优先级大于“++”
printf("%d\n",(++p)->a);//(++p)->a <=> (++*p).a <=> a[2].a
}
运行结果:
1
1
1
1
3
3
例2:
void main(){
struct s{
int x,*y;
}*p;
int data[5]={10,20,30,40,50};
struct s a[5]={100,&data[0],200,&data[1],300,&data[2],400,&data[4],500,&data[5]};
p=a;
//p->x <=> (*p).x <=> a[0].x
printf("%d\n",p->x);//100
printf("%d\n",(*p).x);//100
//*p->y <=> *(*p).y
printf("%d\n",*p->y);//10
printf("%d\n",*(*p).y);//10
//p所指向的x这个成员自增1
printf("%d\n",++p->x);//101
//p指针自增1,指向下一个地址
printf("%d\n",(++p)->x);//200
//先引用再自增,p所指向的x这个成员自增1
printf("%d\n",p->x++);//200
printf("%d\n",p->x);//201
//“->”优先级大于“*”,p所指向的y这个成员,然后y指向data[1],再自增1
printf("%d\n",++(*p->y));//21
//data[1]再自增1
printf("%d\n",++*p->y);//22
//p所指向的y这个成员自增1,然后y指向data[2]
printf("%d\n",*++p->y);//30
printf("%d\n",p->x);//201
//p指针自增1,p指向y这个成员,然后y指向data[2]
printf("%d\n",*(++p)->y);//30
printf("%d\n",p->x);//300
//先引用再自增,p指向y这个成员自增1,y指向data[2]
printf("%d\n",*p->y++);//30
printf("%d\n",p->x);//300
//先引用再自增,p指向y这个成员自增1,y指向data[3]
printf("%d\n",*(p->y)++);//40
printf("%d\n",p->x);//300
//先引用再自增,p指针自增1,最后为400,y指向data[4]
printf("%d\n",*p++->y);//50
printf("%d\n",p->x);//400
}
4. 结构体与函数
4.1 结构体变量作为函数参数
和数组不同,结构体变量作函数参数表示直接将实参结构体变量的各个成员的值全部传递给形参的结构体变量。
struct students{
int num;
char name[20];
};
void list(struct students stu)//传参
{
printf("%d,%s\n",stu.num,stu.name);
}
void main(){
void list(struct students stu);
struct students stu[3],*p;
int i;
for(i=0,p=stu;i<3;p++,i++){
printf("students[%d]:\n",i);
scanf("%d%s",&p->num,p->name);
}
printf("All student lists:\n");
for(i=0;i<3;i++)
list(stu[i]);
}
运行结果:
输出:students[0]:
输入:101 zhang3
输出:students[1]:
输入:102 li4
输出:students[2]:
输入:103 wang5
输出:
All student lists:
101,zhang3
102,li4
103,wang5
4.2 指向结构体变量的指针作为函数参数
类似于数组名作为参数,传递的是结构体的地址。
struct students{
int num;
char name[20];
};
void main(){
void print(struct students *p);
struct students stu;
stu.num=101;
strcpy(stu.name,"KangKang");
print(&stu);
}
void print(struct students *p){
printf("%d,%s\n",p->num,p->name);
}
运行结果:
101,KangKang
注:如果函数里用到
strcpy
等字符串处理函数需要在程序代码前加上#include "string.h"
。
4.3 返回结构体类型值的函数
设有一个struct data
类型的参数,并返回struct data
类型的值,则确定输入日期是这年中的第几天。
struct date{
int day;int month;int year;int year_day;
};
int day_tb[][12]={{31,28,31,30,31,30,31,31,30,31,30,31},{31,29,31,30,31,30,31,31,30,31,30,31}};
int main(struct date d){
int i,leap;
scanf("%d%d%d",&d.day,&d.month,&d.year);
d.year_day=d.day;
leap=(d.year%4==0&&d.year%100==0)||(d.year%400==0);//1(闰年)or 0(不闰年)
for(i=0;i<d.month-1;i++)
d.year_day+=day_tb[leap][i];
printf("%d\n",d.year_day);
return d.year_day;//返回d.year_day
}
运行结果:
输入:31 12 2020
输出:365
5. 共用体类型
5.1 定义
共用体(Union)也叫联合体,它会把几种不同类型的变量存放到同一个内存地址中(即起始地址一样)。定义如下:
union 类型名
{
成员类型1 成员名1;
成员类型2 成员名2;
//...
};
union 类型名 变量名;
注:共用体变量的定义类似结构体变量的定义,只不过把
struct
改成union
。
6.2 使用
共用体变量的使用也类似结构体变量的使用,成员运算符.
、指向运算符->
都可以用在共用体变量,指向共用体变量的指针上。
void main(){
union data{
int i;
char ch;
float f;
}a={100},*p;//只能对共用体里的第一个成员进行赋初值
p=&a;
printf("%d\n",p->i);
a.ch='C';
a.f=3.14;
printf("%d\n",a.i);
printf("%c\n",a.ch);
printf("%0.2f\n",a.f);//前面的整型i和字符'C'被浮点型3.14覆盖
printf("%d\n",sizeof(a));//输出a所占空间字节大小
}
运行结果:
100
1078523331
?
3.14
4
注:
- 共用体类型长度为共用体成员长度的最大值;
- 共用体成员同时只有一个是有效的;
- 共用体变量不能对整体赋初值,但能对共用体里的第一个成员进行赋初值;
- 共用体类型可以作为函数参数传递;
- 其他特点类似结构体。
6. 枚举类型
6.1 定义
枚举类型(Enumeration)是一种用户定义的类型,它包含了一组命名的整型常量。枚举类型的定义使用enum
关键字,并且枚举常量之间使用逗号分隔。枚举类型的定义格式如下:
enum 类型名{
枚举常量1,
枚举常量2,
//...
枚举常量n
};
enum 类型名 变量名;
枚举常量的形式是一个标识符,它代表一个整型数。正常情况下枚举常量对应的整型值由0开始递增,除非你在定义枚举类型时设置它的值。
enum 类型名{
枚举常量1 = 值1,
枚举常量2 = 值2,
//...
枚举常量n = 值n
};
注:枚举变量的定义类似结构体变量的定义,只不过把
struct
改成enum
。
6.2 使用
例:
void main(){
enum weekday{
sunday,monday,tuesday,wednesday,thursday,friday,saturday//枚举常量
}a,b,c,d,e,f,g;//枚举变量
a=sunday;
b=monday;
c=tuesday;
d=wednesday;
e=thursday;
f=friday;
g=saturday;
printf("%d,%d,%d,%d,%d,%d,%d\n",a,b,c,d,e,f,g);
}
运行结果:
0,1,2,3,4,5,6
注:
- 枚举类型变量的使用,类似一般整型变量,不过取值范围被限定在枚举常量范围内;
- 可以把枚举常量赋值给相应的枚举变量,但不能把整型赋值给枚举变量(除非强制类型转换),比如
a=sunday;
语句;- 枚举常量的值不能在程序中改变;
- 枚举常量可以进行比较;
- 枚举常量不是字符串。
7. typedef的用法
typedef是在计算机编程语言中用来为复杂的声明定义简单的别名,它与宏定义有些差异。它本身是一种存储类的关键字,与
auto
、extern
、mutable
、static
、register
等关键字不能出现在同一个表达式中。
7.1 定义一般类型
typedef
定义一般类型,格式如下:
typedef 已有的类型 标识符;
例:
typedef int INTEGER;
INTEGER a,b;
注:定义以后
INTEGER
的使用等同于类型int
的使用。
7.2 定义数组或指针
typedef
定义数组或指针,格式如下:
typedef 类型 标识符[长度];
typedef 类型 * 标识符;
该标识符今后表示数组或指针类型,而不是变量,可以用来定义变量。
typedef int A[10];
typedef int *P;
A a,b;
P p1,p2;
注:
A
和P
是类型名,a
和b
是整型数组,p1
和p2
是整型指针。
7.3 定义结构体类型
typedef
定义结构体类型,格式如下:
typedef struct 类型名1{成员定义}类型名;
例:
typedef struct node{
int a;
char b;
}test;//typedef定义的是test类型名
struct node a;
test b;
注:
node
和test
都是类型名,但是typedef
定义的是类型test
,node
不需要typedef
定义;- 用
node
和test
定义结构体变量方法是不同的,test
前不用加struct
,typedef
定义时一般node
可以省略;- 共用体(union)和枚举型(enum)它们的
typedef
定义方式与结构体类似。
——>以上内容是关于C语言结构体的基础知识,希望对初学者或再次学习者有所帮助,基础打扎实,不怕风吹雨打! 如果以上内容有错误或者内容不全,望大家提出!我也会继续写好每一篇博文!
待续未完
——文优
欢迎观看和提问!!!