知识框架:
1 .结构体类型创建
2.结构体的初始化
3.结构体的内存对齐
4.位段
5.枚举和联合
一.结构体类型的创建
C语言允许用户自己指定这样一种数据结构,它由不同类型的数据组合成一个整体,以便引用,结构是一些值的集合,这些值成为成员变量,结构体每个成员可以是不同类型的变量。这些组合在一个整体中的数据是互相联系的,这样的数据结构称为结构体.
结构体类型的声明:描述一个学生
struct stu
{
char name[10];
int age;
char sex[5];
char id[25];
};/*分号不能丢*/
下面对结构体的语法做一些解释:
看下面这段代码:
struct stu
{
int x;
char y;
float z;
};e/*分号不能丢 这里声明了一个e的变量,它里面包括整形变量的x,字符类型的y,浮点类型的y*/
struct stu
{
int x;
char y;
float z;
} t[20], * z;/*分号不能丢 这里创建了t和z t是一个数组,它包括20个结构,z是一个指针,它是指向其类型的指针 */
虽然两个结构体里的成员列表相同,但这两个声明被编译器识别成两个完全不同的类型。
但是这是不是意味着某种特定类型的所有结构都必须使用一个单独的声明来创建呢?
事实并非如此。标签tag允许为成员列表提供一个名字,这样他就可以在后续的声明中使用。标签允许多个声明使用同一个成员列表,并且创建同一种类型的结构,看下面这个例子:
struct stu
{
int x;
char y;
float z;
} ;
这个声明把标签stu和成员列表联系在一起,该声明并没有提供变量列表,所以它并没有创建任何变量。
注意: 标签只是标识了一种模式,用于声明未来的变量,但无论是标签还是模式,其本身都不是变量。
结构体类型的访问:
(1)、结构体变量访问成员: 结构体变量是通过(.)操作符访问成员的。
(2)结构体指针访问结构体成员:
二:结构体的初始化
结构体的初始化方式和数组的初始化方式相似。一个位于一对花括号内部,由逗号分隔的初始值列表可用于结构体各个成员的初始化,这些值根据结构体成员给出的顺序写出,如果初始列表的值不够,剩下的结构体成员将使用缺省值进行初始化。
比如:
struct stu
{
int x;
char y;
float z;
} ;
struct stu a = { 1, 2, 3 };
三:结构对齐体内存
熟悉下面的代码,猜猜运行结果是多少:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<windows.h>
struct stu
{
int x;
char y;
float z;
};
struct a
{
char r;
double a;
int x;
};
int main()
{
printf("%d\n", sizeof(struct stu));
printf("%d\n", sizeof(struct a));
system("pause");
return 0;
}
好了,stu是12s是24
看看下面的规则:
(1)、第一个成员在与结构体变量偏移量为0的地址处;
(2)、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数 = 编译器默认的一个对齐数 与 该成员大小 两者中的较小值。(vs中默认对齐数为8, Linux中默认对齐数为4)
(3)、结构体总大小为最大对齐数(每个成员变量都有一个对齐数,其中最大的一个就是最大对齐数)的整数倍。
(4)、如果嵌套了结构体的情况, 嵌套的结构体对齐到自己最大对齐数的整数倍地址上,结构体的整体大小就是所有最大对齐数(包含结构体的对齐数)的整数倍。
然而为什么会有内存对齐呢?
(1)、平台原因:
不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常;
(2)、性能原因:
数据结构(尤其是栈)应该尽可能的在自认边界上对齐,原因在于:为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存仅需要一次访问。
四.位段
1.位段的内存分配
(1)位段的成员可以是int, signed int, unsigned int或者是char(属于整形家族)类型;
(2)位段的空间上是按照需要以四个字节(int)或者一个字节(char)的方式来开辟的;
(3)以 上面那个位段的例子来说:它先开辟一个int的空间(4个字节,32个比特位)。a:2—>2个比特位,还剩30个比特位; b:5—>5个比特位,还剩25个比特位;c:10—>占10个比特位,还剩15个比特位;c:30要占30个比特位,前面的15个不够用,浪费 掉这15个重新开辟一个int的空间再占用新空间的30个比特位,所以结果是两个int类型,占八个字节;
(4)位段设计很多不确定因素,位段是不跨平台的,注重可移植程序应该避免使用位段
2.位段跨平台问题
(1)、int型位段被当做有符号数还剩无符号数不确定(由编译器决定)
(2)、位段中最大位的数目不确定(16位机器最大是16,32位机器最大是32,如果写成17,则在16位的机器上会出现问题)
(3)、位段中的成员在内存中从右向左分配还是从左向右分配C语言标准尚未定义
(4)、当一个结构体中包含两个位段,第二个位段比较大,无法容纳于第一个位段剩余的位时是舍弃剩余的还是利用,这也不确定。
五.枚举和联合
枚举我们从字面意思就可以了解就是举例的意思
比如说一周有7天
enum
{
mon,
tues,
wed,
thur,
fir,
sat,
sun
}; {}中的是枚举类型的可能取值,一般按照从0开始,然后逐渐加1
也可以赋值
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<windows.h>
enum
{
mon=2,
tues=3,
wed=1,
thur=8,
fir=4,
sat=5,
sun=6
};
枚举的优点:
(1)增加代码的可读性和可维护性
(2)和#define 定义的标识符比较,枚举有类型检查,更加严谨
(3)防止了命名污染
(4)便与调试(#define定义的不能调试)
(5)使用方便,一次可以定义多个常量
联合也是一种自定义类型,它也包含一系列成员,它的特征是这些成员共用同一块内存空间,所以也叫共用体。
联合的特点:
联合的成员是共用同一块内存空间,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大那个成员)
union a
{
char c;
int e;
};
联合大小的计算:
(1)联合的大小至少是最大成员的大小
(2)当最大成员大小不是最大成员大小的整数倍时,就要对齐到最大对齐数的整数倍
好了,以上是我的总结,如果那里有问题,还请大牛们指点。