目录
结构体的详说
结构体的应用
1.结构体的匿名声明:说明下,结构体是可以不完全声明的!
思考:下面的代码正确吗?
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
}a[20],*p;
p = &x;
上述的代码中,虽然看似俩个结构体的数据类型是一样的,但是编译器会把上面俩个声明当成俩个完全不同的俩个类型!因此非法!所以匿名声明是存在隐患的!
2.结构体的自引用(思考下下述的代码是否正确):
struct Node
{
int a;
struct Node next;
};
很明显,这个代码看似正确,在结构体中又调用了这个结构体!可是试着想下:这个代码占的内存是多少呢?能否算的清楚sizeof(struct Node)?认真观察下可以知道--这个代码存在着死循环的隐患。因为在进入这个结构体后又会调用它,循环不止。那么正确的自引用是怎么样的呢?
struct Node
{
int a;
struct Node* next;
};
很简单,将内部的next改变它的类型就可以了,将它改变为指针,使其可以指向原来的结构体;
以下代码运用了指针啊,那以下的代码是否正确?
typedef struct
{
int a;
Node* next;
}Node;
知道编译器运行是自上而下便可以得知,这个结构体在运用typedef修改类型名字靠后,因此非法的!
3.结构体变量的定义与初始化:
有了结构体的类型,其实定义变量是很简单的!下面只说一种比较不常见的初始化!
struct Node
{
int a;
struct Node* next;
}n1 = {20,NULL};
4.结构体内存对齐:
既然掌握了结构体的基本运用,那么也应该知道如何去计算结构体的大小!
struct S1
{
int a;
char c;
};
struct S2
{
char c1;
int a;
char c2;
};
struct S3
{
char c1;
int a;
char c2;
char c3;
};
int main()
{
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
printf("%d\n", sizeof(struct S3));
return 0;
}
要想知道上述的调试结果为何如此,其实要了解并理解结构体的四大对齐原则:
一.结构体的第一个成员一定在与结构体变量偏移量为0的地址处!
看s1的存储方式是这样的;a对应的是整型占四个字节!位于0偏移处!
二:其他成员的变量要对齐到某个数字(对齐数)的整数倍地址处。
注:这个对齐数是指:编译器默认的一个对齐数与该成员的大小的较小值!
而VS的默认对齐数是8;因为c是字符型,大小为1,对齐数为1;因此:在这里每一个偏移量都是1整数倍,对齐的是可存储的最小的整数倍偏移量!因此4是c存储的!
既然只要五个字节存储额,为什么最后调试出大小是8呢?
这个就涉及到了第三条原则:结构体的总大小为最大对齐数的整数倍!
分析下:对于a而言,它的大小是4,VS默认对齐数是8,很明显,对于a而言,它的大小小于默认对齐数,它的对齐数就是4,而c的对齐数是1,因此,这个结构体的总大小必须为4的整数倍!但是int+char=5;大于4,因此这个大小必须进4,使其为8;
下面分析下s2:
可知,s2的存储方式如下:
其占了九个字节对吧!但是9>8进4,因此为12.
!!!s3其实也是如此!
既然普通结构体是这样计算的!那么嵌套结构体又如何计算大小呢?这里就要引入第四原则:
如果嵌套了结构体的情况下,嵌套结构体对齐到自己最大的对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含了嵌套结构体的对齐数)的整数倍!
struct S3
{
double d;
char c;
int i;
};
struct S4
{
char c1;
struct S3 s3;
double d;
};
int main()
{
printf("%d\n", sizeof(struct S4));
return 0;
}
计算可知:S3大小16,它的最大对齐数是8,s4含有三个变量,c1,s3,d,这个的最大对齐数肯定是八!
注:包含了嵌套结构体的对齐数!这里解释下:它说的结构体的整体大小最大对齐数的整数倍是要将嵌套结构体S3的内部变量d,c,i的对齐数拿出同时与S4进行对比选出最大对齐数!
因此可以算出大小为32;
思考下:为什么要进行内存对齐呢?
1.平台原因(移植原因):不是所有的硬件平台都可以访问任意的地址上的任意数据!某些硬件平台只能在某些硬件平台只能在某些地址处某些特定类型的数据,否则抛出硬件异常!
2.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐!
原因在于:为了访问未对齐内存,处理器需要俩次内存访问,而对齐的内存仅需要访问一次!
如;
struct S1
{
char a;
int b;
};
它的未对齐的情况下:内存存储是:
在编译器中,它的编译的访问是4个字节4个字节访问的!可以看出变量b的访问要访问俩次才能访问完b。
而对齐后的访问是这样的!
它只要访问一次就能完全访问完b!
其实,要想满足既要对齐又要节省空间时:就应该要让占用空间小的成员尽量集中在一起!
如:
struct S1
{
char a;
char b;
int c;
};
struct S2
{
char a;
int c;
char b;
};
可知S1大小为8,S2大小为12!
5.修改默认对齐数:可以用#pragma pack()调默认对齐数
#pragma pack(1)//修改默认对齐数为1;
struct S
{
char c1;
int i;
char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认8!
int main()
{
printf("%d\n", sizeof(struct S));//调试结果为6
return 0;
}