c - 使用 union 来简化转换

标签 c data-structures unions

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

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

struct Pixel {
    unsigned char b,g,r,a;
};

我想检查我是否有一个低于特定值的像素(例如 r, g, b <= 0x10 )。我想我只想用 0x00E0E0E0 条件测试像素位的位与位 | (我这里可能有错误的字节顺序)来获取暗像素。

与其使用这个丑陋的烂摊子 (*((uint32_t*)&pixel))要获得 32 位 unsigned int 值,我认为应该有一种方法可以设置它,这样我就可以使用 pixel.i ,同时保持使用 pixel.g 引用绿色字节的能力.

我可以这样做吗?这行不通:

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

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

union Pixel {
    unsigned char c[4];
    uint32_t bits;
};

这也行,但我仍然需要更改所有内容以索引到 c ,这有点难看,但如果我真的需要的话,我可以让它与宏一起工作。

最佳答案

(已编辑) gcc 和 MSVC 都允许“匿名”结构/union ,这可能会解决您的问题。例如:

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);

给出(在英特尔上):

04030201

这需要将原始代码中的所有 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);

这也适用于 VC9,带有“警告 C4201:使用了非标准扩展:无名结构/union ”。例如,Microsoft 在以下方面使用了此技巧:

typedef union {
    struct {
        DWORD LowPart;
        LONG HighPart;
    };  // <-- nameless member!
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER;

但他们通过抑制不需要的警告来“作弊”。

虽然上面的例子没问题,但如果你经常使用这种技术,你很快就会得到无法维护的代码。使事情更清楚的五个建议:

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

(2) 返回到 OP 拒绝的丑陋转换,但将其丑陋隐藏在宏或内联函数中,如:

#define BITS(x) (*(uint32_t*)&(x))

但这会违反严格的别名规则。 (例如,参见 AndreyT 的回答:C99 strict aliasing rules in C++ (GCC)。)

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

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

(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

使用 VC9 或使用 -pedantic 的 gcc,您将需要(不要将它与 gcc 一起使用 -- 请参阅末尾的注释):

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

(5) 宏可能是首选。在 gcc 中,您可以非常巧妙地定义对任何给定类型的 union 转换:

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

对于 VC9 和其他编译器,没有 typeof,可能需要指针(不要将其用于 gcc--见末尾的注释):

#define CAST(typeof_x, type, x) (((union {typeof_x src; type dst;}*)&(x))->dst)

self 记录,更安全。而且不会丑陋。所有这些建议都可能编译成相同的代码,因此效率不是问题。另请参阅我的相关答案:How to format a function pointer? .

关于 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 .

关于c - 使用 union 来简化转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2876832/

相关文章:

c - 将帧从链路层传递到物理层

CMake - 包含第三方文件

c - 如何正确处理套接字接受返回 "Too many open files"

c# - 遍历树C#中的所有叶节点

C:结构,简单,完整的短程序代码显示

python - 遍历我的数据结构,并为其输入随机值

data-structures - 音乐的语义(或符号)表示

mysql - 将一个表中的两列连接到另一表中的一列,为两列中的每一列返回不同或相同的结果

c - 有没有办法在 C 中巧妙地创建一个函数,使其在不同的参数值中执行不同的函数?

c - 在 C 中使用枚举并集