允许结构字段溢出到下一个字段

2024-06-28

考虑以下简单示例:

struct __attribute__ ((__packed__)) {
 int code[1];
 int place_holder[100];
} s;

void test(int n)
{
 int i;

 for (i = 0; i < n; i++) {
  s.code[i] = 1;
 }
}

for 循环正在写入字段code,其大小为 1。之后的下一个字段code is place_holder.
我希望在这种情况下n > 1,写入code数组会溢出并且1将被写入place_holder.

但是,当编译时-O2(在 gcc 4.9.4 上,但也可能在其他版本上)发生了一些有趣的事情。
编译器识别出代码可能溢出数组code, and 将循环展开限制为 1 次迭代.

编译时很容易看出-fdump-tree-all并查看最后一个树通道(“t.optimized”):


;; Function test (test, funcdef_no=0, decl_uid=1366, symbol_order=1)

Removing basic block 5
test (int n)
{
  <bb 2>:
  # DEBUG i => 0
  # DEBUG i => 0
  if (n_4(D) > 0)
    goto <bb 3>;
  else
    goto <bb 4>;

  <bb 3>:
  s.code[0] = 1;
  # DEBUG i => 1
  # DEBUG i => 1

  <bb 4>:
  return;

}

因此,在这种情况下,编译器将循环完全展开为一次迭代。

我的问题是:

  1. 从 C 规范的角度来看,从一个结构成员(故意)溢出到下一个是非法或未定义的行为吗?
    假设我知道内存中的结构布局,并且知道在故意溢出时我在做什么code array.
  2. 在这种情况下有没有办法阻止 gcc 展开循环?我知道我可以完全阻止循环展开,但是我仍然对其他情况下的循环展开感兴趣。我还怀疑编译器正在进行的分析可能会影响除循环展开之外的传递。
    gcc 假设我在访问数组时不会溢出,所以我真正寻找的是告诉编译器不要采用这种假设的方法(通过提供一些编译器选项)。

我知道编写从一个字段溢出到另一个字段的代码是一种不好的做法,而且我不打算编写这样的代码。
我还知道将数组(可能大小为零)作为last结构体字段允许它溢出,编译器很好地支持这一点,而在这种情况下数组code不是最后一个字段。
所以这不是一个“如何修复代码”的问题,而是一个理解编译器假设并影响它们的问题。

当我观察已经以这种方式编写的现有代码并对其进行调试以找出其行为与原始开发人员期望的行为不同的原因时,出现了这些问题。
风险在于代码中的其他地方也存在此类问题。静态分析工具可以帮助找出答案,但我也想知道是否有一种方法可以使编译器容忍此类代码并仍然生成我们期望的结果。

Update

我对上面的问题(1)得到了明确的答案,但对问题(2)却没有得到明确的答案。

  • gcc 可以通过某些编译选项允许将此作为​​扩展吗?
  • 有没有办法至少在 gcc 识别它时收到警告? (并且它通过优化来清楚地识别它)。
    这对于在大型现有代码库中识别此类情况非常重要。

从 C 规范的角度来看,从一个结构成员(故意)溢出到下一个是非法或未定义的行为吗?

这是未定义的行为。这arr[i]运算符是语法糖*(arr + i)。所以数组访问归结为二进制+指针算术运算符,C17 6.5.6 加法运算符,来自 §7 和 §8:

就这些运算符而言,指向不是元素元素的对象的指针 数组的行为与指向长度为 1 的数组的第一个元素的指针相同 对象的类型作为其元素类型。

当一个整数类型的表达式与指针相加或相减时, 结果具有指针操作数的类型。 /--/
如果两个指针 操作数和结果指向同一个数组对象的元素,或者指向最后一个元素 数组对象的元素,求值不得产生溢出;否则, 行为未定义。 如果结果指向数组对象的最后一个元素,则它 不得用作所求值的一元 * 运算符的操作数。

正如您所注意到的,优化编译器可能会利用这些规则来生成更快的代码。


在这种情况下有没有办法阻止 gcc 展开循环?

有一个可以使用的特殊例外规则,C17 6.3.2.3/7:

当指向对象的指针转换为指向字符类型的指针时, 结果指向对象的最低寻址字节。连续递增 结果,直到对象的大小,产生指向对象的剩余字节的指针。

此外,严格别名不适用于字符类型,因为 C17 6.5 §7 中的另一个特殊规则

对象的存储值只能由具有以下之一的左值表达式访问 以下类型: ... 字符类型。

这两种特殊规则和谐共存。因此,假设我们在指针转换期间不会弄乱对齐等,这意味着我们可以这样做:

unsigned char* i;
for(i = (unsigned char*)&mystruct; i < (unsigned char*)(&mystruct + 1); i++)
{
  do_something(*i);
}

然而,这可能会读取填充字节等,因此它是“实现定义的”。但理论上,您可以按字节访问结构体字节,并且只要按字节计算结构体偏移量,您就可以以这种方式迭代结构体(或任何其他对象)的多个成员。


据我所知,这个看起来非常有问题的代码应该是明确定义的:

#include <stdint.h>
#include <string.h>
#include <stdio.h>

struct __attribute__ ((__packed__)) {
 int code[1];
 int place_holder[100];
} s;

void test(int val, int n)
{
  for (unsigned char* i = (unsigned char*)&s; 
       i < (unsigned char*)&s + n*sizeof(int); 
       i += _Alignof(int)) 
  {
    if((uintptr_t)i % _Alignof(int) == 0) // not really necessary, just defensive prog.
    {
      memcpy(i, &val, sizeof(int));
      printf("Writing %d to address %p\n", val, (void*)i);
    }
  }
}

int main (void)
{
  test(42, 3);
  printf("%d %d %d\n", s.code[0], s.place_holder[0], s.place_holder[1]);
}

这在 gcc 和 clang (x86) 上运行良好。至于效率如何,那就是另一个故事了。但请不要编写这样的代码。

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

允许结构字段溢出到下一个字段 的相关文章

  • 预加载整个模型以估计 Tensorflow Serving 的内存消耗

    当执行预测时 Tensorflow Serving 延迟初始化模型 DAG 中的节点 这使得很难估计保存整个模型所需的内存 RAM 是否有一种标准方法可以强制 Tensorflow Serving 将模型完全初始化 加载到内存中 您可以使用
  • 反序列化 GUID 数组时出现 JSON.NET 异常

    我正在使用 JSON NET 反序列化从浏览器发送的 AJAX HTTP 请求 并且在使用 Guid 作为参数的 Web 服务调用时遇到问题 当我使用内置的 NET 序列化器时 这工作得很好 首先 流中的原始字节如下所示 System Te
  • 从 WatchOS 捕获 Apple Watch 的型号标识符

    似乎没有任何记录的官方方法可以从手表应用程序获取 Apple Watch 模型 但有一篇文章显示了特殊用途sysctlbyname 如何确定Apple Watch型号 https stackoverflow com questions 49
  • boost::noncopyable 是如何工作的

    我在 C 实践中偶然发现了两个愚蠢的问题 据我所知 c 中的复制构造函数和赋值运算符不是继承的 那么 boost noncopyable 在这种情况下如何帮助禁止这些东西呢 class X private boost noncopyable
  • 在 C 中使用相等运算符 == 比较两个字符串是否相等 [重复]

    这个问题在这里已经有答案了 int main int argc argv if argv 1 hello printf True n else printf False n myProg hello False 为什么 我意识到strcmp
  • 共享库 C++ Makefile

    我需要编译胖二进制文件才能在另一台 Linux 机器上使用它 但缺少一些库 因此据我了解 我应该使用一些 shared 选项来编译它 但我不明白如何为此配置 Makefile 目前我的 makefile 如下所示 CC g CC FLAGS
  • C# 中的 memcpy 函数 [重复]

    这个问题在这里已经有答案了 可能的重复 C memcpy 等效项 https stackoverflow com questions 510971 c memcpy equivalent 相当于什么memcpyC 中的函数 正如已经说过的
  • Unix 编程。不确定如何使用 passwd 结构

    我做了一些研究 但仍在努力解决passwd结构 http www opengroup org onlinepubs 000095399 basedefs pwd h html http www opengroup org onlinepub
  • StreamReader 的默认值是多少?

    我需要使用这个构造函数public StreamReader Stream stream Encoding encoding bool detectEncodingFromByteOrderMarks int bufferSize bool
  • CMake include_directories 和 add_subdirectories 之间的区别?

    我正在学习 CMake 来构建 C 代码 并努力解决以下概念 在我的根级目录中 我有一些 cpp 文件和 CMakeLists txt 它们在 gen cpp 目录中成功生成了一些 thrift 代码 我的根级别 CMakeLists tx
  • NHibernate 继承 - 判别器值

    NHibernate 是否可以有一个像这样工作的判别器 如果值等于 String Empty gt Class1 其他 gt Class2 我已经有一个 CultureName 字符串列 我想将其用作鉴别器 我不想添加额外的布尔列 如果 C
  • 开始学习 C# 的最佳方式是什么?

    我对 vb 6 有一点编程经验 而 vb net 则不多 请告诉我成为专家 C 程序员的最佳方法 我知道这需要很长时间 想想你如何学习人类语言 阅读 写作 口语和听力 阅读代码 阅读文章 阅读示例 当您更有经验时 请查看您使用的一些项目的源
  • 在 Silverlight 中调用 WCF 服务时使用 Observable.FromEvent

    我正在尝试使用 NET Reactive Framework 来简化对我正在编写的 Silverlight 3 应用程序使用的 WCF 服务的一些异步调用 问题是我很难找到一种有效的方式来构建我的代码 毫无疑问 部分问题是了解 Reacti
  • 在 Linux 上用 C 跟踪键盘和鼠标事件

    如何在 Linux 中用 C 语言跟踪键盘或鼠标事件 Like for example if the user presses ESC Shift etc I should be able to track it Same way for
  • Microsoft Build Tools 2013 缺少 v120 目录

    我们已经安装了 Microsoft Build Tools 2013 从http www microsoft com en us download details aspx id 40760 http www microsoft com e
  • 当调试器退出且没有调用堆栈时,用于跟踪 C# 崩溃的工具?

    我有一个大型 复杂的 C GUI 应用程序 它以完全可重现的方式崩溃 但我无法轻松诊断崩溃的原因 因为调试会话不是以通常的方式使用调用堆栈破坏调试器 而是完全退出 唯一的提示是输出窗口末尾有一条消息 STATUS STACK BUFFER
  • 如何使用 Dictionary 属性序列化对象?

    在下面的示例代码中 我得到了这个error 元素 TestSerializeDictionary123 Customer CustomProperties 呕吐型 System Collections Generic Dictionary
  • .NET Compact Framework 上的 DateTime.Now 中的毫秒始终为零?

    我想要一个时间戳对于日志Windows 移动项目 精度必须至少在一百毫秒范围内 然而我打电话给DateTime Now返回一个DateTime对象与Millisecond属性设置为零 还有Ticks属性相应地进行四舍五入 如何获得更好的时间
  • 如何获取字典元素的相对位置?

    我有下一个 C 代码 Dictionary
  • 用于 DSP 的快速 2D 卷积

    我想实现一些图像处理算法 这些算法旨在运行在小猎犬板 http en wikipedia org wiki Beagle Board 这些算法广泛使用卷积 我正在尝试为 2D 卷积找到一个好的 C 实现 可能使用快速傅里叶变换 我还希望该算

随机推荐