C++面试八股文:如何在堆上和栈上分配一块内存?
某日二师兄参加XXX科技公司的C++工程师开发岗位6面:
面试官: 如何在堆上申请一块内存?
二师兄:常用的方法有malloc,new等。
面试官:两者有什么区别?
二师兄:malloc是向操作系统申请一块内存,这块内存没有经过初始化,通常需要使用memset手动初始化。而new一般伴随三个动作,向操作系统申请一块内存,并执行类型的默认构造函数,然后返回类的指针。
面试官:嗯,那你知道calloc和realloc吗?
二师兄:calloc比malloc多做了一步,就是把申请的内存初始化成0。而realloc则可以改变当前指针所指向的内存块的大小。
面试官:好的。那么你知道这些api/操作符失败会发生什么吗?
二师兄:malloc/calloc/realloc失败会返回NULL,而new失败则会抛出异常。
面试官:有没有让new失败不抛出异常的方法?
二师兄:好像有,但是我不记得了。。。
面试官:没关系。。。我们都知道new和delete成对出现,new[]和delete[]也是成对出现,那么我想问,如果使用new[]创建的对象用delete释放了会发生什么?为什么?
二师兄:额。。。内存泄漏?对,会发生内存泄漏。因为内存没有被释放。
面试官:好的。我们都知道C++中的内存管理是一个比较麻烦的事情,现在有个需求,需要在程序中记录主动申请的内存和主动释放的内存,以确保没有发生内存泄漏。有什么好的方法吗?
二师兄:可以重载new和delete运算符。
面试官:如何重载new和delete运算符?
二师兄:我得查一下资料,这个重载用的很少。。。
面试官:(笑)好吧,最后一个问题,咱们上面一直在讨论堆中的内存的分配和释放,请问一下,如果在栈上分配一块固定的内存?栈中的内存如何释放?
二师兄:额。。。(思考)使用 char[size] ? 应该不需要手动释放。
面试官:好的,回去等通知吧。
对于二师兄的表现,小伙伴们能给打几分呢?我们先看看二师兄在面试中表现不太好的地方:
面试官:有没有让new失败不抛出异常的方法?
在C++中我们可以使用以下方法使得new运算符不抛出异常,
int* p = new (std::nothrow) int(42);
if(p == nullptr)
{
//分配失败
}
这个特性需要C++11支持。
再看下一个问题:
如果使用new[]创建的对象用delete释放了会发生什么?
一定会发生内存泄漏吗?答案是,不一定。这取决于类型T。我们先看第一种情况:
class Foo
{
public:
Foo():num_(42){}
private:
int num_;
};
Foo* pf = new Foo[1024];
delete pf;
当类型T没有管理资源时,delete pf会把整个申请的1024个Foo所占用的内存全部归还给操作系统,此时并没有内存泄漏。再看下一种情况:
class Foo
{
public:
Foo():num_(new int(42)){}
~Foo(){delete num_;}
private:
int* num_;
};
Foo* pf = new Foo[1024];
delete pf;
此时会造成内存泄漏,原因很简单。在执行delete[]时,首先逆序执行每个元素的析构函数,然后再把整块内存归还给操作系统。而delete只会把内存还给操作系统,没有执行析构函数。当类没有资源需要管理时,执行与不执行析构函数都无关紧要,但是当类中需要管理资源时,析构函数的执行就至关重要了。
如何重载new和delete运算符?
#include <iostream>
#include <cstdlib>
#include <map>
struct MemoryInfo {
size_t size;
const char* file;
int line;
};
std::map<void*, MemoryInfo> memoryMap;
void* operator new(size_t size, const char* file, int line) {
void* ptr = std::malloc(size);
memoryMap[ptr] = {size, file, line};
return ptr;
}
void operator delete(void* ptr) noexcept {
auto it = memoryMap.find(ptr);
if (it != memoryMap.end()) {
std::free(ptr);
memoryMap.erase(it);
}
}
#define new new(__FILE__, __LINE__)
int main() {
int* p = new int(42);
for (const auto& [ptr, info] : memoryMap) {
std::cout << "Memory allocated at " << ptr << " with size " << info.size
<< " in file " << info.file << " at line " << info.line << std::endl;
}
delete p;
for (const auto& [ptr, info] : memoryMap) {
std::cout << "Memory allocated at " << ptr << " with size " << info.size
<< " in file " << info.file << " at line " << info.line << std::endl;
}
return 0;
}
最后一个问题:
如果在栈上分配一块固定的内存?栈中的内存如何释放?
使用alloca,虽然简单,但是很多人可能都没有接触过:
int* p = (int*)alloca(4);
*p = 42;
栈上申请的内存不需要手动释放。注意,如果栈溢出,alloca的行为时未定义的。
好了,今日份面试到这里就结束了,小伙伴们,对于今天二师兄的面试,能打几分呢?
关注我,带你21天“精通”C++!(狗头)
C++面试八股文:如何在堆上和栈上分配一块内存?的更多相关文章
- go语言的局部变量在堆上还是栈上?
在讨论之前,先看如下代码: type treeNode struct { value int left, right *treeNode } func createNode(value int) *t ...
- C++如何限制对象在堆上或栈上生成
1,限制类的对象只能生成在栈上 将 operator new 各种原型设为私有 #include <iostream> class OnlyOnStack { public: OnlyOn ...
- 堆(heap)和栈(stack)几点认识
堆(heap)和栈(stack)主要的区别由以下几点:1.管理方式不同:2.空间大小不同:3.产生碎片不同:4.生长方向不同:5.分配归属不同:6.分配效率不同:7.存取效率不同:管理方式:对于栈来讲 ...
- Heap(堆)和stack(栈)有的区别是什么。
java的内存分为两类,一类是栈内存,一类是堆内存.栈内存是指程序进入一个方法时,会为这个方法单独分配一块私属存储空间,用于存储这个方法内部的局部变量,当这个方法结束时,分配给这个方法的栈会释放,这个 ...
- 最好的方式是用VirtualAlloc分配虚拟内存,它既不是在堆也不是在栈,而是直接在进程的地址空间中保留一块内存
申请效率的比较 栈:由系统自动分配,速度较快.但程序员是无法控制的. 堆:是由new分配的内存,最好的方式是用VirtualAlloc分配虚拟内存,它既不是在堆也不是在栈,而是直接在进程的地址空间中保 ...
- 每日一问2:堆(heap)和栈(stack)的区别
因为这里没有明确指出堆是指数据结构还是存储方式,所以两个尝试都回答一下. 一.堆和栈作为数据结构 1.堆(heap),也叫做优先队列(priority queue),队列中允许的操作是先进先出(FIF ...
- 《面试八股文》之 JVM 20卷
微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是 moon. <面试八股 ...
- 《面试八股文》之kafka21卷
微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是moon,最新一篇面试八股文系 ...
- 《面试八股文》之 Redis 16卷
微信公众号:moon聊技术 关注选择" 星标 ", 重磅干货,第一 时间送达! [如果你觉得文章对你有帮助,欢迎关注,在看,点赞,转发] 大家好,我是 moon. redis 作为 ...
- 一天吃透JVM面试八股文
什么是JVM? JVM,全称Java Virtual Machine(Java虚拟机),是通过在实际的计算机上仿真模拟各种计算机功能来实现的.由一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一 ...
随机推荐
- CSAPP-Shell Lab
提供的工具: parseline:获取参数列表char **argv,返回是否为后台运行命令(true). clearjob:清除job结构. initjobs:初始化jobs链表. maxjid:返 ...
- odoo 开发入门教程系列-计算的字段和变更(Computed Fields And Onchanges)
计算的字段和变更(Computed Fields And Onchanges) 模型之间的关系是任何Odoo模块的关键组成部分.它们对于任何业务案例的建模都是必要的.然而,我们可能需要给定模型中字段之 ...
- 开发轻量级REST API样板 基于Node.js、MongoDB 通过Mongoose驱动
ZY.Node.Mongodb https://gitee.com/Z568_568/node.mongodb.git https://github.com/ZHYI-source/ZY.Node.M ...
- [网络/Java EE/Web]Tomcat/Nginx中配置全局的安全响应头(header)——X-Frame-Options / X-XSS-Protection / X-Content-Options
Step1 配置Tomcat step1.1 查看是否已配置目标的HTTP网络安全头 方式1 – Tomcat / conf/web.xml cat /opt/myTomcat/conf/web.xm ...
- python数据类型、变量以及编码和字符串、格式化
1.数据类型包括整型.浮点型.字符串.布尔型. 整数如果位数太多可以用_隔开,浮点数可以用科学记数法表示,字符串要用单引号或者双引号括起来,布尔型的值只能为True和False 2.变量可以由数字.字 ...
- 一文了解MySQL中的多版本并发控制
作者:京东零售 李泽阳 最近在阅读<认知觉醒>这本书,里面有句话非常打动我:通过自己的语言,用最简单的话把一件事情讲清楚,最好让外行人也能听懂. 也许这就是大道至简,只是我们习惯了烦琐和 ...
- RxJava--create源码分析
使用示例: Observable.create(new ObservableOnSubscribe<String>() { @Override public void subscribe( ...
- Kubernetes入门实践(Pods)
为了解决多应用联合运行的问题,同时还要不破坏容器的隔离,就要再对多个容器进行打包.Pod就是对容器的打包,里面的容器可以看成是一个整体,总是能一起调度.一起运行,绝不会出现分离的情况,而Pod属于Ku ...
- python-SSTI模板注入
一.python_SSTI模板注入介绍 ssti漏洞成因 ssti服务端模板注入,ssti主要为python的一些框架 jinja2 mako tornado django,PHP框架smarty t ...
- vue2路由导航守卫(钩子函数)
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html#%E5%85%A8%E5%B1%80%E5%89%8D%E7%BD% ...