关于c++: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++!(狗头)

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据