在 C++ 中分配和使用无类型内存块的正确方法是什么?

2024-01-08

到目前为止,我对这个问题得到的答案有两种完全相反的答案:“它是安全的”和“它是未定义的行为”。我决定完全重写这个问题,以便为我和任何可能通过谷歌到达这里的人获得一些更好的澄清答案。

另外,我删除了C标签,现在这个问题是 C++ 特定的

我正在制作一个 8 字节对齐的内存堆,将在我的虚拟机中使用。我能想到的最明显的方法是分配一个数组std::uint64_t.

std::unique_ptr<std::uint64_t[]> block(new std::uint64_t[100]);

我们假设sizeof(float) == 4 and sizeof(double) == 8。我想存储一个浮点数和一个双精度数block并打印该值。

float* pf = reinterpret_cast<float*>(&block[0]);
double* pd = reinterpret_cast<double*>(&block[1]);
*pf = 1.1;
*pd = 2.2;
std::cout << *pf << std::endl;
std::cout << *pd << std::endl;

我还想存储一个表示“hello”的 C 字符串。

char* pc = reinterpret_cast<char*>(&block[2]);
std::strcpy(pc, "hello\n");
std::cout << pc;

现在我想存储“Hello, world!”它超过 8 个字节,但我仍然可以使用 2 个连续的单元格。

char* pc2 = reinterpret_cast<char*>(&block[3]);
std::strcpy(pc2, "Hello, world\n");
std::cout << pc2;

对于整数,我不需要reinterpret_cast.

block[5] = 1;
std::cout << block[5] << std::endl;

我正在分配block作为一个数组std::uint64_t仅用于内存对齐的目的。我也不期望任何大于 8 字节的内容存储在其中。如果保证起始地址是 8 字节对齐,则块的类型可以是任何类型。

有些人已经回答说我所做的事情是完全安全的,但另一些人则说我肯定会调用未定义的行为。

我是否编写了正确的代码来实现我的意图?如果不是,合适的方法是什么?


全局分配函数

要分配任意(无类型)内存块,可以使用全局分配函数(第 3.7.4/2 节);

void* operator new(std::size_t);
void* operator new[](std::size_t);

可用于执行此操作 (§3.7.4.1/2)。

§3.7.4.1/2

分配函数尝试分配请求的存储量。如果成功,它将返回存储块的起始地址,该存储块的长度(以字节为单位)应至少与请求的大小一样大。从分配函数返回时,分配的存储空间的内容没有任何限制。通过连续调用分配函数分配的存储的顺序、连续性和初始值是未指定的。返回的指针应适当对齐,以便它可以转换为具有基本对齐要求(3.11)的任何完整对象类型的指针,然后用于访问分配的存储中的对象或数组(直到存储被显式释放)调用相应的释放函数)。

3.11 是这样说的基本对齐要求;

§3.11/2

基本对齐由小于或等于所有上下文中实现支持的最大对齐的对齐表示,该对齐等于alignof(std::max_align_t).

只是为了确保分配函数的行为必须像这样;

§3.7.4/3

C++ 程序中定义的任何分配和/或释放函数(包括库中的默认版本)应符合 3.7.4.1 和 3.7.4.2 中指定的语义。

引述自C++ WD n4527 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4527.pdf.

假设 8 字节对齐小于平台的基本对齐(看起来确实如此,但这可以在目标平台上验证)static_assert(alignof(std::max_align_t) >= 8)) - 您可以使用全局::operator new来分配所需的内存。分配后,可以根据您的大小和对齐要求对内存进行分段和使用。

这里的另一种选择是 the std::aligned_storage http://en.cppreference.com/w/cpp/types/aligned_storage它能够为您提供符合任何要求的内存。

typename std::aligned_storage<sizeof(T), alignof(T)>::type buffer[100];

从问题中,我假设这里的大小和对齐方式T将会是8。


最终内存块的示例如下(包括基本 RAII);

struct DataBlock {
    const std::size_t element_count;
    static constexpr std::size_t element_size = 8;
    void * data = nullptr;
    explicit DataBlock(size_t elements) : element_count(elements)
    {
        data = ::operator new(elements * element_size);
    }
    ~DataBlock()
    {
        ::operator delete(data);
    }
    DataBlock(DataBlock&) = delete; // no copy
    DataBlock& operator=(DataBlock&) = delete; // no assign
    // probably shouldn't move either
    DataBlock(DataBlock&&) = delete;
    DataBlock& operator=(DataBlock&&) = delete;

    template <class T>
    T* get_location(std::size_t index)
    {
        // https://stackoverflow.com/a/6449951/3747990
        // C++ WD n4527 3.9.2/4
        void* t = reinterpret_cast<void*>(reinterpret_cast<unsigned char*>(data) + index*element_size);
        // 5.2.9/13
        return static_cast<T*>(t);

        // C++ WD n4527 5.2.10/7 would allow this to be condensed
        //T* t = reinterpret_cast<T*>(reinterpret_cast<unsigned char*>(data) + index*element_size);
        //return t;
    }
};
// ....
DataBlock block(100);

我构建了更详细的示例DataBlock有合适的模板construct and get功能等,现场演示在这里 http://coliru.stacked-crooked.com/a/37af4796160ecd8c and 这里进行进一步的错误检查等。 http://coliru.stacked-crooked.com/a/8b7251effad9b667.

关于别名的注释

看起来原始代码中确实存在一些别名问题(严格来说);您分配一种类型的内存并将其转换为另一种类型。

它可能会在您的目标平台上按您的预期工作,但您不能依赖它。我对此见过的最实际的评论是;

“未定义的行为会产生令人讨厌的结果,通常会做你认为应该做的事情,直到它不做为止” - hvd https://stackoverflow.com/questions/31204824/why-give-a-c-compiler-warning-when-returning-an-rvalue-reference#comment50412114_31204824.

您拥有的代码可能会起作用。我认为最好使用适当的全局分配函数,并确保在分配和使用所需内存时不存在未定义的行为。

别名仍然适用;一旦分配了内存,别名就适用于它的使用方式。一旦分配了任意内存块(如上面的全局分配函数)并且对象的生命周期开始(§3.8/1) - 别名规则适用。

关于什么std::allocator?

虽然std::allocator http://en.cppreference.com/w/cpp/memory/allocator适用于同质数据容器,您正在寻找的内容类似于异构分配,标准库中的实现(给定分配器概念 http://en.cppreference.com/w/cpp/concept/Allocator)提供了有关原始内存分配和所需对象的相应构造的一些指导。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C++ 中分配和使用无类型内存块的正确方法是什么? 的相关文章

随机推荐

  • 如何识别每个簇内的序列?

    使用作为一部分的 biofam 数据集TraMineR library TraMineR data biofam lab lt c P L M LM C LC LMC D biofam seq lt seqdef biofam 10 25
  • 分页库使数据源无效不起作用

    最近我正在尝试这个 我有一个由数据源支持的作业列表 我正在使用分页库 并且作业列表中的每个项目都有一个保存按钮 并且该保存按钮将数据库中作业的状态从未保存更新为已保存 反之亦然 一旦更新 它就会使数据源失效 现在失效应该会导致当前页面立即重
  • 如何将这个基于表格的布局转换为 CSS?

    我有一个基于表格的布局 高度 宽度为 100 没有滚动条 标题 红色 自动扩展以适应内容 但我不知道它会有多少像素 下面的流体表准确地给出了我的内容 table height 100 width 100 tr height 1 td Fit
  • 如何使用 StreamReader 和 StreamWriter 创建文件副本

    我需要使用 StreamReader 在控制台应用程序上读取 txt 文件 然后创建一个名称不同但内容相同的新文件或备份 问题是我无法弄清楚如何使用第一个文件中的内容放入新文件中 这是学校的事情 我是 C 新手 using System u
  • matplotlib `imshow(interpolation='nearest')` 有什么作用?

    I use imshow功能与interpolation nearest 在灰度图像上并得到一个漂亮的彩色图片 看起来它为我做了某种颜色分割 到底发生了什么 我也想得到类似的图像处理功能 numpy 数组上是否有一些函数 例如interpo
  • 毕加索图像不显示

    一张图像显示在我的应用程序上 另一张图像没有显示 但是都可以从浏览器访问 我的免费主机中的这个未显示在我的应用程序上 请注意 我可以从免费主机服务器看到图像 http www justedhak comlu com images uploa
  • Angular 指令隔离范围上的可选双向绑定

    question 我刚刚了解到您可以通过以下方式进行可选的 反向 或回调绑定 scope parentScopeFunc 我正在尝试查看是否有一种方法可以对双向绑定执行类似的操作 scope optional2WayBoundProp 我尝
  • 如何在 Spring MVC 中使用带注释的映射来实现不区分大小写的 URL

    我已经通过我的 spring mvc Web 应用程序很好地注释了映射 但是它们区分大小写 我找不到一种方法使它们不区分大小写 我很乐意在 Spring MVC 中实现这一点 而不是以某种方式重定向流量 Spring 4 2将支持不区分大小
  • Flutter 通过拖动调整 TextField 大小

    有什么方法可以创建类似这些点的东西 这可以帮助扩展TextField 截屏 创建一个小部件 class ExpandableTextField extends StatefulWidget final double height final
  • GCP:警报触发时是否可以触发云功能?

    我正在使用谷歌云平台的谷歌云监控 我为我监控的对象创建了一些警报策略 但是 当有警报触发时 有些信息未包含在我希望包含在电子邮件中的信息中 因此 我正在考虑使用云功能 如果在这种情况下可以的话 该功能将触发我创建的策略之一 如果可能的话 请
  • TypeError:不支持的操作数类型 -:python 3.x Anaconda 中的“str”和“str”

    我正在尝试在大型数据集中每小时计算一些实例 下面的代码似乎在 python 2 7 上运行良好 但我必须将其升级到 3 x 最新版本的 python 并在 Anaconda 上更新所有包 当我尝试执行该程序时 我正在跟踪str error
  • MySQL 有没有办法隐式地为表创建主键?

    在MySQL中 当CREATE TABLE时 MySQL是否有办法隐式创建一个列 即CREATE TABLE命令中未显式声明的列 作为表的主键 Thanks 不 需要在表上定义主键 您可能正在考虑this https dev mysql c
  • Rails - 如何向用 javascript 创建的表单添加 CSRF 保护?

    我正在使用backbone js 它效果很好 但我作为 JavaScript 模板创建的表单缺少 Rails csrf 保护令牌 如何将其添加到我用 JavaScript 创建的模板中 我解决这个问题的最好方法是在表单中 Update 它看
  • JQuery 验证多个字段并出现一个错误

    我将如何使用 JQuery Validate 插件为 3 个字段提供一条错误消息 例如 3 个 dob 字段 默认情况下 如果所有 3 个字段都留空 我将收到 3 条错误消息 我只想将一个错误链接到 3 个字段 如果有空白 则会出现错误 类
  • 自动调整 SVG 大小?

    这里有一个代码演示 http jsfiddle net y59MR 1 我有一个高度未知的 SVG 我可以在加载 json 并使用 javascript math 后弄清楚它 但是有没有可以使用的 css 来动态调整它的大小 css svg
  • UTF-8 与 Latin1 mysql,UTF-8 上未使用索引

    我尝试使用 UTF 8 和 Latin1 字符集创建 mysql 表 当我使用 Latin1 时 会使用索引 而当我使用 UTF 8 时 选择 限制记录时不会使用索引 我的字符集是否缺少某些内容导致发生这种情况 Cheers Ke 仅当表达
  • .gitattributes 中没有扩展名的文件

    我正在尝试处理 gitattributes 中没有扩展名的文件 text auto eol lf py eol lf 显然没有帮助 git check attr all foo输出 foo 文本 自动 如何才能做到这一点 我认为您必须为所有
  • 迭代 CSV 文件中的列 (PHP)

    我需要编写一个函数 以年份和温度作为输入 并返回给定年份中温度等于或低于给定温度的天数 由于数据是关于小时而不是天 因此需要找到小时数并将其除以 24 示例 getDaysUnderTemp 2019 10 返回 13 92 CSV 文件如
  • ng2-smart-table 缺少依赖项完成程序

    我正在使用 Ng2SmartTable 并且出现此错误 目标入口点 ng2 smart table 丢失时出现错误 依赖项 akveo ng2 completer 我已经尝试过以下命令 但它不起作用 1 npm install save n
  • 在 C++ 中分配和使用无类型内存块的正确方法是什么?

    到目前为止 我对这个问题得到的答案有两种完全相反的答案 它是安全的 和 它是未定义的行为 我决定完全重写这个问题 以便为我和任何可能通过谷歌到达这里的人获得一些更好的澄清答案 另外 我删除了C标签 现在这个问题是 C 特定的 我正在制作一个