|
|
|
|
移动端

1.14.1 内存的分配形式

《程序员面试笔试真题与解析》本书针对当前各大 IT企业面试笔试中特性与侧重点,精心挑选了 3年以来近百家典型 IT企业的面试笔试真题,这些企业涉及业务包括系统软件、搜索引擎、电子商务、手机 APP、安全关键软件等,面试笔试真题非常具有代表性与参考性。本节为大家介绍内存的分配形式。

作者:猿媛之家来源:机械工业出版社|2017-12-06 18:18

年前最后一场技术盛宴 | 1月27日与京东、日志易技术大咖畅聊智能化运维发展趋势!


1.14 内存分配

1.14.1 内存的分配形式

【真题 197】实现内存复制函数 memcpy。答案:memcpy函数的功能是从源 src所指的内存地址的起始位置开始复制 n个字节到目标 dest所

指的内存地址的起始位置中。它的函数原型为 void *memcpy(void *dest, const void *src, size_t n); 这个函数的参数与返回值的类型都是 void *,在实现的时候,需要把 void*转换成可操作的数据类型来处理。下面首先给出一个简单的实现方式,示例代码如下:

  1. #include <iostream> 
  2. using namespace std;  
  3. void *mymemcpy1(void *dst, const void *src, size_t num)  
  4. {  
  5. if (dst == NULL || src == NULL)  
  6. return NULL;  
  7. const char* psrc = (char *)src;  
  8. char* pdst = (char *)dst;  
  9. while (num-->0)  
  10. *pdst++ = *psrc++;  
  11. return dst;  
  12. }  
  13. int main()  
  14. {  
  15. char src[] = "abc";  
  16. char* dest = new char[4];  
  17. dest = (char*)mymemcpy1(dest, src, 4);  
  18. printf("%s\n", dest);  
  19. delete[] dest;  
  20. return 0;  

程序的运行结果为:

  1. abc 

以上这种实现方式显然没有考虑内存重叠的问题,如果源字符串 src与目标字符串 dst有重叠,上述程序将会有意想不到的结果。例如,把 main函数换成如下的写法:

  1. int main()  
  2. {  
  3. char src[] = "abc";  
  4. char *dest = src + 1;  
  5. dest = (char*)mymemcpy1(dest, src, 4);  
  6. printf("%s\n", dest);  
  7. return 0;  

此时,程序会有意想不到的结果。

如下图所示:

在上图中,源字符串 src与目标字符串 dest存在重叠,当复制第一个字符 ‘a’的时候,源字符串 src的第二个字符也被修改了( ‘b’被修改成了 ‘a’),因此,当复制第二个字符的时候已经出错了(本来应该复制的是字符‘b’,但实际上却复制了字符 ‘a’)。更严重的问题是在复制第三个字符的时候把源字符串的结束符‘\0’也给替换掉了,因此,导致在复制第四个字符的时候,应该是复制一个结束符 ‘\0’,但实际上却复制了一个字符‘a’,这就导致复制后, dest字符是无法确定的,因为无法确定下一个字符 ‘\0’出现的位置。

但是,在调用系统函数 memcpy的时候,程序的输出结果依然是 abc,说明上面这个程序还不完整,主要是没有考虑内存重叠的问题,处理内存重叠的主要思路如下:

1)当源内存的首地址大于目标内存的首地址时,从源内存的首地址开始复制。

2)当源内存的首地址小于目标内存的首地址时,从源内存的首地址加待复制字节的长度的地址开始逆序复制。下面给出第二种情况的实现示意图。

从图中可以看出,在这种情况下,如果从字符串的结尾开始倒着复制就能得到正确的结果。实现代码如下:

  1. #include <iostream> 
  2. using namespace std;  
  3. void * mymemcpy2(void *dst, const void *src, size_t num)  
  4. {  
  5. if (dst == NULL || src == NULL)  
  6. return NULL;  
  7. char *pdst = (char *)(dst);  
  8. const char *psrc = (char *)(src);  
  9. //dst 与src 有重叠且dst 指向src 中的元素,因此,需要对src 从后向前复制  
  10. if (pdst > psrc && pdst < psrc + num)  
  11. {  
  12. for (size_t i = num - 1; i != -1; i--)  
  13. {  
  14. pdst[i] = psrc[i];  
  15. }  
  16. }//src 指向dst 中的元素或者没有重叠,因此,对src 从前向后复制  
  17. else  
  18. {  
  19. for (size_t i = 0; i < num; i++)  
  20. {  
  21. pdst[i] = psrc[i];  
  22. }  
  23. }  
  24. return dst;  
  25. }  
  26. int main()  
  27. {  
  28. char src[] = "abc";  
  29. char *dest = src + 1;  
  30. dest = (char*)mymemcpy2(dest, src, 4);  
  31. printf("%s\n", dest);  
  32. return 0;  

程序的运行结果为:

  1. abc 

对于这个函数的实现,除了要保证代码的正确性,还需要特别注意以下几个方面的内容:

1)对异常进行判断:判断 src与 dst是否为空指针。

2)src指针要用 const修饰,以避免无意中修改 src。

3)在实现的时候,需要把 void*转换成能进行操作的数据类型,例如 char*。

4)函数为什么还需要返回值?这样可以支持链式表达。

5)需要特别考虑指针重叠的情况。

【真题 198】二进制地址为 011011110000,大小为 4(十进制数)和 16(十进制数)块的伙伴地址

分别为()和()。

答案:011011110100,011100000000。

伙伴块是指块大小相同,且地址连续的两个内存空间。

如果块大小是 4,地址为 011011110000起始的块应该就是 011011110000~011011110011,那么其

伙伴地址块是 011011110100~011011110111。因此,伙伴地址为 011011110100。同理,大小为 16的伙伴地址应该为 011100000000(011011110000+10000=011100000000)。

【真题 199】定义输出 char aR[4]={0xCC},那么 aR[3]的值是()。

A.0xCC   B.204 C.0D.不确定答案:C。在 C/C++语言中,当对局部数组初始化的时候,如果初始化值的个数少于元素个数,那么数组前面的元素按照初始化的顺序进行初始化,数组后面部分的值会被赋值为 0。因此答案为 C。

喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书频道活动讨论群:365934973

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Visual C++编程从基础到实践

Visual C++ 6.0是Microsoft公司的Visual Studio开发组件中最强大的编程工具,利用它可以开发出高性能的应用程序。本书由浅入深,从基础到实...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊