测试题目
致无法执行原子性操作,进而引发竞态条件。
测试题目
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的原始值是错误的。
理由:
- 源字符串肯定是已知的,返回它没有意义
- 不能支持形如第二例的表达式
- 把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、说一说进程的地址空间分布
参考答案:

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