Как назначить сразу несколько значений в структуру?
Я могу сделать это при инициализации для структуры 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
Ответы
Ответ 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 хранится в .rodatali >
- (текст) последовательность * 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 "всего"?).