结构体声明与定义
struct S1
{
double d;
int i;
int j;
}
struct S2 //声明
{
char c;
int i;
struct S2 s2; //结构体嵌套
double d;
}s1,s2,s3;
struct S3
{
char str[];
struct S3 s3; //结构体不能嵌套调用自己,会形成死递归
struct S3 *s3 //可以嵌套自己的指针类型,形成链表
}
int main()
{
struct S1 s1={3.14,20,30};//定义
}
结构体在内存中的存储讲究内存对齐
结构体中的成员是从对齐数(成员字节数与编译器vs默认对齐数8对比较小的一个数字)的倍数字节数开始存储的
比如
struct S1
{
double d;
int i;
char c;
}
在内存中的存储字节数不是10,而是16
结构体内嵌套结构体,这种情况,嵌套的结构体在父结构体中的存储位置是按照其结构体内部成员最大对齐数的倍数偏移数位置来存储的。,含有嵌套结构体的结构体最终存储的字节要是结构体内部,含嵌套结构体内部最大的对齐数的倍数。
结构体的默认对齐数是可以修改的。
#pragma back(2) //#include
struct student
{
char c;
int i;
double d;
}
#pragma back();
以上代码代表将struct student类型的结构体默认对齐数改为了2,也代表它的最大可能对齐数就是2,但是不影响后续定义的结构体的默认对齐数,#pragma back();意思是取消设置的对齐数2
求结构体内部的偏移数
#include
#include
struct student
{
char c;
int i;
double d;
}
int main()
{
printf("%d\n",offsetof(struct student , c));
return 0;
}
以上代码求出的是struct student结构体内部c成员的偏移数
更改
位段
位段的声明
成员必须是int unsigned int signed int 或者是char类型
成员类型后面是冒号加数字
struct student
{
int _c:2;
int _d:4;
int _t:30;
}
以上代码的2,4,30代表三个int类型变量只占2个,4个,30个bit位
在内存中的存储方式
由于第一个成员是整型,所以先分配一个整型的字节,4个字节32个bit位,按照从低位到高位的二进制位进行存储,将_c和_d存储进32个二进制位中,剩余26个二进制位不足以存储30个二进制位的_t,所以再开辟一个整型的空间,将_t 存储
位段是不能跨平台的
1,在内存中存储是按照低位到高位存储不确定
2、分配完一个类型的空间使用完有剩余的空间,是利用还是不利用,不确定。
3、int位段被当成有符号数还是无符号数来处理不确定
4、int类型最大二进制位不确定,16位和32位系统不一样
枚举类型
enum colour
{
red=1,
green, //后面的数字依次加1
blue
};
枚举类型的成员是常量,初始化之后是不可修改的,跟#define相比方便,可以被检查类型,#define没有类型,而且是全局的,enum可以是局部的。
数值是整型
联合体也成共用体
union U
{
char c;
int i;
}u;
联合体意思是所有成员公用一块空间,最小分配空间是联合体最大类型成员所占用的空间,联合体成员更改一个成员的值就可能影响到其他成员的值,因为所有成员的空间是公用的。
联合体也牵扯到对齐数,联合体的空间大小要是其成员最大对齐数的倍数,比如上述代码联合体u的成员c的对齐数是1,i的对齐数是4,联合体空间大小是4,是4的倍数。
联合体的应用
//求电脑的存储大小端字节序
int main()
{
union U //c和i公用一个空间,i占用4个字节的空间,c则占用i的第一个字节的空间位置
{
char c; //i的赋值为1,取出c的值如果是1说明是小端,是0说明是大端
int i;
}u;
u.i=1;
printf("%d\n",u.c);
}
联合体,结构体,位段,枚举等都可以在函数内部声明,但是声明的同时要定义变量