本篇博客详细介绍C语言最后的三种自定义类型,它们分别有着各自的特点和应用场景,重点在于理解这三种自定义类型的声明方式和使用,以及各自的特点,最后重点掌握该章节常考的考点,如:结构体内存对齐问题,使用联合判断字节序的存储问题。学完本篇博客达到理解会运用!
目录
1.1 结构体类型的声明和结构体的嵌套以及结构体指针(是指针)
一、结构体(struct)
数组可理解为一种自定义数据类型,它存放的是一组相同数据类型数据的集合,每个数据可看作是一个变量,但是现实生活只有这些内置类型还是不够的,假设我想描述学生,描述一本书,这时单一的内置类型是不行的。描述一个学生需要名字、年龄、学号、身高、体重等;描述一本书需要作者、出版社、定价等。需要不同的数据类型,因此,C语言为了解决这个问题,增加了结构体这种自定义的数据类型,让程序员可以自己创造适合的类型。
结构体是一种存放不同数据类型的数据集合,同样也是一种自定义数据类型。结构体是C语言由面向过程向面向对象的一种转变。
1.1 结构体类型的声明和结构体的嵌套以及结构体指针(是指针)
结构体类型的声明通常包括结构体的名称和其成员变量的定义,结构体的成员变量可以包括基本数据类型变量、数组、结构体类型(结构体的嵌套)、指针类型。
结构体指针是指向结构体类型变量内存地址的指针。通过使用结构体指针可以访问或者修改结构体成员变量;
下面使用一个具体的例子说明如何进行结构体类型的声明和结构体的嵌套以及定义结构体指针。
结构体的声明必须放在函数的外部,它告知编译器这个结构体包括哪些成员,按照如下格式进行声明:
struct 结构体名
{
成员列表(结构体所包含的变量或数组或结构体或指针)
};此时,结构体名就是类型名。
//.c文件中(C语言语法)普通的声明结构体的方式和定义结构体变量
struct Address
{
char name[20];
char area[20];
}; //分号不能少
struct Student
{
char * name1; //字符串指针
const char * name2; //字符串指针,用const修饰,只可通过指针解引用访问,不可进行修改
char name3[20]; //字符数组
int id;
float score;
Address address; //结构体嵌套
};
int main()
{
//定义结构体变量
struct Student s={"张三","李四","王五",111,98.5,{"王麻子","陕西"}};
//定义结构体指针
struct Student * ps=&s;
//.cpp文件中(C++语法)语普通的声明结构体的方式和定义结构体变量
struct Address
{
char name[20];
char area[20];
}; //分号不能少
struct Student
{
char * name1; //字符串指针
const char * name2; //字符串指针,用const修饰,只可通过指针解引用访问,不可进行修改
char name3[20]; //字符数组
int id;
float score;
Address address; //结构体嵌套
};
int main()
{
//定义结构体变量
Student s={"张三","李四","王五",111,98.5,{"王麻子","陕西"}};
//定义结构体指针
Student * ps=&s;
return 0;
}
从上面可以看出,.c和.cpp语法的区别在于:c++语法可以省略关键字struct,而c语言语法不能省略struct,为了实际开发统一使用,可以结合使用typedef进行类型重命名,便可在两个文件同时使用,省略关键字struct。
//结构体声明结合typedef使用
typedef struct Address
{
char name[20];
char area[20];
}Address; //分号不能少
typedef struct Student
{
char * name1; //字符串指针
const char * name2; //字符串指针,用const修饰,只可通过指针解引用访问,不可进行修改
char name3[20]; //字符数组
int id;
float score;
Address address; //结构体嵌套
}Student,*Ps;
//利用typedef对结构体和结构体指针进行重命名
//相当于:typedef struct Student Student
//相当于:typedef struct Student* Ps
int main()
{
//定义结构体变量
Student s={"张三","李四","王五",111,98.5,{"王麻子","陕西"}};
//定义结构体指针
Ps=&s;
注意事项:
在对结构体成员设计时,要考虑两个方面:
- 考虑数据的长度,用合适的数据类型变量保存!
- 对于较大的数据,可以用字符串进行保存!
如:字符数组 char str[20]; 内存开辟在栈区,保存的是实际的数据
字符串指针 const char * str; 字符串存储在常量区,指针开辟在栈区,保存的是这个数据的地址
整型数据int存放不下时,可以用字符串的方式存储也可使用上述方法。
1.2 结构体变量的定义及初始化
结构体和数组类似,因此初始化方式和数组一样,采用花括号赋值!结构体嵌套时,里面的结构体也是需要花括号进行初始化的。
注意对结构体中的字符数组或者字符串进行初始化,必须使用字符串拷贝函数,切不可直接赋值,如:s.name="张三",因为左边是字符数组名为字符首元素的地址,地址是编号,是一个常量,常量不可以做左值,左值必须是可以修改的变量,右边是一个字符串常量存储在数据区,且不可以进行修改,代表首元素的地址。改正:strcpy(s.name,"张三");
注意事项:
数组与结构体类似,但是存在以下两点不同之处:
- 结构体变量名和数组名不同,数组名在表达式中会被转换成指针/地址,而结构体变量名并不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址必须要加取地址符&;
- 数组在内存中存放的,并且随着下标的增大,数组元素的地址也随着增大,数组所占的字节数等于数组元素个数乘以单个元素所占字节数,而结构体所占内存空间的大小,需要结合内存对齐问题进行计算!
Student s={"张三","李四","王五",111,98.5,