联合:从联合的一个数据成员中读取以写入另一个

Posted

技术标签:

【中文标题】联合:从联合的一个数据成员中读取以写入另一个【英文标题】:Union: Reading from one data member of a union to write into another 【发布时间】:2012-07-15 09:04:33 【问题描述】:

我知道对于下面的代码,下面的“Illegal”是未定义的(虽然一些编译器允许它),因为联合成员“a”是活动的,然后我们从联合成员“b”中读取。 问题是,“AmILegal”中的代码是否修复了它,还是我在做一些可怕甚至更晦涩的事情?我可以使用 memcpy 来实现相同的效果,还是我在那里调用了另一个未定义的行为?

编辑:也许这个例子不够清楚。我要做的就是激活其他成员。 所以我将浮点数更改为int。虽然看起来很傻,但更接近真实案例。阅读下面的代码。

(是否由于某种原因不允许将一个工会成员复制到另一个工会成员中?)

struct Foo

    union Bar
    
        int a[4];
        int b[4];
    ;

    void this_is_Illegal()
    
         a[0]=1;
         a[1]=2;
         a[2]=3;
         a[3]=4;
         std::cout<<b[0]<<b[1]<<b[2]<<b[3];
    

    void but_is_this_Legal?()
    
         a[0]=1;
         a[1]=2;
         a[2]=3;
         a[3]=4;

         b[0]=a[0];
         b[1]=a[1];
         b[2]=a[2];
         b[3]=a[3];
         std::cout<<b[0]<<b[1]<<b[2]<<b[3];
    


    void this_looks_scary_but_is_it?()
    
         a[0]=1;
         a[1]=2;
         a[2]=3;
         a[3]=4;
         //forget portability for this q, assume sizeof(int)==sizeof(float)
         //maybe memmove works here as well?
         memcpy(b, a, sizeof(int)*4)

         std::cout<<b[0]<<b[1]<<b[2]<<b[3];
    

;

如果以上所有内容听起来都不是很有用,请认为 a 实际上是一个与 float[4] 结合的 _m128。位表示始终是准确和正确的。 在某个时间点,您将需要实际使用它,并且您需要将它作为浮点数组放在主内存中。 “复制指令”实际上是从 _m128 联合成员到 float[4] 成员的 _mm_store_ps。因此,关于 memset 的问题 - 也许这是我需要的更准确的例子......

【问题讨论】:

能贴出相关源码吗? "所以我将 float 更改为 int。" 我不明白您要做什么;是否要将 (int,float) 的位模式解释为 (float,int)? (这就是所谓的“类型双关语”。) 伙计们,请阅读代码下方的最后一段。我正在准确地解释真实的用例,但是因为真实的代码使用的是 VC++ 编译器内在函数等。在这里使用会很不可读。用例将 __m128 的内存位置作为浮点数 [4] 返回(这几乎是它的定义),同时确保定义了行为。 但是在你的例子中甚至没有一个float 声明! 有,但它只是让人们试图推断我想对浮点数中的数据做什么而混淆了事情,我删除了它,因为数据类型对 q 无关紧要:我只是想知道我问什么:是否可以通过从另一个枚举成员写入来激活非活动枚举成员。 __m128 是困扰我的具体案例,但我想要一个通用的答案。 【参考方案1】:

第二个函数是完全合法的 - 但不做同样的事情,因为它会执行一个 int 到 float 的转换,而不是保持位不变。

老实说,我会坚持第一个 - 从技术上讲,该行为是未定义的,但我怀疑它只是为您做了正确的事情。

第三个将一种形式的未定义行为切换为另一种形式(一旦您将任意字节写入浮点数,任何事情都可能发生)。但如果你知道字节确实代表一个有效的浮点值,那就没问题了。

【讨论】:

未定义行为的问题在于,它可能在不同的编译器下甚至在同一编译器的不同版本下,甚至在不同平台上同一编译器的相同版本下的执行方式不同。因此,仅仅因为它产生了所需的输出,就不应该提倡或鼓励它。 你确定第三个是未定义的行为吗?将任意字节写入浮点数我的意思是?我已经做了我想做的事,但我想让它 1000% 可移植和有效。 如果您将字节从有效浮点数复制到另一个浮点数,那么您就可以了,新浮点数与旧浮点数具有相同的值。如果您写入任意字节,则所有赌注都将关闭。 (新标准中为 3.9/2-3。)【参考方案2】:

this_is_illegal,this_is_legal?几乎是使用枚举的标准方法;)

但是 memcpy 不起作用,因为 &a 和 &b 由于枚举而位于同一个地址,而 memcpy 什么也不做

因为 &a 和 &b 在同一个地址,你可以用枚举做一些有趣的事情——在你的情况下,将浮点数解释为整数是枚举的内置功能,但不能触发自动转换,因为他们在同一个地址

你可能想看看 attribute((packed)) 因为它有助于声明协议结构/枚举

【讨论】:

"但是 memcpy 将不起作用,因为 &a 和 &b 位于同一地址,因为枚举和 memcpy 什么都不做" - 我知道它不需要做任何事情。我要做的就是确保第二个工会成员处于活动状态,因此可以“定义”从中读取。 它总是被定义——枚举只存在于编译时,它提供对重叠内存空间的类型访问 仅仅因为它是使用联合的正常方式并不意味着它是按照标准定义好的。 @ZoltánNagy 你在说哪个枚举?

以上是关于联合:从联合的一个数据成员中读取以写入另一个的主要内容,如果未能解决你的问题,请参考以下文章

modelsim和matlab联合使用

C语言的结构和联合,以及PHP是怎么实现弱类型的

从 zip 文件中读取文件并将其写入另一个文件时面临的问题

联合----union

在事务中从一个db解析读取并写入另一个db。

Pyspark 数据帧从一个存储桶中读取,并在同一作业中使用不同的 KMS 密钥写入另一个存储桶