C语言基本功教程系列 高效无错的内存访问

首先说说动态内存分配。在c语言里用的最多的是malloc和free,在c++则是new new[] delete 和delete[]. 这几个函数是动态内存分配的基础,最常用但也是最占用CPU资源的系统调用之一.而且在大量使用以后很容易造成内存的碎片。如果系统内存中的碎片太多,就会在分配大块内存的时候失败或者只能在虚拟内存上分配内存,这就是为什么有些程序在运行了2,3个小时以后很容易速度不稳定和容易崩溃的原因。另外一个重要的因素就是程序员在写程序的时候,经常会分配了内存而忘记释放。特别是写超过 10W行代码的时候往往忘记了在哪里分配了内存. 所以内存的管理对于游戏的稳定性是非常重要的问题,毕竟大家都是动不动玩上10个小时不休息的主。

目前比较流行的解决方法就是在系统提供的内存分配函数上面,写自己的内存管理函数。在C语言里重写malloc和free,对每个内存的分配和使用情况做跟踪记录。在C++里则是重载操作符 new和delete. 通过提供自己的库,可以很容易检测到memory leakage. 通过在程序开始的时候从操作系统分配到一块足够大的内存,在此基础上进行内存管理,还可以有效的防止内存泄漏,并且还可以支持对象复用技术,提高游戏的速度和稳定性。当然,你也可以使用一些memory leakage的检测工具来检查内存使用情况(比如 firefox memory leakage detection tool 或者 Visual leak detector)。

实际上,在游戏程序设计中,很少使用动态的内存分配,大部分的内存都是事先分配好的。即使是链表或者是树这一类的数据结构,也是用数组进行有效的模拟。

================分割线==================

下面说点代码里边应该注意的问题。在相关内存相关的注意事项中,排在第一位的是内存对齐问题。也就是说,一块内存的首地址,必须要能被2,4,8,16,32 或者64整除。 不同的CPU对于这个数字有不同的需要。

针对Intel最新发布的 Pentium Dual Core系列 Xenon系列,以及早些日子的 Pentium 4系列。推荐使用64 Bytes 或者 128 Bytes的内存对齐。 因为在Pentium4 系列用,每当程序要进行内存访问的时候,CPU的一个预处理模块(Prefetch)会事先把内存中的数据读到Level1 cache中,并且每次读入的数据量是 64个 bytes(Pentium Xenon系列是 128 bytes)。 如果没有进行内存对齐, 比如一个int占用4字节,第一个字节在前64bytes中,后3个字节在后64bytes中,那么 CPU在读取这个int的时候就需要多从内存中拿一次数据, 会大大增加代码的运行时间。让我们看下例子:

__declspec(align(64)) int test[128];       // 64字节对齐

int * pInt = (int *)((char *)test + 1);     // 没有对齐的指针
int * pInt2 = test;                               // 对齐的指针

int f1(void)
{
int i, k=0; 
for(i = 0; i < 16; i++) k+=pInt[i];
return k;
}

int f2(void)
{
int i, k=0;
for(i = 0; i < 16; i++) k+=pInt2[i];
return k;
}

对照附件中的 VTune的测试结果(见附件1),我们可以看出非64bytes对齐的运行时间(clockticks值),几乎是对齐内存的运行时间的3 倍。所以在使用动态或者静态内存的时候,最好注意内存的字对齐问题。在Visual Studio .Net中,可以用 __declspec (align(64))对静态变量,数组或者结构进行内存对齐。动态内存分配可以使用_aligned_malloc() 和  _aligned_free().

这些内存对齐的问题,当前的编译器一般都会帮你优化,但是如果要写自己的内存管理函数,就需要分外注意了。


================分割线==================
下面说一下结构数组问题。经常我们会用到结构数组,形式如下:

struct MyStructure{
    int FirstNumber;
    int SecondNumber;
    int ThiredNumber;
    int FourthNumber;
}  StructureArray[100];

这种类型的数据结构,还有另外一种组织的方式,那就是数组结构,形式如下:

struct MyStructure{
    int FirstNumber[100];
    int SecondNumber[100];
    int ThridNumber[100];
    int FourthNumber[100];
} ArrayStructure;

至于这两种形式用哪种好,要根据具体情况来判断。一般来说,如果要对所有结构中的同一个成员进行连续的访问,比如要求100个结构中所有FirstNumber的和,使用第2种形式会快很多。如果要分别求出每个结构所有成员的和,第一种形式要快很多。 

===========求所有结构第一个成员的和==========
// 错误的选择
for(i = 0; i < 100; i++) Sum += StructureArray[i].FirstNumber;
// 正确的选择
for(i = 0; i < 100; i++) Sum += ArrayStructure.FirstNumber[i];

============求每个结构所有成员的和===========
// 错误的选择
for(i = 0; i < 100; i++) 
    Sum =    ArrayStructure.FirstNumber[i] 
              + ArrayStructure.SecondNumber[i]
              + ArrayStructure.ThirdNumber[i]
              + ArrayStructure.FourthNumber[i];
  
// 正确的选择
for(i = 0; i < 100; i++) 
    Sum =    StructureArray[i].FirstNumber
              + StructureArray[i].SecondNumber
              + StructureArray[i].ThirdNumber
              + StructureArray[i].FourthNumber;

我想道理不用多说大家也明白了吧, 具体到程序设计中要根据哪种操作用的多来决定数据的组织方式。

关于内存访问,还有很多很多需要注意的事项,比如aliasing问题,store forward问题等等,建议大家去参考intel关于pentium的文档.

https://i-blog.csdnimg.cn/blog_migrate/3c5f16793a802112931b22d9ecc797b7.gif

 
1. 概述.............................................................................................................................................1 1.1. 自然语言&计算机语言................................................................................................1 1.2. 计算机语言 & C/C++语言..........................................................................................2 1.3. 简单的C/C++程序及其运行方法(环境的使用)................................................2 1.3.1. C/C++程序开发运行环境....................................................................................2 1.3.2. 格式输出函数printf()和格式输入函数scanf()....................................................3 1.4. 习题..............................................................................................................................5 2. 基本的C语言................................................................................................................................6 2.1. C语言中的名词——类型、量值(常量和变量)....................................................6 2.1.1. 整型和整型量值...................................................................................................6 2.1.2. 浮点型和浮点量(常量和变量).......................................................................8 2.1.3. 字符型和字符量(常量和变量).......................................................................9 2.1.4. 字符串常量.........................................................................................................10 2.2. C语言中的动词—运算符,短语-表达式 和和特殊动词性关键字....................11 2.2.1. 赋值运算符和赋值表达式.................................................................................11 2.2.2. 算术运算符和算术表达式.................................................................................12 2.2.3. 逻辑运算符和逻辑表达式.................................................................................13 2.2.4. 关系运算符和关系表达式.................................................................................14 2.2.5. 其它运算符和表达式.........................................................................................15 2.2.6. 不同类型量值的不同运算的混合.....................................................................17 2.3. C语言中的连词及句子——分支、循环和顺序语句..............................................19 2.3.1. if-else:如果-那么...........................................................................................19 2.3.2. switch-case语句..................................................................................................21 2.3.3. ?-:语句................................................................................................................23 2.3.4. for........................................................................................................................23 2.3.5. while....................................................................................................................24 2.3.6. do-while..............................................................................................................25 2.3.7. goto-label-if.........................................................................................................26 2.3.8. break....................................................................................................................27 2.3.9. continue...............................................................................................................28 2.4. C语言中的句子小结..................................................................................................28 2.5. 用C语言写一段话——程序段..................................................................................29 2.6. 用C语言写复杂段落——语句嵌套..........................................................................29 2.6.1. 复杂段落——语句嵌套的含义.........................................................................29 2.6.2. 嵌套语句的示例.................................................................................................30 2.6.3. 复杂段落——语句嵌套的小结.........................................................................33 2.7. 总结............................................................................................................................33 习题...........................................................................................................................................34 3. C语言的横向名词性扩充..........................................................................................................34 3.1. C语言的横向名词性扩充(1):同类变量的集合——数组..................................34
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值