相关疑难解决方法(0)

C和C++中联合的目的

我早先使用过工会; 今天,当我读到这篇文章并开始知道这段代码时,我感到震惊

union ARGB
{
    uint32_t colour;

    struct componentsTag
    {
        uint8_t b;
        uint8_t g;
        uint8_t r;
        uint8_t a;
    } components;

} pixel;

pixel.colour = 0xff040201;  // ARGB::colour is the active member from now on

// somewhere down the line, without any edit to pixel

if(pixel.components.a)      // accessing the non-active member ARGB::components
Run Code Online (Sandbox Code Playgroud)

实际上是未定义的行为即从工会成员读取而不是最近编写的那个导致未定义的行为.如果这不是工会的预期用途,那是什么?有人可以详细解释一下吗?

更新:

事后我想澄清一些事情.

  • 这个问题的答案与C和C++不一样; 我无知的年轻自我将其标记为C和C++.
  • 在仔细研究了C++ 11的标准后,我无法确切地说它调用了访问/检查非活动的union成员是未定义/未指定/实现定义的.我能找到的只是§9.5/ 1:

    如果标准布局联合包含多个共享公共初始序列的标准布局结构,并且如果此标准布局联合类型的对象包含其中一个标准布局结构,则允许检查任何标准布局结构的公共初始序列.标准布局结构成员.§9.2/ 19:如果相应的成员具有布局兼容类型且两个成员都不是位字段,或者两者都是具有相同宽度的位字段,则一个或多个初始序列的两个标准布局结构共享一个公共初始序列成员.

  • 在C中,(C99 TC3 - DR 283以后)这样做是合法的(感谢Pascal Cuoq提出这个问题).但是,如果读取的值对于读取的类型无效(所谓的"陷阱表示"),尝试执行此操作仍会导致未定义的行为.否则,读取的值是实现定义的.
  • C89/90在未指明的行为(附件J)中称之为,而K&R的书称其实施已定义.来自K&R的报价:

    这是联合的目的 - 一个可以合法地保存几种类型中的任何一种的变量.[...]只要用法一致:检索到的类型必须是最近存储的类型.程序员有责任跟踪当前存储在联合中的类型; 如果将某些内容存储为一种类型并将其提取为另一种类型,则结果将依赖于实现. …

c c++ unions type-punning

237
推荐指数
9
解决办法
10万
查看次数

访问非活动的union成员和未定义的行为?

我的印象是访问union除最后一个成员之外的成员是UB,但我似乎无法找到一个可靠的参考(除了声称它是UB但没有标准支持的答案).

那么,这是不确定的行为?

c++ undefined-behavior unions language-lawyer

114
推荐指数
4
解决办法
2万
查看次数

什么是将一个浮点数类型化为int的正确方法,反之亦然?

下面的代码通过一些位攻击执行快速反平方根操作.该算法可能是由Silicon Graphics在1990年代早期开发的,它也出现在Quake 3中. 更多信息

但是我从GCC C++编译器收到以下警告:解除引用类型惩罚指针将破坏严格别名规则

我应该使用static_cast,reinterpret_cast还是dynamic_cast在这种情况下使用?

float InverseSquareRoot(float x)
{
    float xhalf = 0.5f*x;
    int32_t i = *(int32_t*)&x;
    i = 0x5f3759df - (i>>1);
    x = *(float*)&i;
    x = x*(1.5f - xhalf*x*x);
    return x;
}
Run Code Online (Sandbox Code Playgroud)

c++ strict-aliasing gcc-warning undefined-behavior type-punning

24
推荐指数
4
解决办法
6830
查看次数

在实践中,工会,别名和打字:什么有效,有什么不可行?

我有一个问题,了解使用GCC的工会可以做什么和不可以做什么.我阅读了有关它的问题(特别是这里这里),但他们关注C++标准,我觉得C++标准和实践(常用的编译器)之间存在不匹配.

特别是,我最近在阅读有关编译标志-fstrict-aliasingGCC在线文档中发现了令人困惑的信息.它说:

-fstrict走样

允许编译器采用适用于正在编译的语言的最严格的别名规则.对于C(和C++),这将根据表达式的类型激活优化.特别地,假设一种类型的对象永远不会与不同类型的对象驻留在相同的地址,除非类型几乎相同.例如,a unsigned intcan可以是a int,但不是a void*或a double.字符类型可以别名为任何其他类型.特别注意这样的代码:

union a_union {
  int i;
  double d;
};

int f() {
  union a_union t;
  t.d = 3.0;
  return t.i;
}
Run Code Online (Sandbox Code Playgroud)

从不同的工会成员阅读的做法比最近写的那个(称为"打字式")很常见.即使使用-fstrict-aliasing,只要通过union类型访问内存,就允许类型为punning.因此,上面的代码按预期工作.

这是我认为我从这个例子和我的疑虑中理解的:

1)别名仅适用于相似类型或char

1)的后果:别名 - 正如文字暗示的那样 - 是你有一个值和两个成员来访问它(即相同的字节);

怀疑:当它们具有相同的字节大小时,两种类型是相似的吗?如果没有,什么是类似的类型?

1)对于非相似类型(无论这意味着什么)的后果,别名不起作用;

2)类型双关语是指我们读的不同于我们写的成员; 它是常见的,只要通过union类型访问内存,它就可以正常工作;

怀疑:在类型相似的特定情况下别名是什么类型?

我感到困惑,因为它表示unsigned int和double不相似,所以别名不起作用; 然后在示例中它是int和double之间的别名,它清楚地表明它按预期工作,但称之为类型 - 惩罚:不是因为类型是或不相似,而是因为它是从一个不写的成员读取.但是从一个没有写的成员那里读取的是我所理解的混淆(正如这个词所暗示的那样).我迷路了.

问题: 有人可以澄清别名和类型惩罚之间的区别,这两种技术的用途是如何在GCC中发挥作用的?编译器标志有什么作用?

c++ gcc strict-aliasing

16
推荐指数
2
解决办法
1374
查看次数