在前面的章节中学习了一些数据类型,如int、char、float等等,但如果我们想要实现一个“学生类型”,前面学习过的类型所表达的信息就做不到来表示一个“学生”,因此我们需要自己来定义一个复杂的、包含更多信息的类型。那就是本章要讲到的结构体——是由一批数据组合而成的结构型数据。话不多说,正文开始!
本章重点
- 结构体类型的声明
- 结构体初始化
- 结构体成员访问
- 结构体传参
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
1.结构体声明
结构的基础知识
结构是一些值的集合,这些值被称为成员变量。结构的每个成员可以是不同类型的变量
结构体是C语言中一种重要的数据类型,该数据类型由一组称为成员(或称为域,或称为元素)的不同数据组成,其中每个成员可以具有不同的类型。结构体通常用来表示类型不同但是又相关的若干数据
结构体类型不是由系统定义好的,而是需要程序设计者自己定义的。C语言提供了关键字struct来标识所定义的结构体类型
关键字struct和结构体名组合成一种类型标识符,其地位如同通常的int、char等类型标识符,其用途就像 int 类型标识符标识整型变量一样可以用来定义结构体变量。定义变量以后,该变量就可以像定义的其他变量一样使用了;成员又称为成员变量,它是结构体所包含的若干个基本的结构类型,必须用“{}”括起来,并且要以分号结束,每个成员应表明具体的数据类型
在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问
-- -- -- -- -- -- -- -- -- --
结构的声明
结构声明的形式
struct tag //struct:结构体关键字 tag:结构体标签 struct tag:一个结构体类型
{
member_list; //成员列表
} variable_list; //变量列表,可以是结构体变量,也可以是结构体指针
//一个结构的声明是一条语句,不要忘记了分号
-- -- -- -- -- -- -- -- -- --
示例
现在就来解决前言中的问题,创建一个“学生”类型
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}s1,s2,s3;//s1,s2,s3是三个结构体变量,且在结构体声明的末尾创建的变量是全局变量
//注意分号不能丢
还有另一种写法
typedef struct Stu
{
char name[20];
int age;
char sex[5];
char id[20];
}Stu;
typedef:类型重命名
将struct Stu这个结构体类型重命名为Stu,Stu就是结构体类型(struct Stu)的一个新名字,以后用Stu就等于用struct Stu
-- -- -- -- -- -- -- -- -- --
结构体成员的类型
结构的成员可以是变量、数组、指针,甚至是其他结构体(结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针)
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
2.结构体变量的定义和初始化
普通结构体变量定义和初始化
struct Stu
{
int age;
char name[20];
char sex;
};
...
struct Stu stu={10,"zhangsan",男};//定义结构体变量stu并对其初始化
...
-- -- -- -- -- -- -- -- -- --
结构体成员中有其他的结构体类型定义的变量的定义和初始化
struct S
{
int a;
char c;
char arr[20];
double d;
};
struct T
{
char ch[10];
struct S i;//使用结构体类型struct S来定义一个变量i
char* pc;
};
...
char arr[]="shizhanglao";
struct T t={"abc",{100,'w',"hello word",3.14},arr};
//这就是初始化一个 包含结构体类型成员 的 结构体类型变量的形式
//再用一个大括号去初始化内部的结构体变量
...
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
3.结构体成员的访问
先看一段代码
#include <stdio.h>
struct S
{
int a;
char c;
char arr[20];
double d;
};
struct T
{
char ch[10];
struct S i;
char* pc;
};
int main()
{
char arr[] = "xiemingshouhuang";
struct T t = { "abc",{100,'w',"hello world",3.14},arr };
printf("%s\n", t.ch);
printf("%s\n", t.i.arr);
//这个arr是t找到struct T中的i,再从i找到struct S中的arr,这个arr里面初始化的是hello word
printf("%lf\n", t.i.d);
printf("%s\n", t.pc);
//这个t找到了struct T中的pc,pc里面放的是main函数中字符数组的地址
//最后再以%s的形式输出就是本长老啦(xiemingshouhuang)
return 0;
}
运行结果:
abc
hello world
3.140000
xiemingshouhuang
-- -- -- -- -- -- -- -- -- --
结构体变量访问成员
#include <string.h>
typedef struct Stu
{
char name[20];
int age;
}students;
int main()
{
students s;
strcpy(s.name, "shizhanglao");//使用 . 来访问name
s.age = 20;//使用 . 访问age
}
-- -- -- -- -- -- -- -- -- --
结构体指针访问指向变量的成员
#include <stdio.h>
typedef struct Stu
{
char name[20];
int age;
const char* sex;
}Stu;
void Print1(Stu tmp)
{
printf("name: %s\n", tmp.name);
printf("age: %d\n", tmp.age);
}
void Print2(Stu*ps)
{
printf("name: %s\n", ps->name);
printf("age: %d\n", ps->age);
}
int main()
{
Stu sky = { "邪冥兽皇",100000000,"雄" };
Print1(sky);
printf("\n");
Print2(&sky);
return 0;
}
运行结果:
name: 邪冥兽皇
age: 100000000
name: 邪冥兽皇
age: 100000000
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
4.结构体传参
再上一个部分中,Print1和Print2函数哪一个更好呢?它们两个看起来都完成的打印任务,但我们的仍然是——首选Print2函数
原因:函数传参的时候,参数是需要压栈①的。 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。而地址的大小永远是4或8个字节,这就节省了传参时形参拷贝所需要消耗的内存
结论:结构体传参时,要优先传结构体的地址
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
附
①
在计算机系统中,栈则是一个具有以下属性的动态内存区域
程序可以将数据压入栈中,也可以将数据从栈顶弹出,在i386机器中,栈顶由称为esp的寄存器进行定位。压栈的操作使得栈顶的地址减小,弹出的操作使得栈顶的地址增大
栈在程序的运行中有着举足轻重的作用,最重要的是栈保存了一个函数调用时所需要的维护信息,这常常称之为堆栈帧或者活动记录,一般包含如下几方面的信息:
1.函数的返回地址和参数
2. 临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量。
②本章节只是简要讲解结构体,因为目前属于C语言初阶的阶段,更多详细的内容会在进阶部分讲解