使用“memcpy”复制二维数组在技术上是未定义的行为吗?

2024-04-08

评论中出现了一个有趣的讨论最近的这个问题 https://stackoverflow.com/q/69329303/10871073: 现在,虽然有语言C,讨论已经转向什么C++标准指定了使用以下函数访问多维数组的元素时构成未定义行为的内容std::memcpy.

首先,这是该问题的代码,转换为 C++ 并使用const只要有可能:

#include <iostream>
#include <cstring>

void print(const int arr[][3], int n)
{
    for (int r = 0; r < 3; ++r) {
        for (int c = 0; c < n; ++c) {
            std::cout << arr[r][c] << " ";
        }
        std::cout << std::endl;
    }
}

int main()
{
    const int arr[3][3] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
    int arr_copy[3][3];
    print(arr, 3);
    std::memcpy(arr_copy, arr, sizeof arr);
    print(arr_copy, 3);
    return 0;
}

问题在于调用std::memcpy: the arr参数将产生(通过衰减)指向第一个的指针int[3]子数组所以,根据讨论的一方面(由特德·林莫 https://stackoverflow.com/users/7582247/ted-lyngmo?tab=topactivity), 当。。。的时候memcpy函数访问该子数组第三个元素之外的数据,有formally未定义的行为(同样适用于目的地,arr_copy).

然而,辩论的另一方(平庸蔬菜1 https://stackoverflow.com/users/13188071/mediocrevegetable1?tab=topactivity我订阅)使用每个二维数组的基本原理,根据定义,占用连续内存,并且作为参数memcpy只是void*指向这些位置的指针(以及第三个,size论证有效),那么这里不可能有 UB。

以下是与辩论最相关的一些评论的摘要,以防原始问题发生任何“清理”(粗体表示我的强调):

我不认为这里有任何越界。就像memcpy适用于数组ints,它适用于数组int [3]s,并且两者应该是连续的(但我不是 100% 确定)。 – 平庸蔬菜1

当您从以下位置复制第一个字节时,就会发生越界访问:arr[0][3]。我从未见过它真正失败,但是,在 C++ 中,它有 UB。 ——特德·林莫

But the memcpy函数/调用不执行任何数组索引 - 它只是给出两个void*指针并将内存从一个对象复制到另一个对象。 ——阿德里安·莫尔

我不能肯定地说这在 C 中是否重要。在 C++ 中则不然。你得到一个指向第一个的指针int[3]任何超出其范围的访问都有UB。我在 C++ 标准中没有发现任何例外。——特德·林莫

我不认为arr[0][3]事情适用。按照这个逻辑,我认为复制第二个int of an int数组通过memcpy也会是UB。int [3]只是类型arr的元素,以及边界arr整体以字节为单位应该是sizeof (int [3]) * 3。我可能错过了一些东西:/ – 平庸的蔬菜 1

是否有任何 C++ 语言律师可以解决这个问题 – 最好有 C++ 标准的适当引用?

另外,C 标准的相关引用可能会有所帮助 - 特别是如果两种语言标准不同 - 因此我在这个问题中包含了 C 标签。


std::memcpy(arr_copy, arr, sizeof arr);(你的例子)是明确定义的。

std::memcpy(arr_copy, arr[0], sizeof arr);另一方面,会导致未定义的行为(至少在 C++ 中;对于 C 不完全确定)。


多维数组是数组的一维数组。据我所知,与真正的一维数组(即具有非数组类型元素的数组)相比,它们没有得到太多(如果有的话)特殊待遇。

考虑一个一维数组的示例:

int a[3] = {1,2,3}, b[3];
std::memcpy(b, a, sizeof(int) * 3);

This is obviously legal.1

请注意memcpy接收指向数组第一个元素的指针,并且可以访问其他元素。

元素类型不影响此示例的有效性。如果使用二维数组,元素类型变为int[N]而不是int,但有效性不受影响。

现在,考虑一个不同的例子:

int a[2][2] = {{1,2},{3,4}}, b[4];
std::memcpy(b, a[0], sizeof(int) * 4);
//             ^~~~

This one causes UB2, because since memcpy is given a pointer to the first element of a[0], it can only access the elements of a[0] (a[0][i]), and not a[j][i].

但是,如果你想听我的意见,这是一种“驯服”的 UB,在实践中可能不会造成问题(但是,一如既往,如果可能的话,应该避免 UB)。



1 The C++ standard doesn't explain memcpy, and instead refers to the C standard. The C standard uses somewhat sloppy wording:

C11 (N1570)[7.24.2.1]/2 http://port70.net/%7Ensz/c/c11/n1570.html#7.24.2.1p2

The memcpy函数副本n指向的对象中的字符s2进入指向的对象s1.

指向数组的第一个(或任何)元素的指针仅指向该元素,而不指向整个数组,即使可以通过所述指针访问整个数组。因此,如果按字面解释,@LanguageLawyer 似乎是正确的:如果你给出memcpy指向数组元素的指针,您只能复制该单个元素,而不能复制连续的元素。

这种解释与常识相矛盾,而且很可能不是有意的。

例如。考虑中的例子[basic.types.general]/2 http://eel.is/c++draft/basic#types.general-example-1,这适用于memcpy使用指向第一个元素的指针到数组:(即使示例是非规范的)

constexpr std::size_t N = sizeof(T);
char buf[N];
T obj;
std::memcpy(buf, &obj, N);
std::memcpy(&obj, buf, N);

2 This is moot, because of the problematic wording for memcpy described above.

我对 C 不太确定,但对于 C++,有强烈的暗示这是 UB。

首先,考虑一个使用的类似示例std::copy_n,尝试执行按元素复制而不是按字节复制:

#include <algorithm>

consteval void foo()
{
    int a[2][2] = {{1,2},{3,4}}, b[2][2] = {{1,2},{3,4}};
    std::copy_n(a[0], 4, b[0]);
}

int main() {foo();} 

在编译时运行函数会捕获大多数形式的 UB(它使代码格式错误),并且编译此代码片段确实会给出:

error: call to consteval function 'foo' is not a constant expression
note: cannot refer to element 4 of array of 2 elements in a constant expression

情况与memcpy不太确定,因为它执行按字节复制。整个主题似乎是模糊且不明确 https://stackoverflow.com/q/62329008/2752075.

考虑以下措辞:std::launder:

[ptr.launder]/4 http://eel.is/c++draft/ptr.launder#4

一个字节的存储空间b可以通过指向对象的指针值访问Y如果有一个物体Z, 指针可与Y,使得b位于占用的存储空间内Z,或直接封闭的数组对象,如果Z是一个数组元素。

换句话说,给定一个指向数组元素的指针,该数组的所有元素都可以通过该指针访问(非递归地,即通过&a[0][0] only a[0][i]是可达的)。

形式上,这个定义仅用于描述std::launder(事实上​​,它无法扩展给定的指针的可到达区域)。但其含义似乎是该定义总结了标准其他部分描述的可达性规则([static.cast]/13 http://eel.is/c++draft/expr#static.cast-13,请注意reinterpret_cast is 定义通过 http://eel.is/c++draft/expr.reinterpret.cast#7相同的措辞;还[basic.compound]/4 http://eel.is/c++draft/basic.compound#4).

目前尚不完全清楚上述规则是否适用于memcpy,但他们应该。因为否则,程序员将能够使用库函数忽略可达性,这将使可达性的概念几乎毫无用处。

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

使用“memcpy”复制二维数组在技术上是未定义的行为吗? 的相关文章

  • Qt 5 和 QProcess 使用信号/槽 read 重定向标准输出

    这个问题困扰着我 因为它应该有效 但遗憾的是它没有 我试图实现的是读取某个进程的标准输出并让另一个进程处理它 即打印出来 产生输出的过程如下所示 include
  • VS Code:自定义关键字的注入语法范围在 C++ 中被覆盖

    我想制作一个小型 VS Code 扩展 为 C C 代码中的少数自定义关键字添加语法突出显示 我正在尝试通过注入语法来做到这一点source c and source cpp语言范围 遵循VS Code 语法高亮指南 https code
  • 我应该如何以非 root 身份读取 Linux 上的 Intel PCI 非核心性能计数器?

    我想要一个库 允许对 Linux 可执行文件的关键部分进行 自我分析 就像人们可以使用一个部分计时一样获取当日时间 http linux die net man 2 gettimeofday or RDTSC http www strchr
  • Microsoft.Web.Administration 内存泄漏

    拥有一个 Windows 服务 除其他外 还可以监视 IIS 应用程序池 如果任何池已配置应用程序但未运行 则该池 池 将启动 这已经运行良好一段时间了 最近发现该服务存在内存泄漏 查看内存转储 罪魁祸首是用于检查应用程序池的 Micros
  • unique_ptr需要存储删除器怎么可能没有开销呢?

    先看看C Primer讲了什么unique ptr and shared ptr 16 1 6 美元 效率和灵活性 我们可以确定的是shared ptr不将删除者视为直接成员 因为删除器的类型直到运行时才知道 因为删除器的类型是a类型的一部
  • 关闭模态后清除模态字段

    我有这个模式
  • 不屈不挠的野兽:一个二维字符数组,位于结构内部,位于非托管 dll 的内部

    我束紧腰 冒险进入了遗产之地 砍倒 召唤并集结了各种野兽 现在我站在了一个如此凶猛的生物面前 据我对我的弟兄们进行的详尽调查来看 我现在所面对的生物是如此凶猛 武器中 没有一个代码战士能够幸存 以下是详细信息 我试图将结构内部的二维字符数组
  • const int 列表而不是 enum

    我开始研究大型 C 代码库 并发现使用带有多个 const ints 字段的静态类 这个类的行为与枚举完全一样 我想将类转换为实际的枚举 但权力被拒绝 我想转换它的主要原因是这样我可以将枚举作为数据类型而不是 int 这对可读性有很大帮助
  • 如何将 Activator.CreateInstance 与字符串一起使用?

    在我的反射代码中 我的通用代码部分遇到了问题 特别是当我使用字符串时 var oVal object Test var oType oVal GetType var sz Activator CreateInstance oType oVa
  • cygwin $'\r':命令未找到错误

    我稍微修改了一个项目 在调试下它运行得很好 当我尝试在不调试的情况下构建它时 它显示错误 无法修复它 make Making all in third party make 1 Entering directory cygdrive c U
  • C# 多重继承

    目前我正在学习 C 和 ASP NET MVC 4代码优先方法 我是 Visual Basic 开发人员 现在我想开始 C 而且 现在我遇到了必须管理多重继承的情况 但是 对于Class i来说这是不可能的 那么 我应该如何管理我拥有的这些
  • NHibernate Criteria API 是否支持集合属性的投影?

    我需要使用条件 API 复制以下工作 HQL 查询 session CreateQuery select c from Parent p inner join p Children c where p Id 9 and c Id 33 Se
  • 解析 SWIG 接口文件的结构属性

    这是我不久前问过的问题的延续 为通过参数返回的函数创建类型映射 https stackoverflow com questions 12793973 create a typemap for a function that returns
  • 将base64字符串转换为图像c#时出错

    我想在我的网页上显示图像 并单击应该下载的链接按钮 存储的图像文件以二进制格式存储在db中 将 base64 字符串转换为图像时显示错误 详细信息如下 帮助我找到合适的解决方案 谢谢 Error Code protected void Pa
  • 同时运行 x 个网络请求

    我们公司有一个网络服务 我想通过我自己的服务发送 XML 文件 存储在我的驱动器上 HTTPWebRequestC 中的客户端 这已经有效了 Web服务同时支持5个同步请求 一旦服务器上的处理完成 我就会从Web服务获得响应 每个请求的处理
  • 最有用的用户制作的 C 宏(在 GCC 中,还有 C99)? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 实体框架中对象属性中的 NULL 值

    Tables Article Author Comment 1篇文章和1位作者可以有 评论 数据库中有 1 篇文章 1 位作者和 1 条评论 问题是 该代码 myBD my bd new myBD var articles by bd Ar
  • Microsoft Graph API 调用无限期挂起

    我正在尝试使用 Microsoft Graph 查询 Azure Active Directory 用户信息 我可以很好地进行身份验证 但是当我尝试查询用户信息时client Users我的应用程序无限期挂起 没有超时 没有错误 只是挂起
  • qt 如何知道按钮被点击?

    我正在尝试编写一个程序 用声音进行一些操作 我的问题是我有 3 个播放按钮和 3 个标签 我希望无论我单击 播放 按钮 都应该播放按钮附近标签中名称的声音 我有一个没有任何参数的播放插槽 那么 如何分别连接到每个播放按钮和每个标签呢 实际上
  • 在 Rx 中,如何按 id 对事件进行分组并按多个时间跨度限制每个组?

    可以这么说 我陷入了 Rx 热潮 这个问题与我的相关here https stackoverflow com questions 19425965 rx how to group by a key a complex object and

随机推荐

  • 为了速度/性能什么时候应该、不应该脱离 OOP?

    在 Android 开发者文章中 Google 指出 您通常应该声明公共变量 而不是带有 getter 和 setter 的私有变量 以增强嵌入式设备的性能 我认为函数调用比仅仅写入地址更昂贵 我想知道 应该在多大程度上牺牲性能来坚持 OO
  • 如何在列表框中设置对齐格式

    我将值添加到列表框 for int i 0 i lt 2 i lbBeamValue Items Add Beam i ToString value1 i Angle i ToString value2 i 显示如下 Beam 0 0 12
  • 仅保留 JavaScript 对象中的某些属性

    我有一个对象 我想通过删除除某些特定属性之外的所有属性来修改对象 而不是克隆它 例如 如果我从这个对象开始 var myObj p1 123 p2 321 p3 p3 1 1231 p3 2 342 p4 23423 p99 p99 1 s
  • 截断分页中的页数

    这可能是一个非常愚蠢的问题 但我想不出任何可以帮助我走得更远的东西 我希望缩短页面导航中的数字数量 而不是像 1 2 3 4 5 6 7 8 我希望它像 1 2 7 8 当我去2 数字3现在应该可以在数字组中看到 这是我负责页码的代码 di
  • MySQL 最大用户连接数与最大连接数

    可悲的是 我在任何地方都找不到对此查询的任何直接解释 甚至在 MySQL 文档中也找不到 各个论坛上的一些人说 max user connections 永远不能大于 max connections 例如 如果一个用户有3 max user
  • mutableStateOf 和 mutableStateListOf 有什么区别?

    在与一个ViewModel and a List存储在那里 我通常遵循这种方法 var characteristics by mutableStateOf listOf
  • Emacs、Vim 和 JEdit 中哪些编辑器支持同时多个文本插入点?

    背景 JEdi t 以及其他一些文本编辑器 支持称为多个同时文本插入点 http groups csail mit edu uid projects simuledit usenix01 html 至少我在这里这么称呼它 要了解这意味着什么
  • 使用 STM32 USB 设备库将闪存作为大容量存储设备

    我的板上有这个闪存IC 它连接到我的STM32F04 ARM处理器 处理器的USB端口可供用户使用 我希望我的闪存在通过 USB 连接到 PC 时被检测为存储设备 作为第一步 我在程序中将 USB 类定义为 MSC 效果很好 因为当我将主板
  • Laravel 5 - Php artisan 语法错误

    我目前正在使用 Laravel 5 开发一个应用程序 突然工匠停止工作了 我无法对其使用单个命令 它总是返回错误 Symfony Component Debug Exception FatalErrorException syntax er
  • 什么时候值得使用数据库?

    我有一个与数据库有关的问题 以及什么时候值得深入研究 我主要是一名嵌入式工程师 但我正在使用 Qt 编写一个应用程序来与我们的控制器交互 我们正处于一个奇怪的境地 我们有足够的数据 可以实现一个数据库 大约 700 多个项目并且还在不断增长
  • EF4 审核多对多关系的更改

    我正在将审核添加到我的 EF4 模型优先 应用程序中 我可以获得有关发生更改的实体的结构属性的详细信息 我还可以看到多对多关系何时发生变化 我可以看到所涉及类型的名称以及发生的情况 添加或删除 但我真正想要的是关系更改中涉及的实体的 ID
  • 使用 scala 时 lambda 的 AWS 凭证不起作用

    尝试使用通过 DefaultCredentialProvider 提供的凭证时 AWS lambda 函数不起作用 我需要将凭据传递给 S3 才能运行 Code def initializeAwsCredentials AWSCredent
  • iOS Firebase - 如何从不同节点中删除具有相同密钥的子节点

    我有一个名为 以下 的裁判 在该引用下 有 2 个不同的 userId 关注同一用户 如果他们都关注的用户想要删除他们的帐户 我想从关注节点中删除他们 多位置更新似乎不正确地实现此目的 如何做呢 用户 kk8qFOIw 是正在删除其帐户的用
  • 如何追加到 R 中的现有文件而不覆盖它?

    我想写入一个文件 然后在循环中多次追加它 在 Windows 机器上 每次追加后 我想关闭连接 因为我希望该文件存入保管箱帐户 以便我可以在代码运行时在其他计算机上打开它 以检查日志文件的状态 注意 这个条件使得这个问题不同于SO上关于si
  • Dagger 2 构建 IllegalArgumentExceptioncompileDebugJavaWithJavac

    我一直在测试 Dagger 2 一切都正常 直到我做了一些重构 现在 gradle 正在抛出一个IllegalArgumentException 而且我无法弄清楚我所做的更改现在导致了错误 我没有对 gradle 文件进行任何更改 这似乎是
  • r 沿着向量搜索并计算平均值

    我的数据看起来像 require data table DT lt data table x c 19 19 19 21 21 19 19 22 22 22 y c 53 54 55 32 44 45 49 56 57 58 我想沿着 x
  • 自动将 GitHub 分支部署到 AWS Elastic Beanstalk

    说我有一个分支stable在 GitHub 上 我希望在提交时自动部署到我的 AWS EB 实例 我看过CodePipeline 这在我托管实例的区域不可用 我也看过CodeDeploy但这似乎仅适用于 EC2 的单个实例 而不适用于Ela
  • 骆驼http端点动态形成url

    我正在尝试使用的伙计们 from direct a to someUrl processor new Processor Override public void process Exchange arg0 throws Exception
  • chrome 扩展,每 x 分钟执行一次

    我只是做一个简单的 chrome 扩展 我希望我的后台页面 或部分 每 5 分钟执行一次 以获取一些数据并显示桌面通知 如果有 我该如何执行此操作 重要提示 如果您使用非持久后台脚本 Manifest V3service worker或清单
  • 使用“memcpy”复制二维数组在技术上是未定义的行为吗?

    评论中出现了一个有趣的讨论最近的这个问题 https stackoverflow com q 69329303 10871073 现在 虽然有语言C 讨论已经转向什么C 标准指定了使用以下函数访问多维数组的元素时构成未定义行为的内容std