Как выполнить приведение битового поля к указателю?

Я написал следующий фрагмент кода, который создает

предупреждение: инициализация делает указатель из целого числа без приведения

OR A

предупреждение: приведение к указателю из целого числа разного размера

из gcc (GCC) 4.1.1 20070105 (Red Hat 4.1.1-52)

struct my_t {
  unsigned int a     : 1;
  unsigned int b    : 1;
};

struct my_t mine = {
  .a = 1,
  .b = 0
};

const void * bools[] = { "ItemA", mine->a, "ItemB", mine->b, 0, 0 };

int i;
for (i = 0; bools[i] != NULL; i += 2)
  fprintf(stderr, "%s = %d\n", bools[i], (unsigned int) bools[i + 1] ? "true" : "false");

Как мне убрать предупреждение? Независимо от того, что я пробовал использовать, всегда появляется предупреждение.

Спасибо, Ченз


person Crazy Chenz    schedule 03.01.2011    source источник


Ответы (4)


const void * bools[] = { "ItemA", mine->a, "ItemB", mine->b, 0, 0 }; 

С этим фрагментом есть несколько проблем:

  1. mine isn't declared as a pointer type (at least not in the code you posted), so you shouldn't be using the -> component selection operator;
  2. If you change that to use the . selection operator, you'd be attempting to store the boolean value in a or b as a pointer, which isn't what you want;
  3. But that doesn't matter, since you cannot take the address of a bit-field (§ 6.5.3.2, paragraph 1).

Если вы пытаетесь связать логическое значение с другим объектом, лучше объявить такой тип, как

struct checkedObject {void *objPtr; int check};

и инициализировать массив как

struct checkedObject[] = {{"ItemA", 1}, {"ItemB", 0}, {NULL, 0}};

У битовых полей есть свои применения, но это не одно из них. Вы действительно не экономите место в этом случае, так как по крайней мере одна полная адресуемая единица хранения (байт, слово, что угодно) должна быть выделена для хранения двух битовых полей.

person John Bode    schedule 03.01.2011
comment
Да, мой реальный код действительно использует намного больше логических значений, просто подумал, что было бы неплохо использовать битовые поля. Упс ... Я действительно хотел использовать здесь свои обозначения. Ваш третий пункт о том, что вы не можете взять адрес битового поля, кажется наиболее разумным ... Интересно, как GCC обрабатывает код тогда, в конце концов, это всего лишь предупреждение ... - person Crazy Chenz; 05.01.2011

Хм, а почему вы настаиваете на использовании указателей в качестве логических? Как насчет этой альтернативы?

struct named_bool {
    const char* name;
    int         val;
};

const struct named_bool bools[] = {{ "ItemA", 1 }, { "ItemB", 1 }, { 0, 0 }};
person Nikolai Fetissov    schedule 03.01.2011

Две проблемы:

  1. Не уверен, почему вы пытаетесь преобразовать unsigned int a:1 в void*. Если вы пытаетесь ссылаться на него, синтаксис будет &mine->a, а не mine->a, но ...

  2. Вы не можете создать указатель на бит в C (по крайней мере, насколько я знаю). Если вы пытаетесь создать указатель на бит, вы можете рассмотреть один из следующих вариантов:

    • Создайте указатель на структуру битового поля (т.е. struct my_t *) и (при необходимости) используйте отдельный номер, чтобы указать, какой бит использовать. Пример:

      struct bit_ref {
          struct my_t  *bits;
          unsigned int  a_or_b; // 0 for bits->a, 1 for bits->b
      }
      
    • Не используйте битовое поле. Используйте char для каждого флага, так как это наименьший тип данных, на который вы можете создать указатель.

    • Используйте битовое поле, но реализуйте его вручную с помощью логических операций. Пример:

      typedef unsigned int my_t;
      
      
      #define MY_T_A (1u << 0)
      #define MY_T_B (1u << 1)
      
      
      struct bit_ref {
          struct my_t  *bits;
          unsigned int  shift;
      };
      
      
      int deref(const struct bit_ref bit_ref)
      {
          return !!(bit_ref.bits & (1 << bit_ref.shift));
      }
      
person Joey Adams    schedule 03.01.2011

Есть несколько способов избавиться от предупреждения, но при этом использовать значение указателя как логическое. Например, вы могли бы сделать это:

const void * bools[] = { "ItemA", mine->a ? &bools : 0, "ItemB", mine->b ? &bools : 0, 0, 0 };

Здесь используется указатель NULL для false и ненулевой указатель (в данном случае &bools, но указатель на любой объект с правильной продолжительностью хранения будет прекрасным) для true. Затем вы бы также удалили приведение к unsigned int в тесте, чтобы оно было просто:

fprintf(stderr, "%s = %d\n", bools[i], bools[i + 1] ? "true" : "false");

(Нулевой указатель всегда оценивается как ложь, а ненулевой указатель как истина).

Однако я согласен с тем, что вам лучше создать массив structs.

person caf    schedule 03.01.2011