大神洗礼第二讲——内存对齐相关

Author:bakari       Date:2012.10.22

主要内容:内存对齐相关

1、 温故而知新:防御性编程的应用

防御性编程的详细讲解可见我的另一篇文章:https://cloud.tencent.com/developer/article/1017817

string函数原型的详解可见我的“string函数系列之”的文章:https://cloud.tencent.com/developer/article/1017805

< 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 >、数据对齐

这一部分在我的另外一篇文章也有详细的讲解:https://cloud.tencent.com/developer/article/1017819

i、 现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。

ii、 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

iii、 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

4、 复杂声明:

所用方法:右左法则

对于右左法则的详细讲解在我的另外一篇文章也有详细的讲解:https://cloud.tencent.com/developer/article/1017820

int (*p)[10]; p是一个指向10个int型元素的数组指针。

int *(*func())() ; func是一个指向无参函数的函数指针,函数返回值也是一个指向另外一个函数的函数指针,该函数的返回值是int型变量。

char (*(*x[3])())[5]; 非法,原因是函数的返回值是一个具有5个int元素数组。C语言规定不能返回数组。

char (*(*x())[])(); 非法,原因是数组的元素是函数。这样不能保证函数的类型一致。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小樱的经验随笔

记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)

这一串代码描述是这样子,我们要绕过A-Za-z0-9这些常规数字、字母字符串的传参,将非字母、数字的字符经过各种变换,最后能构造出 a-z 中任意一个字符,并且...

21020
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-11总结Eclipse使用,API,Object类

1:Eclipse的概述使用(掌握) 1:Eclipse的安装 2:用Eclipse写一个HelloWorld案例,最终在控制台输出你的名字 A:创建项目 ...

36660
来自专栏yang0range

Java的面试基础题(二)

1)特点:存储对象;长度可变;存储对象的类型可不同 2)Collection (1)List:有序的;元素可重复,有索引 (add(index, elem...

19620
来自专栏xx_Cc的学习总结专栏

iOS底层原理总结 - 探寻block的本质(一)

29140
来自专栏PHP实战技术

PHP面试常考易错题之变量作用域解析

PHP面试中变量作用域是面试中常常出现的问题,也是必考的问题,下面就给大姐讲解一下在面试中注意的点:

14040
来自专栏编程

Python基础—让你规范Python语言的使用

群内不定时分享干货,包括最新的python企业案例学习资料和零基础入门教程,欢迎初学和进阶中的小伙伴入群学习交流 ? Lint 定义: pylint是一个在Py...

22480
来自专栏数据结构与算法

洛谷P1966 火柴排队(逆序对)

首先要保证权值最小,不难想到一种贪心策略,即把两个序列中rank相同的数放到同一个位置

14710
来自专栏wOw的Android小站

[Objective-C] 常量和枚举

#define宏定义 #define是一条预编译指令, 编译器在编译阶段前期会将所有使用到宏的地方简单地进行替换.

9820
来自专栏Python小屋

Python花式编程案例集锦(5)

问题描述: 输入三个序列,例如:[0.9,0.5,0.7],[0.4,0.6,0.3],[0.5,0.2,0.4],输出三个序列中各取一个相乘后最大的值,以及组...

29950
来自专栏潇涧技术专栏

Python Basics

1.使用glob模块可以用通配符的方式搜索某个目录下的特定文件,返回结果是一个list

10620

扫码关注云+社区

领取腾讯云代金券