一. 结构体
1,结构体类型的声明:
struct 结构体名称
{
类型说明符号 成员名;//成员列表
};
成员表由若干个成员组成,每个成员都是该结构的一个组成部分;
对每个成员也必须作类型说明;
结构体成员可以是标量,数组,指针,甚至是其他结构体;
结构体类型相当与一个模型,声明的时候是不分配内存的,只有定义相应的结构体变量的时候,才会分配实际的内存;
2, 结构体的特殊声明:
第一类
struct 结构体名
{
char name[20];
int age; // 成员列表;
}n1, n2;//变量名1,变量名2,变量名n 定义结构体变量
第二类
struct
{
char name[20];
int age; // 成员列表;
}n1, n2;//变量名1,变量名2,变量名n 定义结构体变量
第三类
typedef struct
{
char name[20];
int age; // 成员列表;
}Node;
3, 结构体的自引用:
typedef struct
{
int i;
int j;
Node n1;
}Node;
这段代码是错误的!
错误1:直接在结构体中声明另外一个结构体,会出现死循环,如A包括B,B又包括A,A又包括B……使得编译器无法知道结构体的空间大小,因此,无法通过编译;
错误2:typedef还没有将结构体命名为Node,你就在结构体中使用了Node,显然,编译器此时还不知到Node是什么!所以,无法通过编译;
正确的使用方法如下:
typedef struct node
{
int i,j;
struct node *n1;
}Node;
4, 结构体变量的定义和初始化
typedef struct
{
char name[20];
int age;
char sex[5];
char tele[15];
}stu;
int main()
{
stu s = { "张三",20,"男","10086" };//创建变量同时初始化
printf("%s %d %s %s\n", s.name, s.age, s.sex, s.tele);
system("pause");
return 0;
}
5, 结构体内存对齐和计算结构体的大小
为什么存在内存对齐:
1, 平台原因(移植原因):某些平台只能在特定的地址处访问特定类型的数据;
2, 性能原因:提高存取数据的速度。比如有的平台每次都是从偶地址处读取数据,对于一个int型的变量,若从偶地址单元处存放,则只需一个读取周期即可读取该变量,但是若从奇地址单元处存放,则需要2个读取周期读取该变量。
3,结构体的内存对齐是拿空间来换取时间的做法。
结构体内存对齐的规则:
1,第一个成员在与结构体变量偏移量为0的地址处。
2,其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处,对齐数=编译器默认的一个对齐数与该成员大小的较小值。VS中默认的值为8,Linux中的默认值为8.
3,结构体总大小为最大对齐数的整数倍
4,.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
二、位段
1, 位段的声明和任何普通的结构体成员相同,但有两个不同:
首先位段成员必须声明为int、signed int或者是unsigned int类型。
在成员名的后面是一个冒号和一个整数,这个整数指定该位段所占用的位的数目。
2,位段的内存分配:
1.位段的成员可以是int、unsigned int 、signed int、char(属于整型家族)。
2.位段的空间上是按照4个字节(int)或者1个字节(char)的方式来开辟的。
3.位段涉及很多不确定因素,位段不跨平台,所以可移植的程序避免使用位段。
struct A
{
int a : 2;
int b : 5;
int c : 10;
int d : 30;
};
int main()
{
printf("%d\n", sizeof(struct A));
system("pause");
return 0;
}
跟结构相比,位段可以达到相同的效果,但是可以很好地节省空间,但是有跨平台的问题存在。
三、枚举(就是把可能取值一一列举)
1,枚举类型的定义:
enum color
{
red,
green,
blue
};
enum color: . 是枚举类型
. { }中的内容是枚举类型的可能取值,枚举常量。这些可能取值都是有值的,默认从0开始,依次递增,在定义的时候也可以赋初值。
2,枚举的优点:
增加代码的可读性和可维护性;
和#define定义的标识符比较枚举有类型检查,更加严谨;
防止命名污染;
便与调试;
使用方便,一次可以定义多个常量;
四、联合体
1,联合体类型的定义:联合的声明和结构类似,但它的行为方式却和结构不同,联合的所有成员引用的是内存中的相同位置。 union联合体大小取决于所有成员中,占用空间最大的一个成员的大小。
判断内存的大小端:
int check_sys()
{
union Un
{
int i;
char c;
}un;
un.i = 1;
return un.c;//返回1为小端存储,返回0为大端存储
}
int main()
{
int ret = check_sys();
if (1 == ret)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
system("pause");
return 0;
}
2,联合大小的计算
联合的大小至少是最大成员的大小;
当最大成员大小不是最大对齐数 的整数倍的时候,就要对齐到最大对齐数的整数倍;
union U1
{
char s[11];
int n;
double d;
};
s占11字节,n占4字节,d占8字节,因此其至少需11字节的空间。然而其实际大小并不是11,用运算符sizeof测试其大小为16.这是因为这里存在字节对齐的问题,11既不能被4整除,也不能被8整除。因此补充字节到16,这样就符合所有成员的自身对齐了。从这里可以看出联合体所占的空间不仅取决于最宽成员,还跟所有成员有关系,即其大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除;
union U2
{
char s[5];
int n;
double d;
};
s占5字节,n占4字节,d占8字节,因此其至少需8字节的空间.而char型占用一个字节,int占用四个字节,double占用8字节,其最小公倍数为8字节,所以U2也就是占用8字节就可以了。