将基本类型数组中的内存重用于不同(但仍然是基本)类型数组是否合法

2023-12-28

这是另一个的后续question https://stackoverflow.com/q/51930334/3545273关于内存重用。由于最初的问题是关于具体实现的,因此答案与该具体实现相关。

所以我想知道,在一致的实现中,将基本类型数组的内存重新用于提供的不同类型的数组是否合法:

  • 两种类型都是基本类型,因此具有简单的 dtor 和默认 ctor
  • 两种类型具有相同的尺寸和对齐要求

我以以下示例代码结束:

#include <iostream>

constexpr int Size = 10;

void *allocate_buffer() {
    void * buffer = operator new(Size * sizeof(int), std::align_val_t{alignof(int)});
    int *in = reinterpret_cast<int *>(buffer); // Defined behaviour because alignment is ok
    for (int i=0; i<Size; i++) in[i] = i;  // Defined behaviour because int is a fundamental type:
                                           // lifetime starts when is receives a value
    return buffer;
}
int main() {
    void *buffer = allocate_buffer();        // Ok, defined behaviour
    int *in = static_cast<int *>(buffer);    // Defined behaviour since the underlying type is int *
    for(int i=0; i<Size; i++) {
        std::cout << in[i] << " ";
    }
    std::cout << std::endl;
    static_assert(sizeof(int) == sizeof(float), "Non matching type sizes");
    static_assert(alignof(int) == alignof(float), "Non matching alignments");
    float *out = static_cast<float *>(buffer); //  (question here) Declares a dynamic float array starting at buffer
    // std::cout << out[0];      // UB! object at &out[0] is an int and not a float
    for(int i=0; i<Size; i++) {
        out[i] = static_cast<float>(in[i]) / 2;  // Defined behaviour, after execution buffer will contain floats
                                                 // because float is a fundamental type and memory is re-used.
    }
    // std::cout << in[0];       // UB! lifetime has ended because memory has been reused
    for(int i=0; i<Size; i++) {
        std::cout << out[i] << " ";         // Defined behaviour since the actual object type is float *
    }
    std::cout << std::endl;
    return 0;
}

我添加了注释,解释了为什么我认为这段代码应该具有定义的行为。恕我直言,一切都很好,并且 AFAIK 标准符合,但我无法找到该行是否标记在这里提问是否有效。

浮点数对象会重复使用 int 对象的内存,因此当浮点数的生命周期开始时,整数的生命周期也结束,因此严格别名规则应该不是问题。数组是动态分配的,因此对象(int 和 float)实际上都是在 a 中创建的空型返回的数组operator new。所以我认为一切都应该没问题。

但由于它允许低级对象替换,这在现代 C++ 中通常是不受欢迎的,所以我必须承认我有一个疑问......

所以问题是:上面的代码是否调用了 UB,如果是,在哪里以及为什么?

免责声明:我建议不要在可移植代码库中使用此代码,这实际上是一个语言律师问题。


int *in = reinterpret_cast<int *>(buffer); // Defined behaviour because alignment is ok

正确的。但可能不是你所期望的那样。[expr.static.cast] https://timsong-cpp.github.io/cppwp/n4659/expr.static.cast#13

“指向的指针”类型的纯右值cv1 void” 可以转换为“指向的指针”类型的纯右值cv2 T“, 在哪里T是一个对象类型并且cv2简历资格与以下相同或更高:cv1。如果原始指针值代表地址A内存中的一个字节和A不满足对齐要求T,则结果指针值未指定。否则,如果原始指针值指向一个对象a,并且有一个对象b类型的T(忽略 cv 限定)可与指针相互转换a,结果是一个指向b。否则,指针值不会因转换而改变。

没有int也没有任何指针可相互转换的对象buffer,因此指针值不变。in是一个类型的指针int*指向原始内存区域。

for (int i=0; i<Size; i++) in[i] = i;  // Defined behaviour because int is a fundamental type:
                                       // lifetime starts when is receives a value

是不正确的。[介绍对象] https://timsong-cpp.github.io/cppwp/n4659/intro.object#1

当隐式更改联合的活动成员或创建临时对象时,通过定义、new 表达式创建对象。

值得注意的是,没有分配。不int被建造。事实上,通过消除,in is an 无效指针 https://timsong-cpp.github.io/cppwp/n4659/basic.compound#3,取消引用它是 UB。

后来的float*全部也遵循UB。

即使没有上述所有 UB,通过正确使用new (pointer) Type{i};创建对象,没有array存在的对象。 (不相关的)对象恰好在内存中并排排列。这意味着指针算术运算的结果也是 UB。[表达式.添加] https://timsong-cpp.github.io/cppwp/n4659/expr.add#4

当具有整型类型的表达式与指针相加或相减时,结果具有指针操作数的类型。如果表达式P指向元素x[i]数组对象的x with n元素、表达式P + J and J + P(其中 J 的值为 j)指向(可能是假设的)元素x[i+j] if 0 ≤ i+j ≤ n;否则,行为是未定义的。同样,表达式P - J指向(可能是假设的)元素x[i−j] if 0 ≤ i−j ≤ n;否则,行为是未定义的。

其中假设元素是指最后一个(假设)元素。请注意,指向恰好与另一个对象位于同一地址位置的尾后元素的指针并不指向该另一个对象。

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

将基本类型数组中的内存重用于不同(但仍然是基本)类型数组是否合法 的相关文章

  • 无法使用已与其底层 RCW 分离的 COM 对象。在 oledb 中

    我收到此错误 但我不知道我做错了什么 下面的代码在backrgroundworker中 将异常详细信息复制到剪贴板 System Runtime InteropServices InvalidComObjectException 未处理 通
  • 获取按下的按钮的返回值

    我有一个在特定事件中弹出的表单 它从数组中提取按钮并将标签值设置为特定值 因此 如果您要按下或单击此按钮 该函数应返回标签值 我怎样才能做到这一点 我如何知道点击了哪个按钮 此时代码返回 DialogResult 但我想从函数返回 Tag
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • 将布尔参数传递给 SQL Server 存储过程

    我早些时候问过这个问题 我以为我找到了问题所在 但我没有 我在将布尔参数传递给存储过程时遇到问题 这是我的 C 代码 public bool upload false protected void showDate object sende
  • 将目录压缩为单个文件的方法有哪些

    不知道怎么问 所以我会解释一下情况 我需要存储一些压缩文件 最初的想法是创建一个文件夹并存储所需数量的压缩文件 并创建一个文件来保存有关每个压缩文件的数据 但是 我不被允许创建许多文件 只能有一个 我决定创建一个压缩文件 其中包含有关进一步
  • WPF TabControl,用C#代码更改TabItem的背景颜色

    嗨 我认为这是一个初学者的问题 我搜索了所有相关问题 但所有这些都由 xaml 回答 但是 我需要的是后台代码 我有一个 TabControl 我需要设置其项目的背景颜色 我需要在选择 取消选择和悬停时为项目设置不同的颜色 非常感谢你的帮助
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • Web API - 访问 DbContext 类中的 HttpContext

    在我的 C Web API 应用程序中 我添加了CreatedDate and CreatedBy所有表中的列 现在 每当在任何表中添加新记录时 我想填充这些列 为此目的我已经覆盖SaveChanges and SaveChangesAsy
  • 如何返回 json 结果并将 unicode 字符转义为 \u1234

    我正在实现一个返回 json 结果的方法 例如 public JsonResult MethodName Guid key var result ApiHelper GetData key Data is stored in db as v
  • 如何将图像路径保存到Live Tile的WP8本地文件夹

    我正在更新我的 Windows Phone 应用程序以使用新的 WP8 文件存储 API 本地文件夹 而不是 WP7 API 隔离存储文件 旧的工作方法 这是我如何成功地将图像保存到 共享 ShellContent文件夹使用隔离存储文件方法
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 将 xml 反序列化为类,list<> 出现问题

    我有以下 XML
  • 如何让Gtk+窗口背景透明?

    我想让 Gtk 窗口的背景透明 以便只有窗口中的小部件可见 我找到了一些教程 http mikehearn wordpress com 2006 03 26 gtk windows with alpha channels https web
  • C - 直接从键盘缓冲区读取

    这是C语言中的一个问题 如何直接读取键盘缓冲区中的数据 我想直接访问数据并将其存储在变量中 变量应该是什么数据类型 我需要它用于我们研究所目前正在开发的操作系统 它被称为 ICS OS 我不太清楚具体细节 它在 x86 32 位机器上运行
  • Validation.ErrorTemplate 的 Wpf 动态资源查找

    在我的 App xaml 中 我定义了一个资源Validation ErrorTemplate 这取决于动态BorderBrush资源 我打算定义独特的BorderBrush在我拥有的每个窗口以及窗口内的不同块内
  • 如何使用 std::string 将所有出现的一个字符替换为两个字符?

    有没有一种简单的方法来替换所有出现的 in a std string with 转义 a 中的所有斜杠std string 完成此操作的最简单方法可能是boost字符串算法库 http www boost org doc libs 1 46
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框
  • 使用按位运算符相乘

    我想知道如何使用按位运算符将一系列二进制位相乘 但是 我有兴趣这样做来查找二进制值的十进制小数值 这是我正在尝试做的一个例子 假设 1010010 我想使用每个单独的位 以便将其计算为 1 2 1 0 2 2 1 2 3 0 2 4 虽然我

随机推荐