繁体   English   中英

在位域上投射 uint64_t

[英]Casting uint64_t on bitfield

我找到了位域用于网络消息的代码。 我想知道什么铸造bitfield_struct data = *(bitfield_struct *)&tmp; exaclty 和它的语法是如何工作的。 它不会违反严格的别名规则吗? 这是代码的一部分:

typedef struct  
{
    unsigned      var1    : 1;
    unsigned      var2    : 13;
    unsigned      var3    : 8;
    unsigned      var4    : 10;
    unsigned      var5    : 7;
    unsigned      var6    : 12;
    unsigned      var7    : 7;
    unsigned      var8    : 6;

} bitfield_struct;

void print_data(u_int64_t * raw, FILE * f, int no_object)
{
    uint64_t tmp = ntohll(*raw);

    bitfield_struct data = *(bitfield_struct *)&tmp;

    ...
}

它不会违反严格的别名规则吗?

是的,它会,所以代码调用未定义的行为。 它也是高度不可移植的:

  • 我们不知道给定系统使用的称为“可寻址存储单元”的抽象项的大小。 它不一定是 64 位,因此理论上可能有填充和其他令人讨厌的东西隐藏在位字段中。 64 位unsigned是可疑的。

  • 我们也不知道位域是否使用与uint64_t相同的位顺序。 我们也不知道它们是否使用相同的字节序。

如果需要访问uint64_t单个位(字段),我建议使用按位移位这样做,因为即使在不同的字节序架构之间,这也使代码完全可移植。 那么您也不需要非便携式ntohll调用。

它所做的(或尝试做的)非常简单。

uint64_t tmp = ntohll(*raw);

该行采用指针 raw 中的值,反转字节顺序并将其复制到 temp 中。

bitfield_struct data = *(bitfield_struct *)&tmp;

此行将 temp(是 uint64)中的数据重新解释为bitfield_struct类型并将其复制到数据中。 这基本上相当于:

/* Create a bitfield_struct pointer that points to tmp */
bitfield_struct *p = (bitfield_struct *)&tmp;

/* Copy the value in tmp to data */
bitfield_struct data = *p;

这是因为通常bitfield_structuint64是不兼容的类型,您不能仅使用bitfield_struct data = tmp;将一个分配给另一个bitfield_struct data = tmp;

代码大概会继续通过data访问位域中的字段,例如data.var1

现在,正如人们指出的那样,有几个问题使此代码不可靠且不可移植。

  1. 位域在很大程度上依赖于实现。 解决方案? 阅读手册并弄清楚您的特定编译器变体如何处理位域。 或者根本不使用位域。

  2. 不能保证 uint64_t 和 bitfield_struct 具有相同的对齐方式。 这意味着可能有填充可以完全抵消您的期望并让您最终得到错误的数据。 一种解决方案是使用memcpy来复制而不是指针,这可能会让您遇到此特定问题。 或者使用编译器提供的机制指定压缩对齐。

  3. 当应用严格的别名规则时,代码会调用 UB。 解决方案? 大多数编译器都有一个可以启用的no-strict-aliasing标志,但会降低性能。 或者更好的是,使用bitfield_structuint64_t创建联合类型,并使用它来重新解释一个和另一个。 即使有严格的别名规则,这也是允许的。 使用memcpy也是合法的,因为它将数据视为字符数组。

但是,最好的办法是根本不要使用这段代码。 您可能已经注意到,它过于依赖编译器和特定于平台的东西。 相反,尝试使用位掩码和移位来完成同样的事情。 这摆脱了上面提到的所有三个问题,不需要特殊的编译器标志,也不必面对任何真正的可移植性问题。 最重要的是,它可以避免其他开发人员阅读您的代码,以后不必担心此类事情。

右到左:

&tmp&tmp地址

(bitfield_struct *)&tmp地址是 bitfield_struct 类型的数据的地址

*(bitfield_struct *)&tmp*(bitfield_struct *)&tmp提取值,假设它是 bitfield_struct 数据

bitfield_struct data = *(bitfield_struct *)&tmp; 将tmp存入数据,假设tmp是bitfield_struct

所以它只是使用额外的指针进行复制以避免编译错误/不兼容类型的警告。

您可能不明白的是结构的位寻址。

unsigned var1 : 1; unsigned var2 : 13;

在这里你会找到更多关于它的信息: https : //www.tutorialspoint.com/cprogramming/c_bit_fields.htm

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM