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语言中,带指针参数的函数无法判断输入参数是否是有效的指针,有一些简单的方法来“解决”这个问题。

  1. 如果参数为结构体指针,那么可以为结构体指针增加一个位字段来表示其类型,并且封装malloc函数,在malloc之后为类型字段置位,在使用指针前,检查该位来判断指针是否有效。
  2. 第二种方法就是利用内存对齐的特性来判断,比如结构体指针的值(即地址值)由于对齐要求必须为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语言中的内存压缩技术的更多相关文章

  1. C语言中动态内存分配的本质是什么?

    摘要:C语言中比较重要的就是指针,它可以用来链表操作,谈到链表,很多时候为此分配内存采用动态分配而不是静态分配. 本文分享自华为云社区<[云驻共创]C语言中动态内存分配的本质>,作者: G ...

  2. C语言中的内存分配与释放

    C语言中的内存分配与释放 对C语言一直都是抱着学习的态度,很多都不懂,今天突然被问道C语言的内存分配问题,说了一些自己知道的,但感觉回答的并不完善,所以才有这篇笔记,总结一下C语言中内存分配的主要内容 ...

  3. C语言中的内存管理

    开始陆续的发一下唐老师视频的笔记吧,顺便带一些正冲哥书的的内容.不能一下都发出来,因为内容发多了自己也受不了,而且发的都是学习视频时候的一些笔记,可能会有一些问题不是很清晰. 先说一下C语言中的内存管 ...

  4. C语言中的内存对齐

    最近看了好多,也编了好多C语言的浩强哥书后的题,总觉的很不爽,真的真的好怀念linux驱动的代码,好怀念那下划线,那结构体,虽然自己还很菜. 同时看了一遍陈正冲老师的C语言深度剖析,收益很多,又把唐老 ...

  5. windev中的内存机制及其与C语言中的内存指针相似性(一)

    windev中的内存机制,是初入windev世界必须要越过的一道高山,以下我的理解和经验未必都对,如有错误或遗漏,以后再纠正或补充!另外,以下内容,咱先谈应用,再说对机制的认识和理解. 一.新建表单, ...

  6. C语言中堆内存的开辟和释放与内存处理函数

    C语言动态分配内存,malloc的出现就是来弥补静态内存分配的缺点 比如说我们在定义数组的时候,数组的长度必须是一个常量,不能改变的值,假如我事先定义了数组,一旦业务需求发生改变,那么这个数组就不能再 ...

  7. C语言中的内存相关问题

    内存是用来存储数据与程序的,对我们写程序来说非常重要.所以内存对程序来说几乎是本质需求.越简单的程序需要越少的内存,而越庞大越复杂的程序需要更多的内存. 注意:在嵌入式系统中有ROM和RAM两类内存, ...

  8. [c语言]c语言中的内存分配[转]

    在任何程序设计环境及语言中,内存管理都十分重要.在目前的计算机系统或嵌入式系统中,内存资源仍然是有限的.因此在程序设计中,有效地管理内存资源是程序员首先考虑的问题. 第1节主要介绍内存管理基本概念,重 ...

  9. c 从语言中的内存管理

    在C里,内存管理是通过专门的函数来实现.另外,为了兼容各种编程语言,操作系统提供的接口通常是 C 语言写成的函数声明(Windows 本身也由C和汇编语言写成). 1 分配内存 malloc 函数 需 ...

随机推荐

  1. C# 运用params修饰符来实现变长参数传递

    一般来说,参数个数都是固定的,定义为集群类型的参数可以实现可变数目参数的目的,但是.NET提供了更灵活的机制来实现可变数目参数,这就是使用params修饰符.可变数目参数的好处就是在某些情况下可以方便 ...

  2. Java读取Properties文件的六种方法

    使用J2SE API读取Properties文件的六种方法 1.使用java.util.Properties类的load()方法示例: InputStream in = lnew BufferedIn ...

  3. ASP.NET验证控件RegularExpressionValidator的常见表达式

    可以输入非0和0开头的数字:“^(0*[1-9][0-9]*|[1-9][0-9]*)$”只能输入数字:“^[0-9]*$”只能输入n位的数字:“^\d{n}$”只能输入至少n位数字:“^\d{n,} ...

  4. (剑指Offer)面试题24:二叉搜索树的后序遍历序列

    题目: 输入一个整数数组,判断该数组是不是某个二叉搜索树的后序遍历的结果,如果是则返回true,否则返回false. 假设输入的数组的任意两个数字都互不相同. 思路: 根据二叉搜索树的后序遍历特点,很 ...

  5. Flex data

    <?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="ht ...

  6. Jquery 提示

    1  文字提示: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  7. 批量更改数据库COLLATION

    企业内部有很多系统是繁体的,由于各方面的原因,公司目前正在实行简体化,但各系统中又有数据间的交换,所以系统只能一个一个的更改,以防同时出现过多的问题.由于原先数据库只能存储繁体,而原先已存在的数据则可 ...

  8. 【转】REST on Rails指南

    REST on Rails指南1:理解资源 这是来自http://www.softiesonrails.com的REST简明指南. PART I 在理解REST on Rails之前,有必要先思考一下 ...

  9. 解决从内部存储设备安装apk提示Permission Denied

    做应用商店,下载apk,考虑一种情况,如果没有sd卡的情况下就将apk下载到 Internal Cache目录下. 下载都正常,但是在安装的时候提示Permission Denied /data/da ...

  10. discuz制作

    discuz x1.5模板制作图文教程[1] 在开始前,先了介绍一下discuz!的几个模板文件:1.        header.htm ------------------------------ ...