当前位置: 首页 > 知识库问答 >
问题:

编译器指令重新排序

胡泓
2023-03-14
struct Vec
{
    double x, y, z;
};

Vec vec;
vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
double res = (&vec.x)[2];    // (&vec.x)[2] should be equal to vec.z

变量res的值应等于3。但是当我打开优化时,编译器错误地重新排列了指令,并且res包含一些垃圾。一些可能的重新排序示例:

vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
res = (&vec.x)[2];    // correct value

vec.x = 1.0;
vec.y = 2.0;
res = (&vec.x)[2];    // incorrect value
vec.z = 3.0;

vec.x = 1.0;
res = (&vec.x)[2];    // incorrect value
vec.y = 2.0;
vec.z = 3.0;

这是编译器中的错误吗?还是不允许像这样访问结构数据成员?

编辑:

我刚刚意识到之前的代码实际上有效,抱歉。但这不起作用:

Vec vec;
vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
double res = (&vec.x)[i];    // (&vec.x)[i] should be equal to vec.z when i == 2

编译时不知道变量i时,编译器会错误地重新排序指令。

共有3个答案

谷泽宇
2023-03-14

我最终找到了一个解决方案,可以防止指令重新排序并且不会增加Vec结构的大小:

struct Vec
{
    union
    {
        struct
        {
            double x, y, z;
        };
        double data[3];
    };
};

Vec vec;
vec.x = 1.0;
vec.y = 2.0;
vec.z = 3.0;
double res = vec.data[i];

谢谢你的回答。

蓬琦
2023-03-14

除了未定义的行为之外,请注意,C并未强制将它们彼此相邻分配。如果您想通过索引和名称引用它们,我建议采取其他方式:

struct Vec
{
    double elems[3];
    double& x;
    double& y;
    double& z;

    Vec()
    : x( elems[0] ), y( elems[1] ), z( elems[2] )
    {
    }
};

如果您负担不起每个对象可能(但不是必要的)额外空间,您不妨将x、y、z声明为返回引用的函数。

赏成益
2023-03-14

您正在调用未定义的行为<代码>(

当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型。如果指针操作数指向数组对象84的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果和原始数组元素的下标差等于整数表达式。换句话说,如果表达式P指向数组对象的第i个元素,则表达式(P)N(等价地,N(P))和(P)-N(其中N的值为n)分别指向数组对象的第i个第n个和第i−n个元素,前提是它们存在。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)1指向数组对象的最后一个元素,如果表达式Q指向数组对象的最后一个元素,则表达式(Q)-1指向数组对象的最后一个元素。如果指针操作数和结果都指向同一个数组对象的元素,或者数组对象的最后一个元素之后的元素,则计算不应产生溢出;否则,行为未定义。

另见上文附注84:

84)为此目的,不是数组元素的对象被认为属于单元素数组;参见5.3.1。

由于vec. x不是数组,因此被认为是单元素数组的元素。因此vec. xvec. z不是同一个数组的元素,行为未定义。

 类似资料:
  • 问题内容: 我已经反编译了一个jar(可能出于调试目的)并希望重新编译它。 我已经使用了JAD和JD,并且由于goto指令而无法编译 例如 有没有简单的方法(或任何方法)来重新编译它?(除了手动重构和删除的外) 问题答案: 不,没有。在您的情况下,JD和JAD都无法正确地反编译类文件。“ goto”语句是有关实际程序流程的提示。 您可以直接与Java Decompiler团队联系,发送一个类文件,

  • 问题内容: 我正在阅读此博客文章。 作者正在谈论在多线程环境中打破in 。 有了: 变成: 作者说,我引用: “我在这里所做的是添加一个附加读取: 哈希的第二次读取,在返回之前 。听起来很奇怪,而且不太可能发生,第一次读取可以返回正确计算出的哈希值,内存模型允许第二次读取返回0!这是允许的,因为该模型允许对操作进行广泛的重新排序。第二次读取实际上可以在代码中移动,以便处理器在第一次读取之前进行处理

  • 我使用maven命令来清理、构建整个项目、创建war并部署到服务器。我不能使用Intellij来做这件事,因为我只有社区版。它在与intellij相同的目录中构建项目。 为了加快速度,我编写了一个脚本,可以在本地“target”目录中找到比服务器中更新的已编译文件,并进行复制。虽然一切正常,但问题是Intellij并没有将使用maven编译的类视为应该跳过并重新构建整个项目的类。 目前它的工作原理

  • 主要内容:实例,实例,实例,实例,实例,实例,实例以反引号 ` 开始的某些标识符是 Verilog 系统编译指令。 编译指令为 Verilog 代码的撰写、编译、调试等提供了极大的便利。 下面介绍下完整的 8 种编译指令,其中前 4 种使用频率较高。 `define, `undef 在编译阶段,`define 用于文本替换,类似于 C 语言中的 #define。 一旦 `define 指令被编译,其在整个编译过程中都会有效。例如,在一个文件中定义

  • 问题内容: 我只是碰到一篇文章,声称我从未听过,也找不到其他地方。声称是从另一个线程的角度来看,可以根据构造函数内部的指令对构造函数返回的值的分配进行重新排序。换句话说,声称是在下面的代码中,另一个线程可以读取其中未设置的非空值。 这是真的? 编辑: 我认为从线程执行的角度来看,可以保证的分配与的分配具有先发生后关系。但是,这两个值都可能缓存在寄存器中,并且可能未按照最初写入的顺序将它们刷新到主存

  • 问题内容: 我研究了Java内存模型,并发现了重新排序问题。一个简单的例子: 重新排序非常不可预测且很奇怪。另外,它破坏了抽象。我想处理器架构必须有充分的理由来做一些对程序员来说很不方便的事情。 那是什么原因 关于如何处理重新排序有很多信息,但是我找不到有关 为什么 需要重新排序的任何信息。人们到处都只说“这是因为有一些性能优势”。例如,在存储之前有什么性能好处? 您可以推荐一些有关此的文章,论文