访问二维数组的一行末尾之后的元素是否是 UB?

2024-04-29

以下程序的行为是否未定义?

#include <stdio.h>

int main(void)
{
    int arr[2][3] = { { 1, 2, 3 },
                      { 4, 5, 6 }
    };

    int *ptr1 = &arr[0][0];      // pointer to first elem of { 1, 2, 3 }
    int *ptr3 = ptr1 + 2;        // pointer to last elem of { 1, 2, 3 }
    int *ptr3_plus_1 = ptr3 + 1; // pointer to one past last elem of { 1, 2, 3 }
    int *ptr4 = &arr[1][0];      // pointer to first elem of { 4, 5, 6 }
//    int *ptr_3_plus_2 = ptr3 + 2; // this is not legal

    /* It is legal to compare ptr3_plus_1 and ptr4 */
    if (ptr3_plus_1 == ptr4) {
        puts("ptr3_plus_1 == ptr4");

        /* ptr3_plus_1 is a valid address, but is it legal to dereference it? */
        printf("*ptr3_plus_1 = %d\n", *ptr3_plus_1);
    } else {
        puts("ptr3_plus_1 != ptr4");
    }

    return 0;
}

根据§6.5.6 ¶8 http://port70.net/~nsz/c/c11/n1570.html#6.5.6p8:

此外,如果表达式P指向最后一个元素 数组对象,表达式(P)+1过去了最后一点 数组对象的元素...如果指针操作数和 结果指向同一个数组对象的元素,或者过去的一个 数组对象的最后一个元素,评估不应产生 溢出;否则,行为是未定义的。如果结果点 超过数组对象的最后一个元素后,它不得用作 被求值的一元 * 运算符的操作数。

由此可见,上述程序的行为是未定义的;ptr3_plus_1指向派生它的数组对象末尾之后的地址,取消引用该地址会导致未定义的行为。

更远,附件J.2 http://port70.net/~nsz/c/c11/n1570.html#J.2表明这是未定义的行为:

数组下标超出范围,即使对象明显是 使用给定的下标可访问(如左值表达式中所示)a[1][7]鉴于声明整数a[4][5])(6.5.6)。

Stack Overflow 问题中有一些关于这个问题的讨论,对多维数组的一维访问:定义良好的 C? https://stackoverflow.com/questions/6290956/one-dimensional-access-to-a-multidimensional-array-well-defined-c。这里的共识似乎是这种访问随意的通过一维下标获取二维数组的元素确实是未定义的行为。

在我看来,问题在于形成指针的地址甚至是不合法的ptr3_plus_2,因此以这种方式访问​​任意二维数组元素是不合法的。但它is构成指针地址是合法的ptr3_plus_1使用这个指针算术。此外,比较两个指针是合法的ptr3_plus_1 and ptr4, 根据§6.5.9 ¶6 http://port70.net/~nsz/c/c11/n1570.html#6.5.9p6:

两个指针比较相等当且仅当两个指针都是空指针时 是指向同一个对象的指针(包括指向对象的指针和 开头的子对象)或函数,两者都是指向一个的指针 经过同一数组对象的最后一个元素,或者一个是指针 一个指向一个数组对象的末尾,另一个是指向 立即发生的不同数组对象的开始 跟随地址空间中的第一个数组对象。

所以,如果两者兼而有之ptr3_plus_1 and ptr4是比较相等的有效指针,并且必须指向相同的地址(由ptr4在内存中必须与所指向的对象相邻ptr3无论如何,由于数组存储必须是连续的),看起来*ptr3_plus_1与以下一样有效*ptr4.

这是未定义的行为,如第 6.5.6 节 ¶8 和附件 J.2 中所述,还是特殊情况?

澄清

尝试访问超过末尾的元素是未定义的行为,这似乎很明确final二维数组的行。我感兴趣的是通过使用指向前一行元素的指针和指针算术形成新指针来访问中间行的第一个元素是否合法的问题。在我看来,附件 J.2 中的另一个例子可以使这一点更加清楚。

是否可以协调第 6.5.6 节中的明确声明¶8 尝试取消引用指向超出数组末尾的位置的指针会导致未定义的行为,而指针超出第一行末尾的想法类型的二维数组T[][]也是一个类型的指针T *指向一个类型的对象T,即类型数组的第一个元素T[]?


所以,如果两者兼而有之ptr3_plus_1 and ptr4是比较相等且必须指向同一地址的有效指针

他们是。

看来*ptr3_plus_1与以下一样有效*ptr4.

它不是。

指针相等,但不等效。区分相等和等价的一个众所周知的例子是负零:

double a = 0.0, b = -0.0;
assert (a == b);
assert (1/a != 1/b);

现在,公平地说,两者之间存在差异,因为正零和负零有不同的表示形式,ptr3_plus_1 and ptr4典型的实现具有相同的表示。这是不能保证的,并且在它们具有不同表示的实现上,很明显您的代码可能会失败。

即使在典型的实现上,虽然有很好的论据表明相同的表示意味着相同的值,但据我所知,官方的解释是该标准不能保证这一点,因此程序不能依赖它,因此实现可以假设程序不这样做并进行相应的优化。

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

访问二维数组的一行末尾之后的元素是否是 UB? 的相关文章

随机推荐

  • 如何在批处理中返回数组的元素?

    我的程序中的数组列表中有两个元素 如何将变量分配给等于其中一个元素 这是代码 echo off setlocal enabledelayedexpansion set p string for l a in 0 1 1000 do if n
  • 在 Windows 上实现堆栈跟踪 [关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我正在为我正在编写的游戏实现一个崩溃报告工具 并且我想为该报告提供 相当 详细的本机堆栈跟踪 我已经在 GNU Linux 上实现
  • Firebase通过时间戳获取数据

    我需要使用过滤数据来获取时间戳匹配的特定数据 例如我需要数据在哪里arrivalTime与数据库中的精确日期和时间字段 时间戳字段匹配 我正在尝试下面 但它没有返回任何数据 arrivalTIme moment todaysDate for
  • 如何避免MySQL'尝试获取锁时发现死锁;尝试重新启动交易'

    我有一个innoDB表 记录在线用户 它会在用户每次刷新页面时进行更新 以跟踪他们所在的页面以及他们上次访问该网站的日期 然后我有一个每 15 分钟运行一次的 cron 来删除旧记录 我收到 尝试获取锁定时发现死锁 昨晚尝试重新启动事务大约
  • BigQuery GitHub 数据:如何处理存储库名称更改?

    我的目标是跟踪我的仓库的星星总数 然而 它的 repo name 随着时间的推移而改变 如何实现这一目标githubarchive数据集 相关https stackoverflow com a 42930963 132438 https s
  • 创建适配器映像时无法应用对象中的 object()

    我正在创建适配器映像 但遇到以下 2 个错误 这是代码 public class GridViewAdapter private Context mcontext private int layoutResourceId public Gr
  • 从 iBeacon 接收 BLE 信号到 Bluno(arduino with BLE)

    我想从 iBeacon 到 Bluno 接收 rssi 信号和 UUID Arduino 板具有 BLE 对此有一些疑问 有没有从 BLE 到 BLE 接收 UUID 和 rssi 的解决方案 两个BLE设备可以互相通信吗 我想找一些网站来
  • 检索 Couchbase 的所有记录(文档)

    我正在使用 node js 并寻找一种方法来获取特定的 couchbase 桶的所有文档 有没有没有循环和增量索引的解决方案 我知道我可以制作一个原子键 然后通过循环使用它来检索所有数据 但我需要一个返回所有文档的函数 是否有任何函数 至少
  • 自动播放视频的 canvas.drawimage 仅在视频元素可见时有效

    我试图通过将视频绘制到画布上来在视频上添加一些滤镜 问题是 当视频元素不在视图中时 它会停止绘制 理想情况下 我想将视频元素全部隐藏起来 我认为它只影响 Chrome 浏览器 另外 似乎如果您停止并用鼠标启动它 问题就会消失 functio
  • *ngIf 中的@ViewChild

    Question 最优雅的获取方式是什么 ViewChild显示模板中的相应元素后 下面是一个例子 还Plunker http plnkr co edit xAhnVVGckjTHLHXva6wp p preview可用的 组件 templ
  • mod_mono 在新安装的 centos 上出现 EOF 错误

    我全新安装了 Centos 6 3 已完全更新 我已经从源安装了 mono xsp 和 mod mono 每个包都完美编译 它们都以 usr local mono 前缀安装 因此所有内容都位于 usr local mono 下 我已将 In
  • IntelliJ & JRuby:如何设置项目?

    我已经下载了 IntelliJ 13 的试用版 并安装了适用于 Windows 的最新 JRuby 版本 我在网上进行了彻底搜索 但无法找到有关如何在 IntelliJ 中设置 JRuby 项目的任何指导 我选择了 IntelliJ 而不是
  • 修正增量函数的摊余成本

    因此 对于 n 位二进制字符串 A 0 n 1 其中 A 0 是最低有效位 A n 1 是最高有效位 增量算法为 Increment A n i 0 while i
  • 如何使用 FLUTTER go_router 弹出上下文?

    如何使用 flutter go router 返回上一个屏幕 如何弹出上下文 目前 我只是将一个新屏幕添加到堆栈中 无论我想返回还是前进 onTap gt context go secondPage 我用过 context pop 但它会抛
  • 在 Symfony2 中使用 json 数据水合实体

    有没有办法用 Symfony2 中传入请求的 json 数据来水合实体 我以为有类似的东西 Form bindRequest 但我找不到任何东西 如果能将这个功能与 knockout js 等库一起使用 那就太好了 如果您需要从 JSON
  • 从对话框调用 CustomAction 时出现 WiX 安装错误 2762

    我是初学者 开始学习WiX 我想在安装过程中捕获 验证和注册用户详细信息 我创建了一个对话框来捕获用户注册并在用户单击 下一步 后调用自定义操作 但在这里我收到安装程序错误 2762 虽然错误描述说 必须在 InstallInitializ
  • 通过 HTTP 更新自托管扩展

    我正在将基于 SDK 的 Firefox 扩展转换为 WebExtensions 并且遇到了更新扩展的问题 当前的扩展托管在我自己的域 这是一个 HTTP 域 上 以及更新 rdf file 现在 对于基于 SDK 的附加组件 只要使用以下
  • 未捕获的错误:地图容器已初始化

    我正在使用 React JS 制作网页 我的目标是在前端显示地图 我正在使用react leaflet npm 包来实现同样的目的 但是 我收到以下错误 Error Uncaught Error Map container is alrea
  • 如何防止通过“new”运算符分配类? (我想确保我的 RAII 类始终分配在堆栈上。)

    我想确保我的 RAII 类始终分配在堆栈上 如何防止通过 new 运算符分配类 您需要做的就是将类的 new 运算符声明为私有 class X private Prevent heap allocation void operator ne
  • 访问二维数组的一行末尾之后的元素是否是 UB?

    以下程序的行为是否未定义 include