1.结构体
- 结构体声明
例如描述一个学生:
struct stu
{
char name[20];
short age;
char sex[3];
};
特殊声明:
struct
{
int a;
int b;
int c;
char d[20];
}x;
struct
{
int a;
int b;
int c;
char d[20];
}a[10],*p;
注意:上面两个结构体省略了结构体标签,编译器会把上面两个声明当成两个完全不同的两个类型,因此p=&x是非法的
- 结构体自引用
struct Node
{
int data;//数据域
struct Node* next;//指针域
};
注意:
typedef struct
{
int data;
}Node;//这是结构体类型,不用开辟空间
struct
{
int data;
}Node;//这是结构体变量,需要开辟空间
- 结构体变量和初始化
struct point//类型声明
{
int x;
int y;
}p1;//声明类型的同时声明变量
struct point p2;//声明结构体变量p2
struct stu
{
char name[20];//名字
int age;//年龄
};
struct stu s = { "lisi", 20 };//初始化
struct Node
{
int data;
struct point p;
struct Node* next;
}n1 = { 10, { 1, 2 }, NULL };//结构体嵌套初始化
- 结构体内存对齐
首先,我们要了解为什么要进行内存对齐?
1、平台原因,并不是所有的硬件平台都能访问任意地址上的任意数据,在一些硬件平台上,只能在某些地址处取特定类型的数据,否则出现硬件异常
2、性能原因:数据结构(尤其是栈)应当尽可能在自然边界上对齐,因为为了访问未对齐的内存,处理器需要进行两次内存访问,而对齐的内存访问仅需要一次。
对齐原则:
(1)基本类型自身的对齐值
(2)自定义类型的对齐值(自身最大值)
(3)程序指定的对齐值(#pragma a pack(n))
(4)有效对齐(自身和程序指定的较小的一个)
例如:
typedef struct Test
{
short a;//2+6
struct
{
int b[100];//400
double c;//8
char d;//1+7
};
long e;//4+4
}Test;
结果为432(vs或者vc平台下)
但若是这样:
typedef struct Test
{
short a;
struct t
{
int b[100];
double c;
char d;
};
long e;
}Test;
在vc平台下就为8,因为在struct后面加上t,表示为类型,不开辟空间,所以没有大小,但两者在vs平台下都为432(编译平台原因)
__ 如何让结构体按照指定的对齐参数进行对齐__:
#pragma pack(n)
强制使其对齐模数为n
如何知道结构体中某个成员相对于结构体起始位置的偏移量?
size_t offsetof(structname,membername);
直接使用offsetof宏,如何模拟实现呢?
#define my_offsetof(structName,memName)\
(size_t)&(((structName*)0)->memName)//'\'为续行符
分析:((structName*)0)表示有一个指向结构体的指针,它的值为0,&(((structName*)0)->memName)表示取结构体中的成员,由于这个类的基址是0,结构体成员的地址就是其在结构体中的偏移量。
- 结构体传参
函数传参的时候,参数需要压栈,会有时间和空间上的系统开销,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降,因此,结构体传参的时候,要传结构体指针。 - 位段
位段的成员可以是int unsigned int signed int char类型
位段的大小计算
例如:
struct A
{
int a : 2;
int b : 5;
int c : 10;
int d : 30;
};
分析:它的大小为8,’:'后面的为bit位,a需要2个bit位,int类型表示开辟4个字节,为32个bit位,还剩30个bit位,b需要5个bit位,剩25个bit位,c需要10个bit位,剩15个bit位,d需要30个bit位,不够,重新开辟一个int类型的空间,用掉30个bit位,剩2个,一共8个字节。
再例如:
#pragma pack(1)
struct tag
{
unsigned char a : 1;
unsigned char b : 2;
unsigned char c : 6;
unsigned char d : 4;
unsigned char e;
unsigned char f : 4;
unsigned char g;
};
分析:它的大小为6,先开辟一个char类型的空间,为1个字节,即8个bit位,a需要1个bit位,剩7个bit位,b需要2个bit位,剩5个bit位,c需要6个bit位,不够,重新开辟一个字节,用掉6个bit位,剩2个bit位,d需要4个bit位,不够,再开辟一个字节,用掉4个bit位,剩4个bit位,e占用一个字节,再开辟一个字节,f需要4个bit位,剩4个bit位,g占用一个字节,一共开辟了6个字节,在以1对齐时,为6,以4对齐时,按照内存对齐第四条原则,还是6.
例如:
typedef struct Test
{
char a : 1;
int b : 2;
}Test;
分析:结果为8,存在对齐
结论:位域有两个限制条件:
1>不能跨字节存储
2>不能跨类型存储
2.枚举
- 类型定义
例如:
enum color
{
red,
green,
blue
};
成员变量没有赋值,默认从零开始依次递增1,若赋值也遵循递增1,例如:
enum ENUM_A
{
x1,
y1,
z1=255,
a1,
b1
};
enum ENUM_A enumA = y1;
enum ENUM_A enumB = b1;
int main()
{
printf("%d\n", enumA);
printf("%d\n", enumB);
return 0;
}
分析:x1为0,y1为1,z1赋值为255,则a1为256,b1为257,结果为1和257
3.联合体
- 联合体声明
union Un
{
char c;
int i;
};
- 联合体特点:
共用一块空间,一个联合变量的大小为最大变量的大小
union Un
{
char c;
int i;
};
union Un un;
printf("%d\n", sizeof(un));
大小为4
- 经典应用:当前计算机大小端存储
int check()
{
union Un
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
int main()
{
int ret = check();
if (ret == 1)
{
printf("小端\n");
}
else
printf("大端\n");
return 0;
}
- 联合大小计算
1> 联合大小至少为最大成员大小
2> 当最大成员大小不是最大对齐数整数倍时,就要对齐到最大对齐数的整数倍
例如:
union un1
{
char arr[5];
int i;
};
union un2
{
short c[7];
int i;
};
int main()
{
printf("%d\n", sizeof(union un1));
printf("%d\n", sizeof(union un2));
return 0;
}
分析:第一个大小为8,arr占5个字节,int占4个字节,对齐到最大数的整数倍,为8,第二个大小为16,分析同上。
- 重要的例子
unsigned short *arr[10][10];
typedef union un
{
unsigned long a;
unsigned short b[7];
unsigned char c;
}un1;
un1 st, *pst;
int main()
{
printf("%d\n", sizeof(arr));//400
printf("%d\n", sizeof(st));//16
printf("%d\n", sizeof(pst));//4
printf("%d\n", sizeof(*pst));//16
system("pause");
return 0;
}
分析:arr为指针数组,大小为整个数组大小为400,st为联合体变量,联合大小为16,pst为联合体指针,指针大小为4,*pst为联合体指针解引用,为联合大小16。