C语言《认识结构体》---重点解析内存对齐
文章目录
- 结构体
- 一、结构体概念
- 二、结构体声明
- (1)、特殊声明
- 三、结构体变量的定义和初始化
- (1)、定义初始化例子1
- (2)、定义初始化例子2:
- (3)、结构嵌套
- 四、结构体自引用
- (1)、 结构体自引用概念
- (2)、不使用typedef时
- (3)、使用typedef时
- 五、结构体内存对齐
- (1)、结构体内存对齐概念
- (2)、结构体内存对齐规则
- (3)、普通结构体实战练习
- (4)、结构体嵌套
- (一)、结构体嵌套概念及规则
- (二)、嵌套结构体实战练习
- (5)、修改默认对齐数
结构体
一、结构体概念
1、结构体是由一批数据组合而成的结构型数据。而这些数据又是成员变量,这些变量可以是不同的类型。
2、结构体通常用来表示类型不同但是又相关的若干数据。
3、结构体类型不是又系统定义好的,需要我们自己去定义。C语言只是提供了关键字struct来标识所定义的结构体类型。
二、结构体声明
我们定义一个结构体类型,描述一个书的基本信息的时候,有编号,书名,作者,价格等。这些视为成员变量如下:
struct Book
{int code;//编号
char name[20];//书名
char author[20];//作者
double price;//价格
};
或者描述一个人,有名字,身份证,性别,年龄如下:
struct Pepole
{char name[20];//名字
int ID;//身份证号
char sex[10];//性别
int age;//年龄
};
注意括号最后的分号不能少些。
(1)、特殊声明
特殊声明就是在声明结构的时候,可以做不完全声明,可以把名字省略掉,即匿名结构体类型。看如下:
struct
{int code;//编号
char name[20];//书名
char author[20];//作者
double price;//价格
}B;
struct
{char name[20];//名字
int ID;//身份证号
char sex[10];//性别
int age;//年龄
}*p;
注意不能这样写,*p=&x,把x的地址放在指针变量p中,编译器会报警告,它会把上面的两个声明当成完全不同的两个类型。
三、结构体变量的定义和初始化
(1)、定义初始化例子1
struct Grade
{int a;int b;
}x1;
struct Grade x2;
struct Grade x3={a,b};
1、如上,先定义一个成绩结构类型,x1表示声明类型的同时定义变量x1。
2、x2表示定义结构体变量x2。
3、x3表示,初始化,定义变量的同时赋初值。
(2)、定义初始化例子2:
struct Pepole//类型声明
{char name[20];//名字
char sex[10];//性别
int age;//年龄
};
struct Stu s = {"lihua","男","17"};//初始化赋初值
(3)、结构嵌套
在结构体里面再套上一个结构体类型。
struct Book
{char author[20];//作者
double price;//价格
};
struct Pepole
{char name[20];//名字
char sex[10];//性别
int age;//年龄
}p;
对上面进行嵌套如下:
struct Book
{char name[20];//书名
struct Pepole p;
double price;//价格
}n={"C语言",{"lihua",{"男",17},66};//结构体嵌套初始化赋初值
struct Book m={"C语言",{"lihua",{"男",17},66};//另一种结构体嵌套初始化赋初值
以上的变量都是全局变量。也可以放在main 函数里面创建局部变量,更加安全。
四、结构体自引用
(1)、 结构体自引用概念
结构体自引用是在结构体里面,创建一个指向自身类型结构体的指针
(2)、不使用typedef时
struct Grade
{int a;
struct Grade next;
};
以上声明是非法的,它是一个无限循环,成员next是一个结构体,内部还会有成员是结构体,构成一个死循环,当然长度也就无法判断。
需要做出更正:
struct Grade
{int a;
struct Grade *next;
};
让它变为指针,因为指针长度是确定的,它是一个地址,机器能判断它是4或8字节。因此编译器能判断它的长度。
(3)、使用typedef时
typedef struct
{int data;
Node* next;
}Node;
以上是错误的,因为next虽然是一个指针,但这里的Node并没有定义。
typedef是为结构体创建一个别名NODE,可是类型名的作用域是从语句的结尾开始的,在里面是无法使用的,因为没有定义。
我们有三种解决方法:
typedef struct Node//第一种
{int data;struct Node* next;
}Node;struct NODE;//第二种
typedef struct NODE Node;
struct NODE
{int data:Node *next;
}struct NODE
{int data;struct NODE* next;
}
typedef struct NODE Node;
五、结构体内存对齐
(1)、结构体内存对齐概念
结构体内存对齐是指我们创建一个结构体变量时,会向内存申请所需的空间,用来存储结构体成员的内容。
(2)、结构体内存对齐规则
1、结构体的一个成员直接对齐到相对于结构体变量起始位置为0的偏移处。
2、从第二个成员开始,要对齐到某个对齐数的整数倍的偏移处。
3、对齐数:结构体成员自身大小和默认对齐数的较小值。
4、默认对齐数大小:VS默认是8、linux环境上不设对齐数,也就是结构体成员自身大小。
5、最后一步,结构体的总大小,必须是成员里面中最大对齐数的整数倍。
(3)、普通结构体实战练习
struct S2
{char c1;int c2;short i;
};
int main()
{printf("%d\n", sizeof(struct S2));return 0;
}
如果不按规则,我们会想当然会认为这个结构体的大小是7。看下结果:
然而并不是。如下看图解析:
1、我们先求出各个成员的对齐数,char是1,int是4,short是2(和VS对齐数8比取最小值),最大对齐数是4。
2、按规则,第一个成员是char类型,对齐到相对于结构体变量起始位置为0的偏移处。
2、第二个成员是int类型占4个字节,但下一个地址偏移处是1,不是它的整数倍,继续往下找,找到4,就从4开始填4个字节,直到7处停止,最后一个是short类型而下一个地址是从8开始,是2的倍数,填充到9停止。
3、最后一步,此时已经有了10个字节,判断它是否是最大对齐数的整数倍,10不是4的倍数,就往下找到12,所以结构体的大小是12。
(4)、结构体嵌套
(一)、结构体嵌套概念及规则
在结构体里面嵌套一个结构体,求此结构体的大小。
规则:
除上面规则外,加上一条:嵌套的结构体要对齐到自己最大的对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
(二)、嵌套结构体实战练习
struct S2
{char c1;int c2;short i;
}s3;
struct S4
{char c1;struct S2 s3;double d;
};
int main()
{printf("%d\n", sizeof(struct S4));return 0;
}
如下看图解析:
1、求S4中各个成员的对齐数,char类型是1,因为是嵌套要求它的最大对齐数是4,d是8,所有最大对齐数是8。
2、为了方便起见 我们从1开始(也可以把它想象成从0开始),char占1字节,直接填。
3、第二个成员是嵌套结构体,要对齐到自己最大对齐数的整数倍处,从开始填12个字节,直到15停止,最后一个是double类型对齐数是8,并占8个字节,16符合条件,从它开始填到24。
4、最后一步是判断填完之后是否是所有最大对齐数的整数倍,24是8的倍数符合条件结束。
结果如下:
(5)、修改默认对齐数
1、因结构体的字节对齐方式在不同的编译器中不一样,我们可以用预处理命令#pragma pack(n),可以将结构体的变量强制对到某个值为n的字节,这样也可以让我们节省空间。
2、在用完这个命令之后,要在定义这个结构体后加一个取消自定义对齐的命令:#pragma pack()。
如下例子:
#include <stdio.h>
#pragma pack(1)//设置默认对齐数为1
struct S1
{char c1;int m;short i;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{printf("%d\n", sizeof(struct S1));return 0;
}
上面按默认对齐算的是12,修改对齐数得到应是1。
结果:
C语言《认识结构体》---重点解析内存对齐相关推荐
- C语言结构体的大小 — — 内存对齐和位域
C语言结构体对齐 C语言结构体对齐也是老生常谈的话题了.基本上是面试题的必考题.内容虽然很基础,但一不小心就会弄错.写出一个struct,然后sizeof,你会不会经常对结果感到奇怪?sizeof的结 ...
- 失传的C结构体打包技艺 -- 内存对齐
作者:Eric S. Raymond 原文链接:http://www.catb.org/esr/structure-packing/ 谁应阅读本文 本文探讨如何通过手工重新打包C结构体声明,来减小内存 ...
- [c/c++] programming之路(28)、结构体存储和内存对齐+枚举类型+typedef+深拷贝和浅拷贝...
一.结构体存储 #include<stdio.h> #include<stdlib.h>struct info{char c; //1 2 4 8 double num; // ...
- 【C语言系列】-结构体中的内存对齐
目录
- C语言之结构体(进阶篇)
目录 1.结构体的内存对齐 如何计算呢? 掌握结构体的对齐规则: 为什么存在内存对齐呢? offsetof 位段 什么是位段? 比如: 位段的内存分配 举个例子 位段的跨平 ...
- 【C 语言】结构体 ( 结构体 数组 作为函数参数 | 数组 在 堆内存创建 )
文章目录 一.结构体 数组 作为函数参数 ( 数组 在 堆内存创建 ) 二.完整代码示例 一.结构体 数组 作为函数参数 ( 数组 在 堆内存创建 ) 在上一篇博客 [C 语言]结构体 ( 结构体 数 ...
- C语言定义了一个结构体怎么分配内存?C\C++中结构体变量与结构体指针内存分配问题?
C语言定义了一个结构体怎么分配内存?C\C++中结构体变量与结构体指针内存分配问题? 问题1:结构体指针最开始怎么分配内存?用sizeof()是不能确定大小的. 问题2:给结构体变量分配之后,是否还要 ...
- c语言中申请内存并初始化,c语言中结构体的定义、初始化及内存分配
#include struct person { char *name; int age; }; int main() { //结构体可以定义在函数内,也可以定义到函数外 //相当于全局变量与局部变量 ...
- C语言小记:结构体及其在内存中的储存形式
C语言小记:结构体及其在内存中的储存形式 1.结构体 结构体的作用:自定义一个包含多种不同数据类型的变量. 语法: struct 结构体名称 {结构体成员1:结构体成员2:结构体成员3:... } 定 ...
最新文章
- Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性-[Android取经之路]
- Linux中的盘符问题
- 第7章:Kubernetes存储
- Python自然语言处理学习笔记(19):3.3 使用Unicode进行文字处理
- .NET Core 取消令牌:CancellationToken
- c语言 自动化编译环境,《C编程.开始C》3.编译基础
- 华为P30 Pro终极渲染图曝光:后置徕卡四摄颜值出众
- zlib源码导读[转]
- linux 对象 调出r_[转载]linux系统下安装R包
- CS224N 笔记一
- 3D建模的通用文件格式
- 夏令营501-511NOIP训练18
- 基于深度学习技术的AI输入法引擎
- 送人玫瑰,手有余香----七夕情人节的电子玫瑰
- 浏览器(V8)的垃圾回收机制
- 3D建模中的BIP骨骼镜像对称画是什么东西?
- 深度学习框架【MxNet】的安装
- 后台缓存收回进程无法释放上下文[/BUSINESS的缓存的[10]%-请考虑增加缓存的最大大小
- 【备忘】Word在任意第M-N页间插入页码
- 安霸发布4K摄像机SoC
热门文章
- 【笔记】Linux安装RabbitMQ
- 百分之 95% 的程序员不知道 Trending 是什么。
- 作为前端的你不能不知道的知识,总结物理像素,像素密度,分辨率,CSS像素,设备像素比,二倍图,layout viewport、visual viewport 、ideal viewport、meta。
- Postman实现接口测试(附项目实战)
- Java获取本机名称、网卡名称、IP、MAC
- golang 消息忽略 signal.Ignore(syscall.SIGHUP) 示例
- gRPC遭抛弃!Storj为何使用DRPC替代gRPC?
- 谈谈从经纬度坐标到欧式距离的转化【matlab实现】
- Mysql循环插入数据
- 大咖说·计算讲谈社|当我们在谈目标时,究竟在谈什么?