结构体
概念:将相同或不同数据放在一起的一种聚合数据类型。
声明:
struct tag
{
member - list;
}variable - list;
对上面的代码的说明:1.tag是结构体的标签或是名称,自己可以命名(见名知意)。可以省略,但是不建议省略。
2.member-list是结构体成员列表(不能为空),且可以是任意类型。struct关键字不能省略。
3.variable-list是变量列表,可以一个或多个,中间用“,”隔开。可以省略,建议省略(按需所取)。
4.variable后面的“;”不能省略。
匿名结构体:(不推荐使用)
结构体1
struct
{
int a;
char b;
float c;
}x;
结构体2
struct
{
int a;
char b;
float c;
}*p;
虽然两个结构体(结构体1和结构体2)的成员都一样,但是x和*p不是同一种类型。结构体之间不能复制。有几个结构体,就有几种类型。
结构体3
struct
{
int a;
char b;
float c;
}x,*p;
此时的x和*p才是同一种类型。
结构体的成员的访问
三种访问方式:1.使用结构体变量名+“.” (eg: S.name)
2.使用指针+“.” (eg: (*p).name )
3.使用指针指向“-->” (eg: p-->name)
结构体的自引用
代码1(错误使用)
struct node
{
int data;
struct node next;
};
代码2(正确使用)
struct node
{
int data;
struct node* next;
};
总结:结构体的自引用只能用自身结构体类型定义指针变量。(这样才可以给其确定类型,进而开辟空间。)
结构体的不完整声明
代码举例:
struct B;
struct A
{
int a;
struct B*pb;
};
struct B
{
int b;
struct A*pa;
};
结构体变量的定义和初始化(类比数组就好)
注意:结构体和数组都能不能整体赋值。
结构体的传参 (不发生降维问题,结构体传参的时候传的是结构体指针)
结构体的内存对齐(重点)
结构体内存对齐的规则:
1.第一个成员在与结构体变量偏移量为0的地址处。(第一个成员对齐数为0,默认是对齐了的)
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处(起始偏移量要整除对齐数)。对齐数=编译器默认的对齐数与该成员大小的较小值。(VS默认的值为8,Linux中默认的是4)
3.结构体总大小为最大对齐数(每一个成员变量都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。总体来说:结构体的内存对齐是拿空间来换取时间的做法。
下面举例说明:
struct S3
{
double d;
char c;
int i;
};
该结构体的大小为16.分析:double对齐数:8。char对齐数:1。int 对齐数:4。char要对齐到4上,所以要加上3。该结构体的最大对齐数是8, 所以最后结构体的大小为16(8+1+3+4).
struct S4
{
char c1;
struct S3 s3;
double d;
};
该结构体的大小为32.分析:char对齐数:1。结构体S3的最大对齐数是8,char要对齐到S3要加上7,又因为S3本身大小为16,所以加上16,还有char自身的大小1,double对齐数为8,所以最终S4大小为32(1+7+16+8)。
位段
位段的声明和结构体是类似的,有两个不同:
1. 位段的成员必须是int,unsigned int, signed int或是char类型。
2.位段的成员后边有一个冒号和一个数字。比如:
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
};
A就是一个位段。int _a:2; 表示_a占用int的2个空间,也只能使用冒号后面数字个比特位。并且_b放置的位置是在_a使用后剩余的空间里。若是剩余的空间不够使用,才会开辟新的空间来使用。所以位段A的大小是8(_a,_b,_c._d总共需要2个int类型的空间)
即就表明了位段的原则是:压缩存储,能挤就挤的原则。位段的内存分配
1. 位段的成员可以是int ,unsigned int ,signed int 或是char类型。
2 .位段的空间上是按照需要以 4个字节(int)或者1个字节(char)的方式来开辟的。
3. 位段涉及很多不确定的因素,位段是不跨平台的,注重可以值得程序应该避免使用位段。
总结:跟结构体相比,位段可以达到同样的效果,可以很好的节省空间,但是有跨平台的问题存在。
枚举
含义:一 一列举。
定义:
enum Color
{
Red,
Blue,
Green,
};
枚举常量之间用“,”隔开,枚举类型定义变量,只能被赋值为枚举的常量。{}中的内容是枚举类型的可能取值,也叫枚举常量。这些可能取值都是有值的,默认从0开始,依次递增1,当然在定义的时候也可以赋初值。
枚举的优点:
1.增加代码的可读性和可维护性。
2.和#define 定义的标识符比较枚举有类型检查,更加严谨。
3.防止了命名污染(封装)。
4.便于调试。
5.使用方便,一次可以定义多个常量。
联合(共用体)
这种类型包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)。
联合体大小的计算:
1.联合体的大小至少是最大成员的大小。
2。当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
比如:
union Un
{
char c;
int i;
};
un的大小就是4.分析:char的对齐数是1,int的对齐数是4,所以该联合体的大小应该是4.
注意:联合体的所有成员都可以认为是联合体内的第一个成员。并且联合体内所有成员的地址都是一样的。
下面是联合体和结构体的一个巧妙连用:
将long 类型的IP地址转为点分十进制的表示形式:
#include<stdio.h>
#include<windows.h>
int main()
{
union ip_addr
{
unsigned long addr;
struct
{
unsigned char c1;
unsigned char c2;
unsigned char c3;
unsigned char c4;
}ip;
};
union ip_addr my_ip;
my_ip.addr = 176238749;
printf("%d.%d.%d.%d\n",my_ip.ip.c4,my_ip.ip.c3,my_ip.ip.c2,my_ip.ip.c1);
system("pause");
return 0;
}