Объединение без подписи - PullRequest
       12

Объединение без подписи

0 голосов
/ 15 февраля 2011

У меня есть объединение следующим образом:

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 для выполнения сдвига и побитового или.Что произойдет, если члены объединения s или c используются для установки его значений?

Я не уверен, что эта функция всегда будет правильно приводить значения.Я подозреваю, что это как-то связано с порядком байтов long и short.Может ли какой-нибудь орган помочь?

Полный пример программы приведен ниже.

#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 ]

1 голос
/ 16 февраля 2011

Если вы не можете изменить объявление объединения для включения явного 64-битного поля, возможно, вы можете просто обернуть его?Например:

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

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

Это действительно нарушает правило, гласящее, что вы можете читать только с последнего члена профсоюза, которому вы писали последний раз, так что, возможно, это подожжет ваши волосы.Я думаю, что это будет довольно безопасно, хотя и не вижу случая, когда такой союз включал бы отступы, но, конечно, я мог ошибаться.

Я в основном хотел включить это, потому что вы не можетеизменение объявления объединения «input» не означает, что вы не можете сделать почти то же самое, обернув его.

1 голос
/ 16 февраля 2011

Этот код должен работать независимо от заполнения, порядка байтов, доступа к объединению и неявного целочисленного продвижения.

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;
}
1 голос
/ 15 февраля 2011

Прежде всего, пожалуйста, используйте typedef s из "stdint.h" для этой цели.У вас есть множество предположений о том, какой будет ширина целочисленных типов, не делайте этого.

Что произойдет, если члены s или c объединения используются для установки его значений?1005 *

Чтение члена объединения, записанного с помощью другого члена, может привести к неопределенному поведению при наличии байтов заполнения или битов заполнения.Единственное исключение из этого - unsigned char, которое всегда можно использовать для доступа к отдельным байтам.Так что доступ через c в порядке.Доступ через s может (в очень маловероятных обстоятельствах) вызвать неопределенное поведение.

И в вашем случае не существует такого понятия, как «правильное» приведение.Это просто зависит от того, как вы хотите интерпретировать массив маленьких чисел как одно большое число.Одна из возможных интерпретаций этой задачи - та, которую вы дали.

1 голос
/ 15 февраля 2011

Это действительно уродливо и зависит от реализации - просто используйте 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;
}
0 голосов
/ 15 февраля 2011

Вы не указываете, что означает «приведение значений правильно».

Этот код будет приведен наиболее простым способом, но он даст разные результаты в зависимости от порядка работы вашей системы.

UINT64 gtu64_to_uint64_cast(GT_U64 number_u) {
  assert(sizeof(UINT64) == sizeof(GT_U64));
  return *(UINT64 *) &number_u;
}
0 голосов
/ 15 февраля 2011

Вероятно, более простой способ приведения - использовать объединение с длинным длинным членом:

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 упаковку.

...