把一些基本类型数据组合在一起,形成一个新的复合数据类型,这个新的类型就叫结构体。比如一个学生信息,就包含学号、名字和年龄等。
定义结构体的三种方式:
-
定义一个结构体类型(以学生信息为例),并定义一个结构体变量:
struct Student { int num; char name[32]; double score; }; int main(void) { struct Student stu1 = {1,"Andy",99}; return 0; }
语句
struct Student stu1={1,"Andy",99};
定义了一个名为stu1
的结构体变量,这个变量的类型是struct Student
。这样子定义变量可以直接整体初始化(要按照顺序):
={1,"Andy",99}
,也可以用点运算符来初始化:stu1.num=1;
……。如果定义完变量后没整体初始化,则只能用点运算符进行单个的赋值。 -
也可以在定义结构体类型的时候就紧接着定义出几个结构体变量(一般不推荐这么定义):
struct Student { int num; char name[32]; double score; }stu1,stu2; int main(void) { stu1.num = 1; stu1.name = "Andy"; stu1.score = 99; return 0; }
不推荐的原因是:如果再想使用【定义的这个结构体类型】来另外定义一个新变量,会出问题。
-
如果像第二种方式,定义结构体类型后紧接着定义了一个变量,则还可以忽略结构体的名称(一般也不推荐这么定义):
struct { int num; char name[32]; double score; }stu1; int main(void) { stu1.num = 1; stu1.name = "Andy"; stu1.score = 99; return 0; }
取出结构体变量中的元素来使用的两种方式:
-
使用
.
:struct Student { int num; char name[32]; double score; }; int main(void) { struct Student stu1 = {1,"Andy",99}; stu1.num = 1; printf("%d\n",stu1.num); return 0; }
-
使用
->
(指针用):struct Student { int num; char name[32]; }; int main(void) { struct Student st1 = {1,"Jason"}; struct Student * p = &st1; printf("%s\n",p->name); return 0; }
- 上面代码中,指针
p
的类型是struct Student *
- 语句
struct Student * p = &st1;
记得要加上&
,不能去掉 p->name
在计算机内部会被转化成(*p).name
,两者是等价的p->name
的含义是:p
所指向的结构体变量中的name
这个成员- 每个成员都是在结构体中的一个域,也称为域表(或成员列表)
- 上面代码中,指针
结构体的一些性质
-
结构体指针与sizeof
struct Student { int num; char name[32]; }; int main(void) { struct Student st1 = {1,"Jason"}; struct Student *p = &st1; printf("%d\n",sizeof(p));//输出结果:8 printf("%d\n",sizeof(*p));//输出结果:36 return 0; }
sizeof(p)
的值为8是因为:指针变量不管是什么类型,本身所占的大小都是一样的,在64位计算机中,指针变量都是占8个字节sizeof(*p)
的值是36是因为int num
是整型数,占4个字节,char name[32]
占32个字节 -
结构体变量作为函数参数
struct Student { int num; char name[32]; }; void func1(struct Student stu) { stu.num = 2; } void func2(struct Student * p) { p->num = 2; } int main(void) { struct Student st1 = {1,"Jason"}; func1(st1); printf("%d\n",st1.num);//输出结果:1 func2(&st1); printf("%d\n",st1.num);//输出结果:2 return 0; }
func1
函数并不能改变st1
里面的成员的数值,而func2
函数可以,因为st1
的地址作为参数传进了func2
里复习一个知识点:指针的优点有:传递速度更快,耗用内存小,执行速度快
以上面代码为例,在调用
func1
时,系统需要先在内存空间里划分sizeof(struct Student)
这么大的空间,然后再把st1的数据拷贝过来。如果struct Student
占用内存很大,那就要耗费很多空间,程序执行速度也会受影响而在调用
func2
时,系统不需要再另外划分空间,只需把st1
的地址传过去就行了,相比之下,数据传递就快很多,内存空间也耗用更少,程序执行速度更快 -
定义一个结构体数组:
struct Student { int num; char name[38]; double score; }; int main(void) { struct Student arr[3] = {{1,"Anna",99},{2,"Ann",100},{3,"Jane",99}}; printf("%d",arr[i].num);//输出结果:1 return 0; }
-
结构体与动态内存分配:
struct Student { int num; char name[32]; }; int main(void) { struct Student *p; int num = 5; //p = (struct Student)malloc(num*sizeof(struct Student));//error //p = (struct Student *)malloc(num*sizeof(struct Student));//OK p = malloc(num*sizeof(struct Student));//OK return 0; }
在上面的代码中,使用
malloc()
时如果前面加强制转换struct Student
会报错:conversion to non-scalar type requested
,因为malloc()
返回的是指针类型(标量),不能强制转换为结构体类型(非标量)