C++ 内存映射

这篇具有很好参考价值的文章主要介绍了C++ 内存映射。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

  在一个版图集成与分析工具的项目中看到了 一种 C++ 内存映射的用法,觉得非常强,分享一下大致的概念。

  随着制造工艺的不断进步,芯片版图文件越来越大,对于一些很大的文件,可能光是打开就需要几个小时,是芯片设计开发人员的一大痛点。

  于是我们领导想出了一个解决这个问题的办法:加载文件慢,是因为要在加载文件的过程中进行 parse, 把版图文件格式转换成自定义的数据格式,才能在工具中进行各种处理。采用内存映射的办法,只需要在第一次加载的时候做一次 parse,之后把自己管理的内存中的内容直接以 bit 的形式保存下来,下次加载的时候直接通过 reinterpret_cast 强制类型转换,把特定的 bit 位转换为对应的指针,就可以恢复 parse 之后的数据了,不需要再一次进行 parse,大大降低了打开文件的时间。

  使用分页方式管理程序的内存可以提高内存访问效率和节约存储空间。你可以将程序的内存划分为固定大小的页面,并按需保存或加载这些页面。通过指针类型转换,可以在加载时快速恢复页面的内存结构。

c++ 中创建对象

  在 C++ 中,一旦对象被创建,其大小通常是固定的,不会随着对象的使用而改变。对象的大小是在编译时确定的,它取决于类的成员变量和对齐规则。

  当你创建一个对象时,编译器会根据类的定义计算出对象所需的内存大小,并为对象分配足够的内存空间。这个大小在对象的生命周期中保持不变,除非你对对象进行了显式的重新分配或重新定义。

  需要注意的是,如果类中包含指针成员或动态分配的资源(如堆内存),对象的大小不会包括指针所指向的内存或动态分配的资源的大小。指针本身的大小是固定的,它保存了地址值而不是实际的数据。

  此外,编译器可能会对对象进行对齐以满足特定的对齐规则,这可能导致对象的大小增加。对齐规则可以保证访问对象的效率,并避免因对齐不当而引起的性能问题。

  总之,在一般情况下,一旦对象被创建,其大小是固定的,并且不会随着对象的使用而改变。对象的大小取决于类的成员变量和对齐规则,并且不包括指针所指向的内存或动态分配的资源的大小。

通过对象的首地址访问对象

  this 指针指向的地址是当前对象的内存地址。每个对象在内存中都有其自己的存储空间,包含了类的成员变量的值。this 指针指向这个存储空间的起始地址,使得成员函数可以通过 this 指针来访问对象的成员。

  只要知道对象存放的首地址,就可以强制转换成对象的指针,然后通过指针访问对象

例:

class A {
public:
    A(string s1) : _s1(s1) {}

    long long   show() { return reinterpret_cast<long long>(this); }
    void        s1() { cout << "s1: " << _s1 << endl; }

private:
    string      _s1;
};

int main() {
    A a("I'm a");

    long long addr = a.show();
    cout << "address: " << addr << endl;

    A* b = reinterpret_cast<A*>(addr); // 根据首地址获取对象指针
    b->s1();

    return 0;
}

/*********输出********************/
address: 247805771088
s1: I'm a

c++ 自己管理内存,创建对象

自己申请内存,在申请的内存上创建对象;通过对象的首地址获取对象

例:

class A {
public:
    A(string s1) : _s1(s1) {}

    long long       show() { return reinterpret_cast<long long>(this); }
    void            s1() { cout << "s1: " << _s1 << endl; }

private:
    string          _s1;
};

int main() {
    unsigned pagesize = 1 << 13; // 8k
    char* page = new char[pagesize]; // 申请了 8k 的内存;
    cout << "page: " << (void*)page << endl;

    A* a = new (page) A("I'm a"); // 在申请的内存上创建对象
    cout << "a: " << a << endl;
    A* b = reinterpret_cast<A*>(page); //直接通过地址获取对象
    b->s1();

    size_t shift = sizeof(A);
    page += shift; // 计算出对象 a 占用的内存

    A* c = new (page) A("I'm c"); // 在未被占用的内存上创建 c 对象
    A* d = reinterpret_cast<A*>(page); // 直接通过地址获取对象
    d->s1();

    return 0;
}
/********* 输出 **************/
page: 0x159ac205c50
a: 0x159ac205c50
s1: I'm a
s1: I'm c

把内存保存成文件再加载

上面讲过:对象的大小不会包括指针所指向的内存或动态分配的资源的大小,所以保存成文件的对象里不能有动态分配的内存(其实可以有,只是不能直接使用,要先初始化,申请内存,不然就会访问到不合法的内存),所以不能用 string,这里用 int 做成员变量举例。用有虚函数的类也比较麻烦,子类的大小难以计算。

例:

class A {
public:
    A(int num) : _num(num) {}

    void        show() { cout << "num: " << _num << endl; }

private:
    int             _num;
};

void saveMemoryToFile(const char* memory, size_t size, const string& filename) {
    ofstream file(filename, ios::binary);
    if (!file) {
        cerr << "Failed to open file for writing: " << filename << endl;
        return;
    }

    file.write(memory, size);
    file.close();
}

void readMemoryFromFile(char* memory, const string& filename) {
    ifstream file(filename, ios::binary);
    if (!file) {
        cerr << "Failed to open file for reading: " << filename << endl;
        return;
    }

    // 获取文件大小
    file.seekg(0, ios::end);
    size_t fileSize = file.tellg();
    file.seekg(0, ios::beg);

    // 读取文件内容
    file.read(memory, fileSize);
    file.close();
}

// 打印一段内存的内容,二进制表示,用于测试
void printMemoryBinary(const char* memory, std::size_t size) {
    for (std::size_t i = 0; i < size; ++i) {
        std::bitset<8> bits(memory[i]);
        std::cout << bits << ' ';
    }
    std::cout << std::endl;
}

int main() {
    unsigned pagesize = 1 << 13; // 8k
    char* page = new char[pagesize]; // 申请了 8k 的内存;

    size_t shift = sizeof(A);
    char* offset = page + shift; // 第二个 A 对象的起始地址
#if 1
    A* a = new (page) A(1160); // 在申请的内存上创建对象
    a->show();

    A* c = new (offset) A(5678); // 在未被占用的内存上创建 c 对象
    c->show();

    saveMemoryToFile(page, pagesize, "C:/lian/sandbox/testFile");
#else
    readMemoryFromFile(page, "C:/lian/sandbox/testFile");

    A* b = reinterpret_cast<A*>(page); //直接通过地址获取对象
    b->show();

    A* d = reinterpret_cast<A*>(offset); // 直接通过地址获取对象
    d->show();
#endif
    return 0;
}
  1. 把 #if 打开,执行程序。代码的作用是把类对象保存到文件里;

  2. 把 #if 关闭,打开 #else 中的代码,再执行程序。代码的作用是读取文件,直接把文件中的内容转换成类 A 的对象,

第一次输出:
num: 1160
num: 5678

第二次输出:
num: 1160
num: 5678

从结果可以看出来,成功通过访问内存的方式获取类对象。

总结

  对于比较大的数据文件,解析的时间较长,可以采用这种内存映射的方式保存出一份内存文件,这样就可以快速的打开文件,直接获取解析之后的数据格式。

博主个人网站文章来源地址https://www.toymoban.com/news/detail-536072.html

到了这里,关于C++ 内存映射的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

原文地址:https://blog.csdn.net/weixin_42344452/article/details/131604636

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包