联盟到unsigned long long int cast

时间:2011-02-15 13:41:44

标签: c casting aggregate unions

我有一个如下联盟:

typedef unsigned long GT_U32;
typedef unsigned short GT_U16;
typedef unsigned char GT_U8;

typedef union
{
    GT_U8   c[8];
    GT_U16  s[4];
    GT_U32  l[2];
} GT_U64;

我想将此联盟转换为以下内容:

typedef unsigned long long int UINT64;

我写的演员函数如下:

UINT64 gtu64_to_uint64_cast(GT_U64 number_u)
{
    UINT64 casted_number = 0;

    casted_number = number_u.l[0];
    casted_number = casted_number << 32;
    casted_number = casted_number | number_u.l[1];

    return casted_number;
}

此函数使用l成员执行移位和按位或。如果联盟的sc成员用于设置其值,会发生什么?

我不确定此函数是否总是正确地转换值。我怀疑它与longshort的字节顺序有关。任何人都可以帮忙吗?

下面列出了完整的示例程序。

#include <stdio.h>

typedef unsigned long GT_U32;
typedef unsigned short GT_U16;
typedef unsigned char GT_U8;

typedef union
{
    GT_U8   c[8];
    GT_U16  s[4];
    GT_U32  l[2];
} GT_U64;

typedef unsigned long long int UINT64;

UINT64 gtu64_to_uint64_cast(GT_U64 number_u)
{
    UINT64 casted_number = 0;

    casted_number = number_u.l[0];
    casted_number = casted_number << 32;
    casted_number = casted_number | number_u.l[1];

    return casted_number;
}

int main()
{
    UINT64 left;
    GT_U64 right;

    right.s[0] = 0x00;
    right.s[1] = 0x00;
    right.s[2] = 0x00;
    right.s[3] = 0x01;
    left = gtu64_to_uint64_cast(right);

    printf ("%llu\n", left);
    return 0;
}

6 个答案:

答案 0 :(得分:1)

这真的很丑陋且依赖于实现 - 只需使用memcpy,例如

UINT64 gtu64_to_uint64_cast(GT_U64 number_u)
{
    UINT64 casted_number;

    assert(sizeof(casted_number) == sizeof(number_u));

    memcpy(&casted_number, &number_u, sizeof(number_u));

    return casted_number;
}

答案 1 :(得分:1)

首先,请使用“stdint.h”中的typedef来达到这个目的。你有很多关于整数类型宽度的假设,不要这样做。

  

如果s或c成员会发生什么   联盟用来设置它   值?

如果存在填充字节或填充位,则读取已通过其他成员写入的联合的成员可能会导致未定义的行为。唯一的例外是unsigned char,它可能总是用于访问各个字节。因此通过c访问很好。通过s访问可能(在非常不可能的情况下)导致未定义的行为。

在你的情况下,没有像“正确”演员这样的事情。它只取决于你想如何将一个小数字数组解释为一个大数字。对此任务的一种可能的解释是你给出的那个。

答案 2 :(得分:1)

此代码应独立于填充,字节序,联合访问和隐式整数提升。

uint64_t gtu64_to_uint64_cast (const GT_U64* number_u)
{
    uint64_t casted_number = 0;
    uint8_t  i;


    for(i=0; i<8; i++)
    {
      casted_number |= (uint64_t) number_u->c[i] << i*8U;
    }

    return casted_number;
}

答案 3 :(得分:1)

如果你不能改变union的声明来包含一个显式的64位字段,也许你可以将它包装起来?像这样:

UINT64 convert(const GT_U64 *value)
{
  union {
    GT_U64 in;
    UINT64 out;
  } tmp;

  tmp.in = *value;
  return tmp.out;
}

这个确实违反了规则,即你只能从上次写入的工会成员中读取,所以也许它会让你的头发着火。我认为这样会很安全,不要看到像这样的联盟会包含填充但当然我可能错了。

我主要想包括这个,因为你不能改变“输入”联合的声明并不意味着你不能通过包装来做几乎相同的事情。

答案 4 :(得分:0)

可能更简单的方法是使用一个长长的成员:

typedef unsigned long long int UINT64;
typedef unsigned long GT_U32;
typedef unsigned short GT_U16;
typedef unsigned char GT_U8;

typedef union
{
    GT_U8   c[8];
    GT_U16  s[4];
    GT_U32  l[2];
    UINT64 ll;
} GT_U64;

然后,只需访问ll即可获得64位值,而无需进行显式转换。您需要告诉编译器使用单字节struct打包。

答案 5 :(得分:0)

您没有指定“正确投射值”的含义。

此代码将以最简单的方式进行转换,但它会根据您的系统字节顺序给出不同的结果。

UINT64 gtu64_to_uint64_cast(GT_U64 number_u) {
  assert(sizeof(UINT64) == sizeof(GT_U64));
  return *(UINT64 *) &number_u;
}