通过其他结构体成员的偏移指针访问结构体成员是否合法?

2024-04-24

在这两个示例中,通过偏移其他成员的指针来访问结构体成员是否会导致未定义/未指定/实现定义的行为?

struct {
  int a;
  int b;
} foo1 = {0, 0};

(&foo1.a)[1] = 1;
printf("%d", foo1.b);


struct {
  int arr[1];
  int b;
} foo2 = {{0}, 0};

foo2.arr[1] = 1;
printf("%d", foo2.b);

C11 § 6.7.2.1 第 14 段似乎表明这应该是实现定义的:

结构或联合对象的每个非位域成员都以适合其类型的实现定义方式对齐。

然后接着说:

结构对象内可能有未命名的填充,但不是在其开头。

然而,像下面这样的代码似乎相当常见:

union {
  int arr[2];
  struct {
    int a;
    int b;
  };
} foo3 = {{0, 0}};

foo3.arr[1] = 1;
printf("%d", foo3.b);

(&foo3.a)[1] = 2; // appears to be illegal despite foo3.arr == &foo3.a
printf("%d", foo3.b);

该标准似乎保证foo3.arr是相同的&foo3.a,并且以一种方式引用它是合法的而另一种方式不合法是没有意义的,但同样,将外部联合与数组相加应该突然使(&foo3.a)[1] legal.

因此,我认为第一个例子也必须是合法的:

  1. foo3.arr保证与&foo.a
  2. foo3.arr + 1 and &foo3.b指向同一个内存位置
  3. &foo3.a + 1 and &foo3.b因此必须指向相同的内存位置(来自 1 和 2)
  4. 结构布局需要一致,所以&foo1.a and &foo1.b应该布置得与&foo3.a and &foo3.b
  5. &foo1.a + 1 and &foo1.b因此必须指向相同的内存位置(来自 3 和 4)

我发现一些外部消息来源表明foo3.arr[1] and (&foo3.a)[1]示例是非法的,但是我无法在标准中找到可以实现这一点的具体声明。 即使它们都是非法的,也可以使用灵活的数组指针构造相同的场景,据我所知,does具有标准定义的行为。

union {
  struct {
    int x;
    int arr[];
  };
  struct {
    int y;
    int a;
    int b;
  };
} foo4;

原始应用程序正在考虑从一个结构体字段到另一个结构体字段的缓冲区溢出是否严格来说是由标准定义的:

struct {
  char buffer[8];
  char overflow[8];
} buf;
strcpy(buf.buffer, "Hello world!");
println(buf.overflow);

我希望这个输出"rld!"几乎在任何现实世界的编译器上,但是此行为是由标准保证的,还是未定义或实现定义的行为?


介绍:该领域的标准是不够的,并且关于该主题和严格别名的争论已有数十年的历史,没有令人信服的解决方案或建议来解决。

这个答案反映了我的观点,而不是该标准的任何强加。


首先:人们普遍认为第一个代码示例中的代码是未定义的行为,因为通过直接指针算术访问数组的边界之外。

规则是 C11 6.5.6/8 。它表示指针的索引必须保留在“数组对象”内(或超出末尾的一个)。它没有说which数组对象,但普遍认为在这种情况下int *p = &foo.a;那么“数组对象”是foo.a,而不是任何更大的物体foo.a是一个子对象。

相关链接:one https://stackoverflow.com/questions/47224138/is-it-ok-to-access-past-the-size-of-a-structure-via-member-address-with-enough/47224596#47224596, two https://stackoverflow.com/questions/48147422/is-it-legal-to-alias-a-struct-and-an-array.


其次:人们普遍认为你们俩union例子是正确的。该标准明确规定联盟的任何成员都可以被读取;无论相关内存位置的内容是什么,都将被解释为正在读取的联合成员的类型。


你建议union正确意味着第一个代码也应该是正确的,但事实并非如此。问题不在于指定读取的内存位置;而在于指定读取的内存位置。问题在于我们如何得出指定该内存位置的表达式。

尽管我们知道&foo.a + 1 and &foo.b是相同的内存地址,访问是有效的int通过第二个并且无法有效访问int通过第一个。

人们普遍认为,您可以通过不违反 6.5.6/8 规则的其他方式计算其地址来访问 int,例如:

((int *)((char *)&foo + offsetof(foo, b))[0]

or

((int *)((uintptr_t)&foo.a + sizeof(int)))[0]

相关链接:one https://stackoverflow.com/questions/5524552/, two https://stackoverflow.com/questions/37412887/


It's not普遍同意是否((int *)&foo)[1]已验证。有人说它与您的第一个代码基本相同,因为标准说“指向对象的指针,经过适当转换,指向元素的第一个对象”。别人说和我的基本一样(char *)上面的例子是因为它遵循指针转换的规范。有些人甚至声称这是严格的别名违规,因为它将结构别名为数组。

也许相关的是N2090 - 指针来源提案 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2090.htm。这并不直接解决该问题,也不建议废除 6.5.6/8。

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

通过其他结构体成员的偏移指针访问结构体成员是否合法? 的相关文章

随机推荐

  • 如何确定主线程上运行的内容 + 减慢 UI 速度?

    我在我的应用程序中添加了新的数据加载功能 它的目的是将大型数据库的内容从移动设备传输到后端并进行处理 在我在此管道中运行的每个函数中 该函数的全部内容都在一个 dispatch async 这会分派到非主线程 我还通过日志验证了这些功能是否
  • 自动增量不适用于具有复合键的实体类

    我希望我的复合键元素之一自动递增 并且我正在为实体类使用嵌入键 因此我无法使用生成值注释来解决问题 因为 GeneratedValue使用时注释被忽略 Embeddable or EmbeddedId 你不能使用 GeneratedValu
  • DC.js 到 React 的转换

    有 dc js 反应转换的示例吗 对折线图 表格和时间滑块 带画笔的条形图 感兴趣 任何帮助 将不胜感激 Thanks 这是将 DC js 转换为 React 的开始 使用的库的版本如下 package json dependencies
  • Visual Studio 调试器提示和技巧(适用于 C/C++ 项目)

    我对有关在 Visual Studio 调试器中调试 C C 项目的提示和技巧感兴趣 我最近发现 如果您有一个指向某种数据类型的指针 比如说 char ptr 那么您可以使用以下语法在监视窗口中将其视为数组 ptr 10 这将显示 ptr
  • 从安装项目重新启动系统

    我正在使用 Visual Studio 创建一个安装项目 我想在安装成功完成后重新启动系统 我想要一次的努力 我不想每次在将设置交付给客户之前使用任何外部工具 实用程序修改设置 我可以调整安装项目本身吗 我怎样才能做到这一点 您可以使用 O
  • MatSort 未定义 - Angular 5

    我正在尝试在我的角度应用程序中实现材质表 分页和过滤器工作正常 但我无法对表格进行排序 我对 MatSort 的引用是未定义的 我确实将其导入到 AppModule 中 import MatTableModule from angular
  • 安卓版本设置

    我希望我的 Xamarin Android 应用程序支持 API 级别 16 及更高级别 但我对 Visual Studio 的属性窗格中存在三种不同的版本设置感到困惑 属性的名称是 Compile using Android versio
  • (Windows Phone 10) 是否可以在 Windows Phone 10 中以编程方式编辑、添加新联系人?

    我想在 Windows Phone 10 中以编程方式实现功能编辑和添加联系人 是否可以 有相关样本吗 以下是用于创建联系人的代码片段 public async Task AddContact String FirstName String
  • 加盐哈希 - 为什么文献中将盐视为夏娃已知的盐?

    标题说明了一切 我不明白 为什么你不应该像密码一样保密你的盐 或者我误解了什么 盐被视为公开的主要是因为没有必要保密 盐的目的主要是使字典攻击变得更加困难 不太实用 在字典攻击中 攻击者对字典中的常用单词进行哈希处理 并 如果他是认真的 用
  • 什么时候适合使用关联类型而不是泛型类型?

    In 这个问题 https stackoverflow com q 32053402 155423 出现了一个问题 可以通过将使用泛型类型参数的尝试更改为关联类型来解决 这引发了一个问题 为什么关联类型在这里更合适 这让我想了解更多 The
  • 如何将文件从 S3 下载到 iPhone 应用程序?

    我是 iPhone 开发新手 我正在开发一个 iPhone 应用程序 需要打开存储在 Amazon S3 服务上的文件 如何将文件从 S3 下载到我的 iPhone 应用程序 我尝试过亚马逊的SDK 但他们似乎没有提供下载和保存文件的方法
  • PHP 输出缓冲回调产生空白屏幕

    这是代码 ob start array dispatcher outputCallback include file ob end flush function outputCallback string if ob get level 1
  • Nodejs Hapi - 如何启用跨源访问控制

    我正在使用 HapiJs Restful Web 服务并尝试启用 cors 以便任何来自不同域的客户端都可以使用我的服务 我在服务器连接对象中尝试了 cors true 但没有成功 你放在哪里cors true 你能添加一些代码吗 不知道你
  • 如何将 BYTE 数组转换为 char 数组以使用套接字 C++ 发送

    我有一些问题 我编写客户端服务器应用程序 其中使用 win 套接字发送和接收信息 在我的项目中需要发送BYTE数组到客户端 并在客户端上将此 char 数组转换为BYTE 我该如何创建它 请帮助我 因为send 函数只能发送char 谢谢
  • 替换精灵中的图像-iPhone的cocos2d游戏开发

    我想改变精灵图像 举例来说 mainSprite Sprite spriteWithFile redFile png self addChild mainSprite 在这里 Sprite 已经添加到图层中 我有可以访问它的 mainSpr
  • x86 实模式下的段大小

    我对实模式下段的大小有一个疑问 因为它们不能超过 64K 但可以小于那个 http support microsoft com kb 120069 我的问题是这些段大小和基地址是如何初始化的 就像保护模式下有 GDT 和 LDT 一样 实模
  • “发生了致命的 JavaScript 错误。您想发送错误报告吗?”在 phpmyadmin 上

    几天前 我按照网站上的教程将 phpMyAdmin 升级到最新版本 删除并替换 Xampp 中 phpMyAdmin 文件夹的所有内容 然后替换为最新版本 然后将旧版本config inc php再次 升级后 我经常收到错误消息和错误代码
  • 在不同的翻译单元中启动全局变量(涉及链接器)

    最近在复习C 这是我的问题 请参阅中的代码file1 cpp and file2 cpp 文件1 cpp int x1 1 int y1 x1 2 文件2 cpp include necessary headers extern int y
  • 如何使用spark-submit的--properties-file选项在IntelliJ IDEA中启动Spark应用程序?

    我正在启动一个使用 Scala 和 IntelliJ IDE 开发的 Spark 项目 我想知道如何设置 properties fileIntelliJ 配置中 Spark 的具体配置 我正在阅读这样的配置 param1 gt sc get
  • 通过其他结构体成员的偏移指针访问结构体成员是否合法?

    在这两个示例中 通过偏移其他成员的指针来访问结构体成员是否会导致未定义 未指定 实现定义的行为 struct int a int b foo1 0 0 foo1 a 1 1 printf d foo1 b struct int arr 1