目录
1.1、没有成员的结构体占用的空间是多少个字节? 答案:0字节
1.2、运行结果。https://www.runoob.com/try/runcode.php?filename=helloworld&type=c
三、在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:
四、 对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:
五:另一个特殊的例子是结构体中包含数组,其sizeof应当和处理嵌套结构体一样,将其展开,如下例子:
简要说明:结构体成员按照定义时的顺序依次存储在连续的内存空间,但是结构体的大小并不是简单的把所有成员大小相加,而是遵循一定的规则,需要考虑到系统在存储结构体变量时的地址对齐问题。
一,基本变量数值和空结构体的大小(0)
1.1、没有成员的结构体占用的空间是多少个字节? 答案:0字节
#include <stdio.h>
#include <stdlib.h>
struct
{
}stru_empty;
int main()
{
printf("sizof(char)=%d \n",(int)sizeof(char)); //1
printf("sizof(int)=%d \n",(int)sizeof(int)); //4
printf("sizof(short int)=%d \n",(int)sizeof(short int)); //2
printf("sizof(long int)=%d \n",sizeof(long int)); //8
printf("sizof(long)=%d \n",sizeof(long)); //8
printf("sizof(float)=%d \n\n",sizeof(float)); //8
printf("sizof(stru_empty)=%d \n",sizeof(stru_empty)); //0
return 0;
}
1.2、运行结果。https://www.runoob.com/try/runcode.php?filename=helloworld&type=c
二、首先介绍一个相关的概念——偏移量
struct stru
{
int a; //start address is 0
char b; //start address is 4
int c; //start address is 8
};
偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。比如上面的结构体,第一个成员a的偏移量为0。第二个成员b的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员c的偏移量是第二个成员的偏移量应该是加上第二个成员的大小(4+1)。
三、在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:
(1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
(2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。
例子1:
struct stru1
{
int a; //start address is 0
char b; //start address is 4
int c; //start address is 8
};
PS:用sizeof求该结构体的大小,发现值为12。int占4个字节,char占1个字节,结果应该是9个字节才对啊,为什么呢?这个例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量为5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小为12,满足要求。
例子2:
struct stru2
{
int i; //start address is 0
short m; //start address is 4
};
PS:成员i的偏移量为0;成员m的偏移量为4,都不需要调整。但计算出来的大小为6,显然不是成员i大小的整数倍。因此,编译器会在成员m后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。
例子3、4:
struct stru3
{
char i; //start address is 0
int m; //start address is 4
char n; //start address is 8
};
struct stru4
{
char i; //start address is 0
char n; //start address is 1 //(1)结构体变量中成员的偏移量必须是成员大小的整数倍。 偏移2,3空出来
int m; //start address is 4
};
虽然结构体stru3和stru4中成员都一样,但sizeof(struct stru3)的值为12而sizeof(struct stru4)的值为8。
由此可见,结构体类型需要考虑到字节对齐的情况,不同的顺序会影响结构体的大小。
四、 对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:
(1)展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。
(2)结构体大小必须是所有成员大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体。
例子1:
struct stru5
{
short i; //4
struct
{
char c;
int j;
} tt; //8
int k; //4
};
结构体stru5的成员tt.c的偏移量应该是4,而不是2。整个结构体大小应该是16。//16=4+4*2+4
例子2:
struct stru6
{
char i; //4
struct
{
char c;
int j;
} tt; //4*2=8
char a; //4个连续的char恰好为 4Byte
char b;
char d;
char e;
int f; //4
};
结构体tt单独计算占用空间为8,而stru6的sizeof则是20,不是8的整数倍,这说明在计算sizeof(stru6)时,将嵌套的结构体ss展开了,这样stu6中最大的成员为tt.j,占用4个字节,20为4的整数倍。如果将tt当做一个整体,结果应该是24了。
五:另一个特殊的例子是结构体中包含数组,其sizeof应当和处理嵌套结构体一样,将其展开,如下例子:
struct array
{
float f; //4
char p; //4
int arr[3]; //4*3=12
};
其值为20。float占4个字节,到char p时偏移量为4,p占一个字节,到int arr[3]时偏移量为5,扩展为int的整数倍,而非int arr[3]的整数倍,这样偏移量变为8,而不是12。结果是8+12=20,是最大成员float或int的大小的整数倍。
六、结构体包含指针和实际例子
6.1、代码和验证结果
#include <stdio.h>
#include <stdlib.h>
//#include "node.h"
typedef struct _node0 //8=64位地址指针
{
// int value; //4字节
struct _node0 *next; //指针地址大小为64位,8字节
}Node0;
typedef struct _node1 //16=4+4(空)+8
{
int value; //4字节,后续空4字节
struct _node1 *next; //指针大小8字节
}Node1;
typedef struct _node2 //16=4+1+3(空)+8
{
int value; //4字节
char a1; //1字节,后续空3字节
struct _node2 *next; //8字节
}Node2;
typedef struct _node3 //16=4+1+1+2(空)+8
{
int value; //4字节
char a1; //1
char a2; //1
struct _node3 *next; //8字节
}Node3;
typedef struct _node4 //24=4+1+3(空)+8+1+7(空)
{
int value; //4字节
char a1;
struct _node4 *next; //12字节
char a2;
}Node4;
int main()
{
int *p;
printf("sizeof(p)=%d\n",sizeof(p));//8 指针地址大小8字节
printf("sizeof(Node0)=%d\n",sizeof(Node0));//8
printf("sizeof(Node1)=%d\n",sizeof(Node1));//16
printf("sizeof(Node2)=%d\n",sizeof(Node2));//16
printf("sizeof(Node3)=%d\n",sizeof(Node3));//16
printf("sizeof(Node4)=%d\n",sizeof(Node4));//24
return 0;
}