Authorbakari Date:2012.10.22
主要内容内存对齐相关
1、 温故而知新防御性编程的应用
防御性编程的详细讲解可见我的另一篇文章http://www.cnblogs.com/bakari/archive/2012/08/27/2658215.html
string函数原型的详解可见我的“string函数系列之”的文章http://www.cnblogs.com/bakari/archive/2012/08/09/string%E5%BA%93%E5%87%BD%E6%95%B0.html
< 1 >、strlen函数无局部变量递归调用
1 size_t myStrlen(const char * Str) //递归调用 2 { 3 assert(NULL != Str); 4 if ('\0' == * Str) 5 return 0; 6 else 7 return (myStrlen(Str + 1) + 1); 8 } 9
< 2 >、strlen函数含局部变量
1 size_t myStrlen(const char * Str) //常规方式返回长度 2 { 3 assert(NULL != Str); 4 size_t nLen = 0; 5 while ('\0' != * Str ++) 6 nLen ++; 7 8 return nLen; 9 } 10
1 size_t myStrlen(const char * Str) //常规方式返回地址的差值 2 { 3 assert(NULL != Str); 4 const char * tempStr = Str; 5 while('\0' != * ( ++ Str ) ); //note! 6 7 return (Str - tempStr); 8 } 9
< 3 >、strcpy函数原型
1 void myStrcpy(char * strDestination, const char * strSource) //strcpy() 2 { 3 assert(NULL != strDestination); 4 assert(NULL != strSource); 5 while ('\0' != * strSource) 6 * strDestination ++ = * strSource ++; 7 8 * strDestination = '\0'; //key! 9 } 10
<4>、memcpy函数原型
1 void * myMemcpy(void * strDestination, const void * strSource, size_t n) //memcpy() 2 { 3 assert(NULL != strDestination); 4 assert(NULL != strSource); 5 char * strD = (char *)strDestination; 6 char * strS = (char *)strSource; 7 8 while ( '\0' != * (char *) strSource && n -- ) 9 * strD ++ = * strS ++; 10 * strD = '\0'; 11 return strDestination; 12 } 13
2、不同数据在内存中的存储
这一部分在我的另外一篇文章有详细的讲解http://www.cnblogs.com/bakari/archive/2012/08/05/2623637.html
全局/静态数据区存储全局变量、全局静态变量、局部静态变量。
常量数据区存储常量字符串字符串常量存储的区域不可修改。
代码区存储程序代码。 栈区存储自动变量或局部变量函数参数等。
堆区是由用户程序控制的存储区存储动态产生的数据通过new或malloc获得的内存是堆得内存。
这是分大类细分的话还有初始化数据区和非初始化数据区等等。
见下面的图
下面是linux进程内存布局4G的内存分配1G给内核使用用户地址空间又细分成上面图示的分布。
如上图内存区的堆和栈是动态增长和缩减的堆是从未初始化数据区开始向上动态增长增长过程中虚拟地址值变大而栈则从高地址向下端动态增长虚拟地址值变小。 DATA初始化数据区和BSS未初始化数据区有一些值得注意的问题
DATA区存放着在编译期就已确定的变量这些初始变量值保存在最终生成的二进制文件中并在程序运行时原封不动地将这些值映射到进程的初始化数据区。也就是二进制文件所存储变量的空间大小和进程虚拟地址空间一样大。
BSS区存储那些未被初始化的变量在程序启动时这些变量都被初始化为0和DATA区不同在最终生成的二进制文件中不是存储着每一个变量的大小值而是用一个记录值来记录空间的总大小。E.g有150KB的未初始化数据的大小用一个4字节的空间来记录值记为150KB * 1024但在进程虚拟地址空间中必须开辟出150KB的大小的空间来记录每一个值值为0。
3、 不同数据在内存中的存储
< 1 >、自定义数据类型
简单说就是指由若干标准数据类型组成的一种复合类型也叫记录类型。
定义方式 Type 自定义类型名
元素名[下标] As 类型名
……
元素名[下标] As 类型名
End Type
元素名表示自定义类型中的一个成员。
下标表示是数组。
类型名为标准类型。
注意:
<1> 自定义类型一般在标准模块.bas中定义默认是Public
<2> 自定义类型中的元素可以是字符串,但应是定长字符串
<3> 不可把自定义类型名与该类型的变量名混淆
<4> 注意自定义类型变量与数组的差别它们都由若干元素组成前者的元素代表不同性质、不同类型的数据 像struct union enum就是三种比较常用的自定义数据类型。
< 2 >、数据对齐
这一部分在我的另外一篇文章也有详细的讲解http://www.cnblogs.com/bakari/archive/2012/08/27/2658956.html
i、 现代计算机中内存空间都是按照byte划分的从理论上讲似乎对任何类型的变量的访问可以从任何地址开始但实际情况是在访问特定变量的时候经常在特定的内存地址访问这就需要各类型数据按照一定的规则在空间上排列而不是顺序的一个接一个的排放这就是对齐。
ii、 平台原因(移植原因)不是所有的硬件平台都能访问任意地址上的任意数据的某些硬件平台只能在某些地址处取某些特定类型的数据否则抛出硬件异常。
iii、 性能原因数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于为了访问未对齐的内存处理器需要作两次内存访问而对齐的内存访问仅需要一次访问。
4、 复杂声明
所用方法右左法则
对于右左法则的详细讲解在我的另外一篇文章也有详细的讲解http://www.cnblogs.com/bakari/archive/2012/08/28/2659889.html
int (*p)[10]; p是一个指向10个int型元素的数组指针。
int *(*func())() ; func是一个指向无参函数的函数指针函数返回值也是一个指向另外一个函数的函数指针该函数的返回值是int型变量。
char (*(*x[3])())[5]; 非法原因是函数的返回值是一个具有5个int元素数组。C语言规定不能返回数组。
char (*(*x())[])(); 非法原因是数组的元素是函数。这样不能保证函数的类型一致。