前言
无论在C语言还是C++中,均包含结构体、共用体和枚举类型。由于这三种复合数据类型的概念和使用方法比较简单,所以本文仅对其一些需要注意的内容进行总结。如有疏漏或不足之处,还望各位读者能够不吝指正。
结构体
简介
前面我们学习过的数组,其作用是保存多个相同数据类型的数据。但是在实际应用中,我们往往会遇到更加复杂的情况。比如:我们的学生管理系统,我们不仅需要保存学生的姓名,而且还需要保存学生的班级、学号、年龄等一些列信息,再加上这些信息不能够使用相同的数据类型进行存储,所以便不能使用数组去保存每个学生的详细信息了。
此时,便可以使用结构体数据类型,保存由不同数据类型组成的数据集合了。
简而言之,结构体就是一种可以保存不同数据类型的数据结构。
创建结构体的方法
结构体以struct关键字作为数据类型的标志。在实际使用中,我们可以使用下述三种方法,创建结构体数据类型:
1、常规方式1
struct testStruct
{
string name;//学生的姓名
unsigned int core;//学生的分数
string address; //学生的地址
};//声明一个结构体数据类型
testStruct structStu1;//C++中创建一个结构体structStu1
//struct testStruct structStu1;//C语言中创建一个结构体structStu1
2、常规方式2
struct testStruct
{
string name;//学生的姓名
unsigned int core;//学生的分数
string address; //学生的地址
}structStu1,structStu2;//声明一个结构体数据类型,并创建两个结构体structStu1和structStu2
3、利用typedef的方式
typedef struct
{
string name;//学生的姓名
unsigned int core;//学生的分数
string address; //学生的地址
}testStruct;//声明一个结构体数据类型
testStruct structStu1,structStu2;//创建两个结构体structStu1和structStu2
结构体中的位字段
在嵌入式等底层编程中,往往会遇到结构体的位字段概念,即可以指定结构体成员所占的位数。
尤其是涉及到寄存器封装的部分。由于寄存器往往由许多Bit位组成,而每个Bit位所占的位数可能并不相同,所以此时使用结构体的位字段,便可以实现寄存器的内存空间分配。我们以TI的DSP28335芯片的一个寄存器为例进行说明:
结构体使用技巧
在使用结构体时,如果注意成员变量的位置,那么可以在一定程度上提高程序的执行效率并且节省内存空间。
由于结构体中可以存储不同数据类型的成员,所以在开辟内存空间时,并不是按照固定的大小进行分配的。再加上CPU一般以4个字节作为最优的数据处理大小,所以注意不同数据类型的成员数据的位置,可以对结构体进行优化。
比如:
//声明一个学生的结构体
struct student1
{
char sex;//学生的性别
char test;//用于测试的一个字符
int age;//学生的年龄
};
struct student2
{
char sex;//学生的性别
int age;//学生的年龄
char test;//用于测试的一个字符
};
int main()
{
student1 stu1 =
{
'M','F',18
};//定义并初始化一个结构体变量stu1
cout << "stu1结构体占用的内存大小为:" << sizeof(stu1) << endl;
student2 stu2 =
{
'M',18,'F'
};//定义并初始化一个结构体变量stu2
cout << "stu2结构体占用的内存大小为:" << sizeof(stu2) << endl;
return 0;
}
输出结果如下:
为什么相同成员的结构体,所占的内存空间大小却不同呢?
因为在student1结构体中,两个char数据类型是相邻的,由于char数据类型的大小为一个字节,所以4个字节可以保存两个char数据类型的变量,再加上int数据类型的大小为4个字节,所以stu1结构体变量所占的内存空间便是8个字节。
但是在student2结构体中,两个char数据类型之间插入了一个int数据类型,第一个char数据类型占据了4个字节中的一个字节,剩下的3个字节无法存储int类型的变量,所以还需要在第二个4字节的空间中保存int数据,而第二个char数据类型还需要再开辟一个4字节大小的空间进行保存,所以最终占用的内存空间大小便是12个字节。
共用体
简介
共用体和结构体的作用类似,不同之处在于:
结构体中可以同时存储结构体成员数据,但是共用体只能同时存储一个共用体结构体成员。
共用体的内存分配
通俗的来说,就是在创建结构体变量时,编译器会为结构体中所有的成员开辟内存空间。而在创建共用体变量时,编译器仅会开辟一个内存空间,该内存空间的大小与共用体成员中所占空间最大的成员数据相等。
从内存大小来说,结构体的大小是所有成员数据的大小之和,而共用体的大小是其占用内存最大的成员数据的大小。并且,结构体中的成员数据位于不同的内存地址处,而共用体中的成员数据全部位于同一个地址处。测例如下:
#include <iostream>
#include <cstring>
typedef struct
{
char name[10];
double core;
char address[20];
}stuStruct;
typedef union
{
char name[10];
double core;
char address[20];
}stuUnion;
int main()
{
using namespace std;
stuStruct student1 =
{"Issac",89.0,"ChengDu"};//初始化结构体
cout << "结构体占用的内存大小为:" << sizeof(student1) << endl;
cout << "结构体student1.name的地址:" << &student1.name << endl;
cout << "结构体student1.core的地址:" << &student1.core << endl;
cout << "结构体student1.address的地址:" << &student1.address << endl;
stuUnion student2;
student2.core = 90.5;
cout << "共用体占用的内存大小为:" << sizeof(student2) << endl;
cout << "共用体student2.core的地址:" << &student2.core << endl;
strcpy(student2.name,"Issac");
cout << "共用体student2.name的地址:" << &student2.name << endl;
return 0;
}
输出结果如下:
共用体与结构体的结合
在嵌入式开发中,经常需要对寄存器的某个位进行操作,但是也可以对整个寄存器的整体进行操作,完成整个寄存器的赋值操作。此时,便可以使用共用体和结构体,共同完成上述功能。如下所示:
在实际使用中,我们可以按照下面的说明进行使用:
union GPACTRL_REG GpaCtrlRegs;//创建一个共用体变量
GpaCtrlRegs.all = 0xFF;//将这个寄存器的所有位设置为1
GpaCtrlRegs.bit.test = 1;//将该寄存器的test位设置为1
枚举类型
简介
枚举类型的作用与利用define宏定义或者const关键字类似,均是创建常量的一种方式。在创建枚举类型时,使用enum作为该类型的标志。
枚举类型一般用于只能在某个数值范围内进行选择的场合下,比如:每周的七天,每年的12个月等。
创建枚举类型
此处,我们创建一个枚举数据类型,用来表示每周的星期几:
enum week
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
};//默认情况下,枚举类型的数据成员从0开始,比如上面的Monday就等于0,Tuesday等于1
enum week
{
Monday = 1,Tuesday,Wednesday = 3,Thursday,Friday,Saturday,Sunday
};//我们也可以人为指定枚举成员的数值,如上所示
为枚举类型分配内存空间
枚举类型的内存分配方法与共用体的方法相似,也仅仅会为整个枚举类型分配一个元素的内存空间的大小,比如:
typedef enum
{
one = 1,two,three,four
}NUM_ENUM;//声明一个枚举类型
int main()
{
NUM_ENUM num;//定义一个枚举类型变量
cout << "枚举数据类型的大小为" << sizeof(num) << endl;//输出的结果为4
return 0;
}
注意事项
在使用枚举类型时,需要额外注意以下限制:
1、枚举成员的值必须是整型,且同一个枚举变量中,不能有相同的枚举成员!!!比如:
enum testEnum
{
one = 1,two = 2.0
};//错误,枚举类型只能是整型数据
enum testEnum
{
one = 1,two = 2,one = 3
};//错误。同一个枚举类型中,不能出现相同的枚举成员
2、访问枚举成员时,必须访问枚举类型中存在的枚举成员,不能访问不存在的成员。
3、虽然枚举成员可以被认为是相应的整型数据,但是不能直接将一个整型数据赋值给一个枚举变量,但是可以利用强制类型转换实现,比如:
enum testEnum
{
one = 1,two = 2
};
testEnum test1;
test1 = 1;//错误,及时枚举成员中的one实际上就是1
test1 = (testEnum)(1);//正确
总结
上述三种复合数据类型,在实际的工程开发中,也是会经常出现的。虽然概念上不难理解。但是在实际使用时,还是需要注意各个数据类型的注意事项。尤其是在使用共用体时,需要注意其内存分配原则。