结构体声明的方法
结构体声明最常见的有四种方法。
1.直接定义结构体变量。
2.声明结构体的同时定义结构体变量。
3.先声明结构体再定义变量。
4.用typedef 重定义。
1.直接定义结构体变量
struct
{
char str1[10];
char str2[10];
short str3[10];
int str4[10];
float str5[10];
double str6[10];
long str7[10];
long long str8[10];
}Variable_name1, Variable_name2;
这种方法比较死板,如果需要再添加几个相同类型的结构体就没有办法添加了。一般不建议使用,除非你确定只需要Variable_name1, Variable_name2。而不会添加新结构体Variable_name3, Variable_name4。
2.声明结构体的同时定义结构体变量
struct statement
{
char str1[10];
char str2[10];
short str3[10];
int str4[10];
float str5[10];
double str6[10];
long str7[10];
long long str8[10];
}Variable_name1, Variable_name2;
Variable_name1和Variable_name2已经是结构体变量了,如果需要添加新的结构体变量
struct statement Variable_name3, Variable_name4;
这样定义看起来不整洁,而且初学者可能搞不清楚 statement 和 Variable_name1, Variable_name2的区别是什么,或者不清楚二者怎么去用,所以不建议初学者这样用。
3.先声明结构体再定义变量
struct statement
{
char str1[10];
char str2[10];
short str3[10];
int str4[10];
float str5[10];
double str6[10];
long str7[10];
long long str8[10];
};
struct statement Variable_name1, Variable_name2;
struct statement Variable_name3, Variable_name4;
这样初学者看起来会比较容易理解,statement是结构体,不能直接使用,只能用来定义结构体变量,定义之后的结构体变量才可使用。
4.用 typedef 重定义结构体
typedef struct
{
char str1[10];
char str2[10];
short str3[10];
int str4[10];
float str5[10];
double str6[10];
long str7[10];
long long str8[10];
}statement;
statement Variable_name1, Variable_name2;
statement Variable_name3, Variable_name4;
用 typedef 重定义结构体名为 statement,定义结构体变量时直接 statement 后面跟结构体变量名就行,跟第三种方法比起来定义结构体变量时少了 struct 这个关键字,是最简单也是最简洁的方法,本人就喜欢这种方法,看每个人的习惯不一样,可以根据自己的习惯来使用适合自己的方法
注意:typedef 的作用是为一种类型引入新的名字,而不是为变量分配空间。在某些方面,typedef 类似于宏文本替换——它并没有引入新的类型,而是为现有的类型取一个新的名字。uint32_t,uint8_t 等都是通过 typedef 把数据类型重定义得来的新名字。
结构体字节对齐特点
以前我一直以为结构体是四字节对齐的,直到后面使用遇到问题我才知道,结构体是以结构体成员里面占内存最大的对齐方式来对齐,比如结构体成员里面最大变量占一个字节,该结构体就以一字节对齐方式对齐,结构体成员里面最大变量占两个字节,该结构体就以两字节对齐方式对齐,结构体成员里面最大变量占八个字节,该结构体就以八字节对齐方式对齐。
这样对齐的原因是因为:结构体的最终大小必须是其成员(基础数据类型成员)里最大成员所占字节的整数倍
要验证结构体对齐方式,我们首先要知道编译器里各种数据类型所占的大小,因为不同编译器中同一数据类型所占字节数是不一样的,比如 int 可以是两个字节,也可以是四个字节,double 可以是占四个字节,也可以是占八个字节。我们来看看几种常用数据类型所占字节大小。
typedef struct
{
char a;
short b;
char c;
char d;
}statement1;
statement1 Variable_name1;
按前面所说,该结构体最大成员占两个字节,所以为两字节对齐,所 a =2 , b =2, c+d =2, 所以一共占六个字节。
typedef struct
{
double a;
char b;
short c;
int d;
int e;
char f;
float g;
}statement1;
statement1 Variable_name1;
我们来分析一下这个结构体占几个字节
该结构体里面的成员最大占八个字节,所以是八字节对齐方式,a =8, b+c+d =8, e+f =8 float =8, 所以该结构体所占大小为32 字节,下面我们来运行一下看结果对不对
结果是对的
注意:相同成员的结构体因为放置成员的顺序不同,可能会导致结构体所占空间大小不同,所以在使用时要尽量合理放置顺序,避免造成不必要的内存资源浪费
结构体优化
我们在做项目过程中,经常会用到很多标志位,只需要一位(即0和1)就行,对于51单片机来说,可以用bit ,但是对于32单片机来说最小的数据类型都是八位,如果标志位很多的时候,就会造成大量的内存资源浪费,毕竟内存大的单片机价格会高很多,所以需要做一些内存优化,比如结构体和枚举全部放共用体里面,就算如此,结构体如果不优化,所占内存还是结构体大小,并没有对实质的内存进行优化。只是节省了少部分枚举所占内存,大量内存还是结构体浪费了。
这就引入一个经常和结构体一起使用的东西,位域。位域配合结构体使用起来就比较灵活,而且不会浪费资源,需要多少位就用多少位。
typedef struct
{
char a :1;
char b :1;
char c :1;
char d :1;
char e :1;
char f :1;
char g :1;
char h :1;
}statement1;
statement1 Variable_name1;
所占大小为一个字节,如果是八个char 类型那就得占八个字节,内存得到了很大的优化。
typedef struct
{
char a :3;
char b :1;
char c :1;
char d :1;
char e :1;
char f :1;
char g :1;
char h :1;
}statement1;
statement1 Variable_name1;
在使用位域时也要注意,不足一个字节的剩余部分也会占一个字节。所以使用时合理放置顺序。
typedef struct
{
char a :1;
char b :1;
char c :1;
char d :1;
char e :1;
char f :1;
char g :1;
short h :1;
}statement1;
statement1 Variable_name1;
typedef struct
{
char a :1;
char b :1;
char c :1;
char d :1;
char e :1;
char f :1;
char g :1;
int h :1;
}statement1;
statement1 Variable_name1;
typedef struct
{
char a :1;
char b :1;
char c :1;
char d :1;
char e :1;
char f :1;
char g :1;
int h :25;
}statement1;
statement1 Variable_name1;
typedef struct
{
char a :1;
char b :1;
char c :1;
char d :1;
char e :1;
char f :1;
char g :1;
int h :26;
}statement1;
statement1 Variable_name1;
位域也是跟据数据类型来进行字节对齐的,也是按成员里面最大的成员所占字节数来对齐,数据类型都是char类型就是以一字节对齐,不足和超过的部分都按一字节,例如6位占一个字节,9位就是占两个字节(8+1占两个 一字节)。成员最大为 short 就以两字节对齐,整个结构体所占不足或者超过16位,也是按两字节对齐,例如,10位占两字节,17位占四字节(16+1就是两个两字节)。最大成员为 int 就以四字节对齐,不足和超过的部分都是以四字节对齐,例如 ,3位是占四字节 ,33位则占八字节(32+1 就是两个四字节)。
注意;float 和 double 不能使用位域。