结构体
结构体是一些值的集合,这些值称为成员变量,结构体每个成员可以使不同的变量
结构体声明
结构体的普通声明
struct tag
{
member list;
}variavle list;
//举例
//描述一个学生的信息(姓名、年龄、性别、成绩)
struct Stu
{
char name[10];
int age;
char sex;
int grade[5];
};//定义结束处分号不可省略
结构体声明时可以不完全声明,例如:
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;//创建一个x结构体变量
struct
{
int a;
char b;
float c;
}*p;//创建一个y结构体变量指针
//问题,上面基础下,下面代码是否合法
p = &x;
//未声明结构体即使内部成员相同,仍为两个不同类型结构体,因此该句非法
小结结构体创建
1.结构体内部成员不可省略,至少一个
2.结构体名称尽量不省略,即tag
3.变量可以省略
结构体访问
//以学生信息结构体为例
struct Stu
{
char name[10];
int age;
char sex;
int grade[5];
};
sturct Stu x;
sturct Stu y, *p = &x;
//创建两个x和y结构体和一个结构体指针p
//访问时可以通过下面方式访问
p->age = x.age;
strcpy(x.name, p->name);
(*p).sex = M;
结构体自引用
//错误自引用
struct Node
{
int data;
struct Node next;//错误,无法确定结构体的大小
};
//正确的自引用
struct Node
{
int data;
struct Node* next;//正确,指针大小确定
};
//注意一下结构体互相引用的问题
//在前面创建的结构体想要引用后面的结构体必须先声明
struct B;
struct A
{
int data;
struct B* next;
};
struct B
{
int data;
struct A* next;
};
结构体初始化
结构体不能直接整体赋值,但可以整体初始化,初始化同数组一样使用花括号{}
例如:
struct Stu A = { Tom, 15, M, { 95, 87, 90, 92, 86 }};
结构体的内存对齐
结构体的大小并不是结构体每个成员大小简单的相加,而是按照一定规则进行存储,这个规则便是内存对齐
struct A
{
char c1;
int i;
char c2;
};
struct B
{
char c1;
char c2;
int i;
};
sizeof(struct A) = 12;
sizeof(struct B) = 8;//成员相同,但是所占大小却不同
内存对齐规则
1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(VS中默认的值为8,linux中的默认值为4)
3.结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
为什么要有内存对齐
1、平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2、性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。
结构体传参问题
数组传入函数时会发生降级,变成相对应的指针。
结构体不同,他传入时不发生降级
位段
什么是位段
位段的声明同结构体相似,但有两个不同:
1.位段的成员必须是整型
2.位段成员名后必须跟一个冒号和数字。
例如:
struct S
{
int a:2;
int b:5;
int c:10;
int d:20
};
sizeof(struct A);//大小为2字节
位段按照我的理解即用位存储数字,在声明时就告知其可使用的位段大小,比如给两位,那么他的最大可存11即3,给五位可存最大为11111即31, 且位段存储的溢出不影响相邻的位段成员,简单来说,溢出后果自负
提醒
1.位段的成员可以是int也可以是char(整型均可)
2.位段空间按照4个字节(int)(32位)或者1个字节(char)(8位)分配
3.位段有很多不确定因素,位段不跨平台,所以可移植的程序需要避免使用位段
4.在不考虑跨平台的情况下,位段能很好地节省内存空间来存储
枚举
把可能的值一一列举出来,在c语言里称之为枚举(enum)
例如:
enum week
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
枚举的变量其实是有值的,若不赋初值,则从0开始,向后依次递增1
也可以直接赋值,方便使用
枚举的优点
1.增加代码的可读性和可维护性
2.和#define相比,枚举具有类型检查,更加严谨
3.防止命名污染(封装)
4.便于调试
5.使用方便,可以一次定义多个常量
联合
联合即一些成员共同使用一块内存空间,即所有成员的首地址相同
union U
{
int a;
char b;
};
联合体的大小由占据内存最大的成员决定
联合体的应用
十进制点分ip地址
union ip_addr
{
unsigned long addr;
struct
{
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
}ip;
};
union ip_addr ip;
ip.addr = 176238749;
printf("ip addr: %d.%d.%d.%d", ip.c1, ip.c2, ip.c3, ip.c4);