C语言中的内存压缩技术
C语言中的内存压缩技术
前言
在整个研究生阶段我都在参与一个LTE协议栈实现的项目,在这个项目中,我们利用一个自己编写的有限状态机框架将协议栈中每一层实现为一个内核模块。我们知道,在编写内核代码时需要考虑内存的使用情况,如果使用内存超出了限制会导致Linux内核崩溃。在我负责的模块中,有的结构体定义非常庞大(几k到十几k),这么大的结构体对于内核编程来说显然有点太大了(在使用内核栈时,有可能造成内核栈溢出),另外,这些结构体可能会通过空中接口传给对等端。于是我们不得不想办法来压缩结构体的内存。
C中的内存对齐和填充
首先来看一段C语言声明变量的代码:(以下假设均在32位机上)
char *p;
char c;
int x;
如果要问声明这三个变量所占的内存大小,估计很多人会回答4+1+4=9字节。真正的答案应该是4+1+3+4=12bytes,其中多出来的3字节是内存对齐时的填充。C编译为了加快读取速度,对变量所存的地址是有要求的:char型所占内存为1字节,因此char型可以存放在任何地址上;而整形和指针在32位机上只能存放在以4为倍数的地址上;同理,short型只能存放在偶数地址上。因此上面的代码中,指针变量p肯定存放在以4为倍数的地址上,而为了使整形变量x也存放在以4为倍数的地址上,必须在char型变量c后面填充3字节。
那么,我们就可以想到如何减少变量所占内存————改变声明顺序。
char *p;
int x;
char c;
我们再来看看三个变量所占的内存大小为:4+4+1=9。因此,要减少内存的占用,需要先声明所占内存较大的变量即可(当然,是在影响代码的可读性的前提下)。
结构体中的内存对齐和填充
我们都知道结构体的内存分布有一个特点:为了加快访问速度,结构体中的内存是以最宽的变量大小对齐的。比如:
struct foo1 {
char *p;
char c;
long x;
};
因为结构体以4字节对齐,那么结构体中的所有变量的地址都为4的倍数,因此这个结构体实际上相当于:
struct foo1 {
char *p;
char c;
char padding[3];
long x;
};
上面这点可能是大家都知道的,另外大家也都知道结构体的地址是结构体中第一个变量的地址。那么另外一个问题来了:
struct foo1 {
char *p;
char c;
};
请问struct foo1[2]所占内存大小是多少? 大家可能都能猜到答案是2*(4+4)=16字节了。我再来详细解释一下。
结构体数组的第一个元素foo1[0]的地址是结构体中的第一个变量即p的地址,而p的地址只能为4的倍数;那么同理,结构体数组的第二个元素foo1[1]的地址也是结构体中的第一个变量即p的地址,而p的地址也只能为4的倍数,因此很显然结构体foo1实际上相当于:
struct foo1 {
char *p;
char c;
char padding[3];
};
最后,值得注意的是,嵌套的结构体中所有的变量也必须以最宽变量大小对齐。
gcc中的_attribute_ ((packed))
如果你读过内核源码,你很可能已经见过这种形式定义结构体和联合了,我们称之为紧凑型结构体/联合体。 _attribute_ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,但是请注意:这是编译器相关的指令。
我们来看看效果:
#include <stdio.h>
int main()
{
struct foo1
{
int x;
char y;
}__attribute__ ((__packed__));
struct foo2
{
int x;
char y;
};
printf("sizeof struct foo1: %d\n", sizeof(struct foo1)); //5
printf("sizeof struct foo2: %d\n", sizeof(struct foo2)); //8
return 0;
}
当然,也不一定有效,因为这还与结构体变量声明顺序等有关。
另外,gcc中的__attribute__机制还有许多其他的有趣功能,详细内容请查阅gcc文档。
20141208更新
今天发现在某些情况下我们可以利用内存对齐这个特性。我们知道在C语言中,带指针参数的函数无法判断输入参数是否是有效的指针,有一些简单的方法来“解决”这个问题。
- 如果参数为结构体指针,那么可以为结构体指针增加一个位字段来表示其类型,并且封装malloc函数,在malloc之后为类型字段置位,在使用指针前,检查该位来判断指针是否有效。
- 第二种方法就是利用内存对齐的特性来判断,比如结构体指针的值(即地址值)由于对齐要求必须为8的倍数,那么我们可以判断这个地址是否为8的倍数,不是8的倍数就是非法指针。
参考文献
[1]http://blog.csdn.net/yuwen_dai/article/details/17784109
[2]http://www.cnblogs.com/longdouhzt/archive/2012/11/15/2771351.html
C语言中的内存压缩技术的更多相关文章
- C语言中动态内存分配的本质是什么?
摘要:C语言中比较重要的就是指针,它可以用来链表操作,谈到链表,很多时候为此分配内存采用动态分配而不是静态分配. 本文分享自华为云社区<[云驻共创]C语言中动态内存分配的本质>,作者: G ...
- C语言中的内存分配与释放
C语言中的内存分配与释放 对C语言一直都是抱着学习的态度,很多都不懂,今天突然被问道C语言的内存分配问题,说了一些自己知道的,但感觉回答的并不完善,所以才有这篇笔记,总结一下C语言中内存分配的主要内容 ...
- C语言中的内存管理
开始陆续的发一下唐老师视频的笔记吧,顺便带一些正冲哥书的的内容.不能一下都发出来,因为内容发多了自己也受不了,而且发的都是学习视频时候的一些笔记,可能会有一些问题不是很清晰. 先说一下C语言中的内存管 ...
- C语言中的内存对齐
最近看了好多,也编了好多C语言的浩强哥书后的题,总觉的很不爽,真的真的好怀念linux驱动的代码,好怀念那下划线,那结构体,虽然自己还很菜. 同时看了一遍陈正冲老师的C语言深度剖析,收益很多,又把唐老 ...
- windev中的内存机制及其与C语言中的内存指针相似性(一)
windev中的内存机制,是初入windev世界必须要越过的一道高山,以下我的理解和经验未必都对,如有错误或遗漏,以后再纠正或补充!另外,以下内容,咱先谈应用,再说对机制的认识和理解. 一.新建表单, ...
- C语言中堆内存的开辟和释放与内存处理函数
C语言动态分配内存,malloc的出现就是来弥补静态内存分配的缺点 比如说我们在定义数组的时候,数组的长度必须是一个常量,不能改变的值,假如我事先定义了数组,一旦业务需求发生改变,那么这个数组就不能再 ...
- C语言中的内存相关问题
内存是用来存储数据与程序的,对我们写程序来说非常重要.所以内存对程序来说几乎是本质需求.越简单的程序需要越少的内存,而越庞大越复杂的程序需要更多的内存. 注意:在嵌入式系统中有ROM和RAM两类内存, ...
- [c语言]c语言中的内存分配[转]
在任何程序设计环境及语言中,内存管理都十分重要.在目前的计算机系统或嵌入式系统中,内存资源仍然是有限的.因此在程序设计中,有效地管理内存资源是程序员首先考虑的问题. 第1节主要介绍内存管理基本概念,重 ...
- c 从语言中的内存管理
在C里,内存管理是通过专门的函数来实现.另外,为了兼容各种编程语言,操作系统提供的接口通常是 C 语言写成的函数声明(Windows 本身也由C和汇编语言写成). 1 分配内存 malloc 函数 需 ...
随机推荐
- HDU 1712 ACboy needs your help (分组背包模版题)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1712 有n门课,和m天时间.每门课上不同的天数有不同的价值,但是上过这门课后不能再上了,求m天里的最大 ...
- C#中的DllImport
大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如 Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功 ...
- 快速排序详解以及java实现
快速排序作为一种高效的排序算法被广泛应用,SUN的JDK中的Arrays.sort 方法用的就是快排. 快排采用了经典的分治思想(divide and conquer): Divide:选取一个基元X ...
- C:数组
数组.排序 关于排序 :参考 关于数组: 参考 求a[i][j]行与列的和然后求平均值 参考 二维数组使用指针的表示方法 参考 字符串数组:char name [5][20] ={ {} , {} ...
- 网页打印时设置A4大小
最近开发项目时遇到了网页打印的问题,这是问题之二,打印宽度设置 在公制长度单位与屏幕分辨率进行换算时,必须用到一个DPI(Dot Per Inch)指标. 经过我仔细的测试,发现了网页打印中,默认采用 ...
- 从零开始学C++之虚函数与多态(二):纯虚函数、抽象类、虚析构函数
一.纯虚函数 虚函数是实现多态性的前提 需要在基类中定义共同的接口 接口要定义为虚函数 如果基类的接口没办法实现怎么办? 如形状类Shape 解决方法 将这些接口定义为纯虚函数 在基类中不能给出有意义 ...
- 在Assertion中获取Response的headers,获取headers中信息,获取body(content)
// get the headers of the requestdef content= messageExchange.getResponseContent()def headers = mes ...
- Android图片圆角效果
一般来说图片加圆角可以使用 Java 的方式来进行, 对图片略加处理即可, 但也可以使用纯XML+Nice-Patch图片来进行, 这样的速度会更快. 如果背景是纯色的情况下建议使用此方法. 原理则是 ...
- 安装linux系统并配置那点事
安装完成后,将看到如下控制台: 输入以上安装信息中所填写的用户名(user),随后输入密码(user),即可登录 Ubuntu. 随时可使用 Ctrl + Alt 快捷键可离开虚拟机. 3 配置 Ub ...
- GXPT(一)——UI设计
通过长时间的积淀.TGB再次開始GXPT模式,JAVA版..NET版两条线同一时候进行. 纯面向对象的底层架构的搭建:easyUI+MVC的界面设计:工作流的再次雄起.云平台的构想:Confluenc ...