Подтвердить что ты не робот

Как назначить сразу несколько значений в структуру?

Я могу сделать это при инициализации для структуры Foo:

Foo foo =  {bunch, of, things, initialized};

но я не могу этого сделать:

Foo foo;
foo = {bunch, of, things, initialized};

Итак, два вопроса:

  • Почему я не могу сделать последнее, является ли первый конструктор только для инициализации?
  • Как я могу сделать что-то похожее на второй пример, т.е. объявить кучу переменных для структуры в одной строке кода после того, как она уже была инициализирована? Я стараюсь не делать этого для больших структур со многими переменными:

    Foo foo;
    
    foo.a = 1;
    foo.b = 2;
    foo.c = 3;
    //... ad infinitum
    
4b9b3361

Ответ 1

Первый - это агрегатный инициализатор - вы можете прочитать эти и помеченные инициализаторы в этом решении:

Что такое синтаксис инициализации структуры тегов?

Это специальный синтаксис инициализации, и вы не можете сделать что-то подобное после инициализации своей структуры. То, что вы можете сделать, это предоставить функцию члена (или нечлена), чтобы взять вашу серию значений в качестве параметров, которые затем присваиваются внутри функции-члена, что позволит вам выполнить это после инициализации структуры таким образом, чтобы она была одинаково (после того, как вы впервые записали эту функцию!)

Ответ 2

Попробуйте следующее:

Foo foo;
foo = (Foo){bunch, of, things, initialized};

Это будет работать, если у вас хороший компилятор (например, GCC). Возможно, вам нужно включить режим C99 с помощью --std=gnu99, я не уверен.

Ответ 3

В С++ 11 вы можете выполнить несколько присвоений с помощью "привязки" (объявленной в заголовке кортежа)

struct foo {
    int a, b, c;
} f;

std::tie(f.a, f.b, f.c) = std::make_tuple(1, 2, 3);

Если ваше правое выражение имеет фиксированный размер, и вам нужно только получить некоторые из элементов, вы можете использовать placeholder ignore с галстуком

std::tie(std::ignore, f.b, std::ignore) = some_tuple; // only f.b modified

Если вы обнаружите, что синтаксис std:: tie (f.a, f.b, f.c) слишком загромождает код, вы можете иметь функцию-член, возвращающую этот кортеж ссылок

struct foo {
    int a, b, c;
    auto members() -> decltype(std::tie(a, b, c)) {
        return std::tie(a, b, c);
    }
} f;

f.members() = std::make_tuple(1, 2, 3);

Все это, предполагая, что перегрузка оператора присваивания не является опцией, потому что ваша структура не является конструктивной с помощью такой последовательности значений, и в этом случае вы могли бы сказать

f = foo(1, 2, 3);

Ответ 4

Если вы не слишком заботитесь об эффективности, вы можете удвоить назначение: создать новый экземпляр структуры, используя агрегатную инициализацию, а затем скопировать ее:

struct Foo foo;

{
  struct Foo __tmp__ = {bunch, of, things, initialized};
  foo = __tmp__;
}

Убедитесь, что вы сохранили часть, заключенную в {} s, чтобы отменить ненужную временную переменную, как только она больше не понадобится.

Обратите внимание, что это не так эффективно, как создание, например, функции 'set' в структуре (если С++) или вне структуры, принимая указатель структуры (если C). Но если вам нужна быстрая, предпочтительно временная альтернатива написанию поэтапного назначения, это может сделать.

Ответ 5

Память. Вот интересное дополнение i386.

После долгих хлопот использование Оптимизация и memcpy, по-видимому, создает минимальный размер с помощью i386 с GCC и C99. Я использую -O3 здесь. У stdlib, похоже, есть всевозможные оптимизаторы компилятора под рукой, и этот пример использует это (memcpy на самом деле скомпилирован здесь).

Сделайте это с помощью

Foo foo; //some global variable

void setStructVal (void)   {

    const Foo FOO_ASSIGN_VAL = {    //this goes into .rodata
            .bunch       = 1,
            .of          = 2,
            .things      = 3,
            .initialized = 4
    };

    memcpy((void*) &FOO_ASSIGN_VAL, (void*) foo, sizeof(Foo));

    return;
}

Результат:

  • (. rodata) FOO_ASSIGN_VAL хранится в .rodata​​li >
  • (текст) последовательность * movl FOO_ASSIGN_VAL,% регистров * происходит
  • (. text) последовательность записей movl%, foo

Пример:

  • Скажем, Foo - это 48-полевая структура значений uint8_t. Он выровнен по памяти.

  • (IDEAL). На 32-разрядной машине этот COULD должен быть таким же быстрым, как 12 команд MOVL, которые немедленно отправляются в адресное пространство. Для меня это 12 * 10 == 120 байт.текста.

  • (АКТУАЛЬНЫЙ) Однако, используя ответ AUTO, скорее всего, будет генерировать 48 команд MOVB в .text. Для меня это 48 * 7 == 336bytes.text!!

  • (SMALLEST *) Используйте версию memcpy выше. ЕСЛИ выравнивание соблюдается,

    • FOO_ASSIGN_VAL помещается в .rodata(48 байт),
    • 12 MOVL в регистр%
    • 12 MOVL outof% регистров используются в .text(24 * 10) == 240bytes.
    • Для меня это всего 288 байт.

Итак, для меня, по крайней мере, с моим кодом i386,

- Ideal:    120 bytes
- Direct:   336 bytes
- Smallest: 288 bytes

* Самый маленький здесь означает "наименьший след, который я знаю". Он также выполняется быстрее, чем вышеупомянутые методы (24 инструкции против 48). Конечно, версия IDEAL самая быстрая и самая маленькая, но я все еще не могу понять этого.

-Джастин

* Кто-нибудь знает, как получить реализацию " IDEAL" выше? Меня раздражает ад!

Ответ 6

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

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

typedef union
{
    struct
    {
      char a;
      char b;
    } Foo;
    unsigned int whole;
} MyUnion;

MyUnion _Union;
_Union.Foo.a = 0x23;    // assign by element
_Union.Foo.b = 0x45;    // assign by element
_Union.whole = 0x6789;  // assign at once

Будьте осторожны в своей организации памяти (это "a" MSB или LSB "всего"?).