使用联合来简化演员表

Ste*_* Lu 12 c unions data-structures

我意识到我想做的事情并不安全.但我只是在做一些测试和图像处理,所以我的重点是速度.

现在,这段代码为我提供了32位像素值类型的相应字节.

struct Pixel {
    unsigned char b,g,r,a;
};
Run Code Online (Sandbox Code Playgroud)

我想检查一下像素是否低于某个值(例如r, g, b <= 0x10).我想我只想条件测试像素的位和位0x00E0E0E0(我可能有错误的字节顺序)来获得暗像素.

我没有使用这个丑陋的混乱(*((uint32_t*)&pixel))来获取32位无符号整数值,而是认为应该有一种方法让我设置它以便我可以使用pixel.i,同时保持使用引用绿色字节的能力pixel.g.

我可以这样做吗?这不起作用:

struct Pixel {
    unsigned char b,g,r,a;
};
union Pixel_u {
    Pixel p;
    uint32_t bits;
};
Run Code Online (Sandbox Code Playgroud)

我需要编辑我现有的代码来说明pixel.p.g绿色字节.如果我这样做会发生同样的情况

union Pixel {
    unsigned char c[4];
    uint32_t bits;
};
Run Code Online (Sandbox Code Playgroud)

这也可以工作,但我仍然需要更改所有索引到的内容c,这有点难看,但如果我真的需要,我可以使用宏.

Jos*_*sey 19

(已编辑)gcc和MSVC都允许"匿名"结构/联合,这可能会解决您的问题.例如:

union Pixel {
   struct {unsigned char b,g,r,a;};
   uint32_t bits;  // use 'unsigned' for MSVC
}

foo.b = 1;
foo.g = 2;
foo.r = 3;
foo.a = 4;
printf ("%08x\n", foo.bits);
Run Code Online (Sandbox Code Playgroud)

给(在英特尔):

04030201
Run Code Online (Sandbox Code Playgroud)

这需要在原始代码中将struct Pixel的所有声明更改为union Pixel.但是这个缺陷可以通过以下方式解决

struct Pixel {
    union {
        struct {unsigned char b,g,r,a;};
        uint32_t bits; 
    };
} foo;

foo.b = 1;
foo.g = 2;
foo.r = 3;
foo.a = 4;
printf ("%08x\n", foo.bits);
Run Code Online (Sandbox Code Playgroud)

这也适用于VC9,"警告C4201:使用非标准扩展名:无名结构/联合".Microsoft使用此技巧,例如:

typedef union {
    struct {
        DWORD LowPart;
        LONG HighPart;
    };  // <-- nameless member!
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER;
Run Code Online (Sandbox Code Playgroud)

但他们通过抑制不必要的警告来"欺骗".

虽然上面的例子没问题,如果你经常使用这种技术,你很快就会得到不可维护的代码.提出更清楚的五条建议:

(1)将名称bits改为更加丑陋的东西union_bits,以清楚地表明一些不寻常的东西.

(2)回到OP拒绝的丑陋演员,但在宏或内联函数中隐藏它的丑陋,如:

#define BITS(x) (*(uint32_t*)&(x))
Run Code Online (Sandbox Code Playgroud)

但这会破坏严格的别名规则.(例如,参见AndreyT的回答:C++(GCC)中的C99严格别名规则.)

(3)保留Pixel的原始定义,但做一个更好的演员:

struct Pixel {unsigned char b,g,r,a;} foo;
// ...
printf("%08x\n", ((union {struct Pixel dummy; uint32_t bits;})foo).bits);
Run Code Online (Sandbox Code Playgroud)

(4)但这更加丑陋.您可以通过一个解决这个问题typedef:

struct Pixel {unsigned char b,g,r,a;} foo;
typedef union {struct Pixel dummy; uint32_t bits;} CastPixelToBits;
// ...
printf("%08x\n", ((CastPixelToBits)foo).bits);    // not VC9
Run Code Online (Sandbox Code Playgroud)

使用VC9,或者使用-pedantic的gcc,你需要(不要gcc中使用它 - 请注意结尾):

printf("%08x\n", ((CastPixelToBits*)&foo)->bits); // VC9 (not gcc)
Run Code Online (Sandbox Code Playgroud)

(5)宏可能是首选.在gcc中,您可以非常巧妙地定义任何给定类型的联合强制转换:

#define CAST(type, x) (((union {typeof(x) src; type dst;})(x)).dst)   // gcc
// ...
printf("%08x\n", CAST(uint32_t, foo));
Run Code Online (Sandbox Code Playgroud)

使用VC9和其他编译器,没有typeof,并且可能需要指针(不要将它与gcc一起使用 - 请注意结尾):

#define CAST(typeof_x, type, x) (((union {typeof_x src; type dst;}*)&(x))->dst)
Run Code Online (Sandbox Code Playgroud)

自我记录,更安全.并不太难看.所有这些建议都可能编译成相同的代码,因此效率不是问题.另请参阅我的相关答案:如何格式化函数指针?.

关于gcc的警告: GCC手册版本4.3.4(但不是版本4.3.0)指出最后一个示例&(x)未定义的行为.请参阅 http://davmac.wordpress.com/2010/01/08/gcc-strict-aliasing-c99/ http://gcc.gnu.org/ml/gcc/2010-01/msg00013.html.


fbr*_*eto 10

为什么不将丑陋的混乱变成内联例程呢?就像是:

inline uint32_t pixel32(const Pixel& p)
{
    return *reinterpret_cast<uint32_t*>(&p);
}
Run Code Online (Sandbox Code Playgroud)

您还可以将此例程提供为Pixel被调用的成员函数,如果您希望以这种方式执行此操作,则可以i()通过该函数访问该值pixel.i().(当不需要强制实施不变量时,我会依靠将功能与数据结构分开.)


Tho*_*ews 10

联合内部结构的问题在于允许编译器在结构(或类)的成员之间添加填充字节,除了位字段.

鉴于:

struct Pixel
{
  unsigned char red;
  unsigned char green;
  unsigned char blue;
  unsigned char alpha;
};
Run Code Online (Sandbox Code Playgroud)

这可以表述为:

Offset  Field
------  -----
0x00    red
0x04    green
0x08    blue
0x0C    alpha
Run Code Online (Sandbox Code Playgroud)

所以结构的大小将是16个字节.

当放入联合时,编译器将采用两者中较大的容量来确定空间.此外,如您所见,32位整数将无法正确对齐.

我建议创建函数来组合和提取32位数量的像素.你也可以声明它inline:

void Int_To_Pixel(const unsigned int word,
                  Pixel& p)
{
  p.red =   (word & 0xff000000) >> 24;
  p.blue =  (word & 0x00ff0000) >> 16;
  p.green = (word & 0x0000ff00) >> 8;
  p.alpha = (word & 0x000000ff);
  return;
}
Run Code Online (Sandbox Code Playgroud)

这比联合中的结构更可靠,包括一个包含位字段的结构:

struct Pixel_Bit_Fields
{
  unsigned int red::8;
  unsigned int green::8;
  unsigned int blue::8;
  unsigned int alpha::8;
};
Run Code Online (Sandbox Code Playgroud)

阅读本文时,无论red是MSB还是alphaMSB,还有一些谜.通过使用位操作,读取代码时毫无疑问.

只是我的建议,YMMV.