wake-up-neo.com

wie kann man einer Struktur mehrere Werte gleichzeitig zuweisen?

Ich kann dies bei der Initialisierung für ein struct Foo tun:

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

aber ich kann das nicht machen:

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

Also zwei Fragen:

  1. Warum kann ich das nicht tun, ist das erst ein spezieller Konstruktor nur für die Initialisierung? 
  2. Wie kann ich etwas Ähnliches wie das zweite Beispiel machen, d. H. Eine Reihe von Variablen für eine Struktur in einer einzelnen Codezeile deklarieren, nachdem sie bereits initialisiert wurde? Ich versuche, dies für große Strukturen mit vielen Variablen zu vermeiden:

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

Der erste ist ein Aggregatinitialisierer - Sie können diese und markierte Initialisierer bei dieser Lösung nachlesen:

Was ist eine markierte Strukturinitialisierungssyntax?

Es ist eine spezielle Initialisierungssyntax, und Sie können nach der Initialisierung Ihrer Struktur nichts Ähnliches ausführen. Sie können eine Member- (oder Nicht-Member-) Funktion bereitstellen, um Ihre Wertereihe als Parameter zu verwenden, die Sie dann innerhalb der Member-Funktion zuweisen. Mit dieser Funktion können Sie dies tun, nachdem die Struktur auf eine gleichwertige Weise initialisiert wurde prägnant (nachdem Sie die Funktion natürlich beim ersten Mal geschrieben haben!)

Versuche dies:

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

Dies funktioniert, wenn Sie einen guten Compiler haben (z. B. GCC). Möglicherweise müssen Sie den C99-Modus mit --std=gnu99 aktivieren. Ich bin mir nicht sicher.

27
David Grayson

In C++ 11 können Sie mehrere Zuordnungen mit "tie" durchführen (im Tuple-Header deklariert).

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

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

Wenn der Ausdruck auf der rechten Seite eine feste Größe hat und Sie nur einige Elemente benötigen, können Sie den Platzhalter ignorieren mit Krawatte verwenden

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

Wenn Sie die Syntax std :: tie (f.a, f.b, f.c) finden, die den Code unübersichtlich machen, könnten Sie eine Member-Funktion haben, die diesen Tuple von Referenzen zurückgibt

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

Dies setzt natürlich voraus, dass das Überladen des Zuweisungsoperators keine Option ist, da Ihre Struktur durch eine solche Folge von Werten nicht konstruierbar ist. In diesem Fall können Sie dies sagen

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

Wenn Ihnen die Effizienz nicht zu wichtig ist, können Sie sie doppelt zuweisen, d. H. Eine neue Instanz der Struktur mithilfe der Aggregatinitialisierung erstellen, und diese dann kopieren:

 struct Foo foo; 

 {
 struct Foo __tmp__ = {Bündel von initialisierten Dingen}; 
 foo = __tmp __; 
} 

Stellen Sie sicher, dass der Teil in {} s eingeschlossen ist, um die unnötige temporäre Variable zu verwerfen, sobald sie nicht mehr erforderlich ist.

Beachten Sie, dass dies nicht so effizient ist wie das Erstellen einer Funktion (set) in der Struktur (wenn C++) oder außerhalb der Struktur, wobei ein Strukturzeiger (wenn C) akzeptiert wird. Wenn Sie jedoch eine schnelle, vorzugsweise temporäre Alternative zum Schreiben der Element-für-Element-Zuordnung benötigen, könnte dies eine Rolle spielen.

3
Codesmith

Memory Footprint - Hier ist ein interessanter i386-Zusatz.

Nach langem Ärger scheint die Verwendung von Optimierung und Memcpy den kleinsten Platzbedarf unter Verwendung von i386 mit GCC und C99 zu erzeugen. Ich verwende hier -O3. stdlib scheint alle möglichen Spaßmöglichkeiten für Compiler-Optimierungen zur Verfügung zu haben, und dieses Beispiel macht davon Gebrauch (Memcpy wird hier tatsächlich kompiliert).

Tun Sie dies durch:

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

Ergebnis:

  • (.rodata) FOO_ASSIGN_VAL wird in .rodata gespeichert
  • (.text) Eine Folge von * movl FOO_ASSIGN_VAL,% register * kommt vor
  • (.text) eine Folge von movl% Registern, foo kommt vor

Beispiel:

  • Angenommen, Foo war eine 48-Feldstruktur von uint8_t-Werten. Es ist im Speicher ausgerichtet.

  • (IDEAL) Auf einer 32-Bit-Maschine kann dieser KÖNNTE so schnell wie 12 MOVL-Anweisungen sein, die sofort in den Adressraum von foo gelangen. Für mich sind das 12 * 10 = 120Bytes Text.

  • (ACTUAL) Wenn Sie jedoch die Antwort von AUTO verwenden, werden wahrscheinlich 48 MOVB-Anweisungen in .text generiert. Für mich sind das 48 * 7 == 336 Byte .text !!

  • (SMALLEST *) Verwenden Sie die Memcpy-Version oben. WENN die Ausrichtung erfolgt ist, 

    • FOO_ASSIGN_VAL wird in .rodata (48 Byte) abgelegt.
    • 12 MOVL in% register 
    • 12 MOVL outof% -Register werden in .text (24 * 10) == = 240 Byte verwendet. 
    • Für mich sind das also insgesamt 288 Bytes.

Also für mich zumindest mit meinem i386-Code,

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

* Kleinste bedeutet "kleinster Footprint, den ich kenne". Es führt auch schneller aus als die oben genannten Methoden (24 Anweisungen vs. 48). Natürlich ist die IDEAL-Version am schnellsten und am kleinsten, aber ich kann das immer noch nicht herausfinden.

-Justin

* Weiß jemand, wie Sie die Implementierung von 'IDEAL' oben erhalten? Es nervt mich verdammt noch mal !!

2
J-Dizzle

Wenn Sie Wert auf die Effizienz legen, können Sie eine Vereinigung definieren, die dieselbe Länge wie Ihre Struktur hat, mit einem Typ, den Sie auf einmal zuweisen können.

Um Werte nach Elementen zuzuweisen, verwenden Sie die Struktur Ihrer Vereinigung. Um alle Daten zuzuweisen, verwenden Sie den anderen Typ Ihrer Vereinigung.

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

Seien Sie vorsichtig mit Ihrer Speicherorganisation (ist "ein" das MSB oder das LSB von "ganz"?).

0
Simon