web-dev-qa-db-fra.com

comment assigner plusieurs valeurs dans une structure à la fois?

Je peux le faire à l'initialisation pour un struct Foo:

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

mais je ne peux pas faire ça:

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

Donc, deux questions:

  1. Pourquoi est-ce que je ne peux pas faire le dernier, le premier est-il un constructeur spécial pour l'initialisation seulement? 
  2. Comment puis-je faire quelque chose de similaire au deuxième exemple, c’est-à-dire déclarer un groupe de variables pour une structure dans une seule ligne de code après son initialisation? J'essaie d'éviter d'avoir à faire cela pour les grandes structures avec beaucoup de variables:

    Foo foo;
    
    foo.a = 1;
    foo.b = 2;
    foo.c = 3;
    //... ad infinitum
    
35
jim

Le premier est un initialiseur d'agrégat - vous pouvez lire sur ces initialiseurs et les balises initialisées à cette solution:

Qu'est-ce que la syntaxe d'initialisation de structure marquée?

C'est une syntaxe d'initialisation spéciale, et vous ne pouvez pas faire la même chose après l'initialisation de votre structure. Ce que vous pouvez faire est de fournir une fonction membre (ou non membre) pour prendre votre série de valeurs en tant que paramètres que vous affectez ensuite dans la fonction membre - ce qui vous permettrait de le faire après l’initialisation de la structure de manière égale. concis (après avoir écrit la fonction la première fois, bien sûr!)

14

Essaye ça:

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

Cela fonctionnera si vous avez un bon compilateur (par exemple, GCC). Vous devrez peut-être activer le mode C99 avec --std=gnu99, je ne suis pas sûr.

27
David Grayson

En C++ 11, vous pouvez effectuer plusieurs affectations avec "tie" (déclaré dans l'en-tête de Tuple).

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

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

Si votre expression de droite a une taille fixe et que vous n’avez besoin que d’obtenir certains éléments, vous pouvez utiliser l’option Ignorer avec lien.

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

Si vous trouvez la syntaxe std :: tie (f.a, f.b, f.c) trop encombrant, vous pourriez avoir une fonction membre renvoyant ce nuage de références

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);

Bien entendu, tout cela en supposant que la surcharge de l'opérateur d'affectation n'est pas une option car votre structure n'est pas constructible par une telle séquence de valeurs, auquel cas vous pourriez dire

f = foo(1, 2, 3);
7
Nikos Athanasiou

Si vous ne vous souciez pas trop de l'efficacité, vous pouvez doubler l'affectation: c.-à-d. Créer une nouvelle instance de la structure en utilisant l'initialisation d'agrégat, puis la copier:

 struct foo foo; 

 {
 struct Foo __tmp__ = {groupe, de choses initialisées}; 
 foo = __tmp __; 
} 

Assurez-vous de conserver la partie entourée de {} s de manière à supprimer la variable temporaire inutile dès qu'elle n'est plus nécessaire.

Notez que cela n'est pas aussi efficace que de créer, par exemple, une fonction 'set' dans la structure (si c ++) ou en dehors de la structure, accepter un pointeur de structure (si C). Mais si vous avez besoin d'une alternative rapide, de préférence temporaire, à l'écriture d'une affectation élément par élément, cela pourrait suffire.

3
Codesmith

Empreinte mémoire - Voici un ajout intéressant pour i386.

Après beaucoup de difficultés, utiliser optimisation et memcpy semble générer le plus petit encombrement avec i386 avec GCC et C99. J'utilise -O3 ici. stdlib semble avoir toutes sortes d'optimisations de compilateur amusantes sous la main, et cet exemple en fait usage (memcpy est en fait compilé ici).

Faites ceci par:

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;
}

Résultat:

  • (.rodata) FOO_ASSIGN_VAL est stocké dans .rodata
  • (.text) une séquence de * movl FOO_ASSIGN_VAL,% registres * se produisent
  • (.text) une séquence de movl% registres, foo se produisent

Exemple:

  • Say Foo était une structure de 48 champs de valeurs uint8_t. Il est aligné en mémoire.

  • (IDÉAL _) Sur une machine 32 bits, cette POURRAIT être aussi rapide que 12 instructions MOVL permettant de passer immédiatement à l'espace adresse de foo. Pour moi, cela représente 12 * 10 == 120 octets .text.

  • (ACTUAL) Cependant, l'utilisation de la réponse par AUTO générera probablement 48 instructions MOVB en .text. Pour moi, cela représente 48 * 7 == 336 octets de .text !!

  • (SMALLEST * _) Utilisez la version de memcpy ci-dessus. Si l'alignement est pris en charge, 

    • FOO_ASSIGN_VAL est placé dans .rodata (48 octets),
    • 12 MOVL dans% register 
    • 12 registres MOVL% sont utilisés en .text (24 * 10) == 240 octets. 
    • Pour moi, cela représente un total de 288 octets.

Donc, pour moi au moins avec mon code i386,

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

* Le plus petit signifie ici «la plus petite empreinte que je connaisse». Il s’exécute également plus rapidement que les méthodes ci-dessus (24 instructions contre 48). Bien sûr, la version IDEAL est la plus rapide et la plus petite, mais je ne le comprends toujours pas.

-Justin

* Est-ce que quelqu'un sait comment obtenir l'implémentation de 'IDÉAL' ci-dessus? C'est énervant de me faire foutre !!

2
J-Dizzle

Si vous vous souciez de l'efficacité, vous pouvez définir une union de la même longueur que votre structure, avec un type que vous pouvez attribuer à la fois.

Pour affecter des valeurs par éléments, utilisez la structure de votre union. Pour attribuer l'intégralité des données, utilisez l'autre type de votre union.

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

Faites attention à votre organisation de la mémoire ("est" le MSB ou le LSB de "tout"?).

0
Simon