← 返回目录

测试题目

第 21 / 81 卷日期:

致无法执行原子性操作,进而引发竞态条件。

测试题目

1、以下为WindowsNT 32位C++程序,请计算下面sizeof的值

char str[] = "hello";

char* p = str;

int n = 10;

// 请计算 sizeof(str) = ?

sizeof(p) = ?

sizeof(n) = ?

void Func(char str[100])

{ // 请计算

sizeof(str) = ?

} void* p = malloc(100);

// 请计算 sizeof(p) = ?

参考答案:

sizeof(str) = 6;

sizeof()计算的是数组的所占内存的大小包括末尾的 '\0'

sizeof(p) = 4;

p为指针变量,32位系统下大小为 4 bytes

sizeof(n) = 4;

n 是整型变量,占用内存空间4个字节

void Func(char str[100])

{ sizeof(str) = 4;

}

函数的参数为字符数组名,即数组首元素的地址,大小为指针的大小

p指向malloc分配的大小为100 byte的内存的起始地址,sizeof(p)为指针的大小,而不是它指向内存的大小

2、分析运行下面的Test函数会有什么样的结果

void GetMemory1(char* p)

{ p = (char*)malloc(100);

}

void Test1(void)

{ char* str = NULL;

GetMemory1(str);

strcpy(str, "hello world");

printf(str);

}

char *GetMemory2(void)

{ char p[] = "hello world";

return p;

}

void Test2(void)

{ char *str = NULL;

str = GetMemory2();

printf(str);

}

void GetMemory3(char** p, int num)

{ *p = (char*)malloc(num);

}

void Test3(void)

{ char* str = NULL;

GetMemory3(&str, 100);

strcpy(str, "hello");

printf(str);

}

void Test4(void)

if(str != NULL) { strcpy(str, "world");

cout << str << endl;

} }

参考答案:

Test1(void):

程序崩溃。因为GetMemory1并不能传递动态内存,Test1函数中的 str一直都是NULL。strcpy(str, "hello world")

将使程序奔溃

Test2(void):

可能是乱码。因为GetMemory2返回的是指向“栈内存”的指针,该指针的地址不是NULL,使其原现的内容已经被清除,新内容不可知。

Test3(void):

能够输出hello, 内存泄露。GetMemory3申请的内存没有释放

Test4(void):

篡改动态内存区的内容,后果难以预料。非常危险。因为 free(str);之后,str成为野指针,if(str != NULL)语句不起作用。

3、实现内存拷贝函数

char* strcpy(char* strDest, const char* strSrc);

参考答案:(函数实现)

char* strcpy(char *dst,const char *src) {// [1] assert(dst != NULL && src != NULL);// [2]

char *ret = dst;// [3] while ((*dst++=*src++)!='\0');// [4]

return ret;

}

[1] const修饰:

(1)源字符串参数用const修饰,防止修改源字符串。

[2] 空指针检查:

(1)不检查指针的有效性,说明答题者不注重代码的健壮性。

(2)检查指针的有效性时使用 assert(!dst && !src);

出来。

[3] 返回目标地址:

(1)忘记保存原始的strdst值。

[4] '\0':

(1)循环写成 while (*dst++=*src++); 明显是错误的。

(2)循环写成 while (*src!='\0') *dst++ = *src++;

循环体结束后,dst字符串的末尾没有正确地加上'\0'。

(3)为什么要返回char *?

返回dst的原始值使函数能够支持链式表达式

链式表达式的形式如:

int l=strlen(strcpy(strA,strB));

又如:

char * strA=strcpy(new char[10],strB);

返回strSrc的原始值是错误的。

理由:

  1. 源字符串肯定是已知的,返回它没有意义
  2. 不能支持形如第二例的表达式
  3. 把const char *作为char *返回,类型不符,编译报错

4、假如考虑dst和src内存重叠的情况,strcpy该怎么实现

char s[10]="hello";

strcpy(s, s+1);

// 应返回 ello

strcpy(s+1, s);

// 应返回 hhello 但实际会报错 // 因为dst与src重叠了,把'\0'覆盖了

所谓重叠,就是src未处理的部分已经被dst给覆盖了,只有一种情况:src<=dst<=src+strlen(src)

C函数 memcpy 自带内存重叠检测功能,下面给出 memcpy 的实现my_memcpy

char * strcpy(char *dst,const char *src)

{

return ret;

}

/* my_memcpy的实现如下 */ char *my_memcpy(char *dst, const char* src, int cnt)

{ assert(dst != NULL && src != NULL);

char *ret = dst;

/*内存重叠,从高地址开始复制*/ if (dst >= src && dst <= src+cnt-1)

{ dst = dst+cnt-1;

src = src+cnt-1;

while (cnt--)

{ *dst-- = *src--;

} }

else//正常情况,从低地址开始复制 {

while (cnt--)

{ *dst++ = *src++;

} }

return ret;

}

5、按照下面描述的要求写程序

已知String的原型为:

class String {

public:

String(const char *str = NULL);

String(const String &other);

~ String(void);

String & operate =(const String &other);

private:

char *m_data;

};

请编写上述四个函数

参考答案:

{ if(str==NULL)

{ m_data = new char[1];//对空字符串自动申请存放结束标志'\0'

*m_data = '\0';

} else

{ int length = strlen(str);

m_data = new char[length + 1];

strcpy(m_data, str);

} }

// 析构函数 String::~String(void)

{ delete [] m_data; // 或delete m_data;

}

//拷贝构造函数 String::String(const String &other)

{ int length = strlen(other.m_data);

m_data = new char[length + 1];

strcpy(m_data, other.m_data);

}

//赋值函数 String &String::operate =(const String &other)

{ if(this == &other)

{ return *this; // 检查自赋值

} delete []m_data; // 释放原有的内存资源

int length = strlen(other.m_data);

m_data = new char[length + 1]; //对m_data加NULL判断 strcpy(m_data, other.m_data);

return *this; //返回本对象的引用 }

6、说一说进程的地址空间分布

参考答案:

图 29-1
图 29-1

如上图,从高地址到低地址,一个程序由命令行参数和环境变量、栈、文件映射区、堆、BSS段、数据段、代码段组成。

(1)命令行参数和环境变量

命令行参数是指从命令行执行程序的时候,给程序的参数。

(2)栈区

存储局部变量、函数参数值。栈从高地址向低地址增长。是一块连续的空间。

(3)文件映射区

位于堆和栈之间。

(4)堆区

动态申请内存用。堆从低地址向高地址增长。

(5)BSS 段

存放程序中未初始化的全局变量和静态变量的一块内存区域。

(6)数据段

存放程序中已初始化的全局变量和静态变量的一块内存区域。

(7)代码段

存放程序执行代码的一块内存区域。只读,代码段的头部还会包含一些只读的常数变量。

7、说一说C与C++的内存分配方式

(1)从静态存储区域分配

内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,如全局变量,static变量。

(2)在栈上创建

在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

全站分卷索引 · 81 卷
  1. 1代码随想录知识星球精华(最强八股文)第五版(C++篇)
  2. 2数据类型
  3. 3无符号类型
  4. 4static关键字的作用
  5. 5const 关键字的作用
  6. 6const和define的区别
  7. 7constexpr函数:
  8. 8constexpr的好处
  9. 9前置++与后置++
  10. 10常量指针和指针常量的区别
  11. 11函数指针和指针函数的区别
  12. 12静态局部变量\全局变量\局部变量的区别和使用场景
  13. 13C++强制类型转换
  14. 143. reinterpret_cast
  15. 15内存泄漏?如何避免?
  16. 16什么是智能指针?有哪些种类?
  17. 17new 和 malloc 有什么区别?
  18. 18什么是野指针,怎么产生的,如何避免
  19. 19野指针和悬浮指针的区别
  20. 20内存对齐是什么?为什么需要考虑内存对齐?
  21. 21测试题目
  22. 22计算机中的乱序执行
  23. 23副作用
  24. 24future库
  25. 25字符串操作函数
  26. 26C++ 面向对象
  27. 272. 封装
  28. 28什么是多重继承?
  29. 29简述一下 C++ 的重载和重写,以及它们的区别
  30. 30c++的多态如何实现
  31. 31成员函数/成员变量/静态成员函数/静态成员变量的区别
  32. 32什么是构造函数和析构函数?
  33. 33C++构造函数有几种,分别什么作用
  34. 34什么是虚函数和虚函数表?
  35. 35虚函数和纯虚函数的区别
  36. 36什么是抽象类和纯虚函数?
  37. 37说说为什么要虚析构,为什么不能虚构造
  38. 38哪些函数不能被声明为虚函数?
  39. 39运算符重载
  40. 40什么是STL,包含哪些组件
  41. 41空间配置器
  42. 42常见的STL容器
  43. 43vector容器实现与扩充
  44. 442. 扩容过程
  45. 453. vector源码
  46. 46list(链表)
  47. 47vector和list的区别
  48. 48stack && queue
  49. 49heap && priority_queue
  50. 50map && set的区别和实现原理
  51. 51map && unordered_map的区别
  52. 52push_back 和 emplace_back 的区别
  53. 53vector的实现原理
  54. 54迭代器有什么作用?什么时候迭代器会失效
  55. 55C++ 新特性
  56. 562. unique_ptr
  57. 57右值引用
  58. 58nullptr
  59. 59列表初始化
  60. 60并发
  61. 612. lock_guard
  62. 62《Effective STL》
  63. 63条款 04
  64. 64条款 07
  65. 65条款 11
  66. 66条款 12
  67. 67条款 16
  68. 68条款 19
  69. 69条款 23
  70. 70条款 27
  71. 71条款 30
  72. 72条款 35
  73. 73条款 41
  74. 74条款 44
  75. 75条款 51
  76. 76条款 07
  77. 77条款 08
  78. 78条款 10
  79. 79条款 12
  80. 80条款 35
  81. 81条款 45