2012-02-13 29 views
33

posso fare questo su di inizializzazione per un Foo struct:come assegnare più valori in una struct in una volta?

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

ma, non posso fare questo:

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

Così, due domande:

  1. Perché puo' t Faccio quest'ultimo, il primo è un costruttore speciale solo per l'inizializzazione?
  2. Come posso fare qualcosa di simile al secondo esempio, cioè dichiarare un gruppo di variabili per una struttura in una singola riga di codice dopo che è già stato inizializzato? Sto cercando di evitare di dover fare questo per grandi le strutture con molte variabili:

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

"* è il precedente un costruttore speciale solo per l'inizializzazione? *" Sì, con precisione - si chiama _aggregazione inizializzazione_. – ildjarn

+8

C o C++? Sceglierne uno. –

+2

C (++) rimane molto primitivo in alcune aree. –

risposta

14

Il primo è un inizializzatore di aggregazione - si può leggere su quelli e contrassegnati inizializzatori a questa soluzione:

What is tagged structure initialization syntax?

Si tratta di una speciale sintassi di inizializzazione, e non si può fare qualcosa di simile dopo l'inizializzazione della tua struttura. Quello che puoi fare è fornire una funzione membro (o non membro) per prendere la tua serie di valori come parametri che poi assegni all'interno della funzione membro - ciò ti permetterebbe di ottenere ciò dopo che la struttura è stata inizializzata in un modo che è ugualmente conciso (dopo aver scritto la funzione la prima volta, ovviamente!)

+0

Ho trovato questa discussione facendo la stessa domanda: http://stackoverflow.com/questions/1705147/struct-initialization-of-the-c-c-programming-language. Tuttavia, tutte le risposte, insieme a Davids (che mi è piaciuto molto e le ho upvoted), sembravano essere specifiche del compilatore. Questo è qualcosa che posso effettivamente implementare sulla mia piattaforma, quindi ho scelto questa come risposta. Ringrazia tutti. – jim

25

Prova questo:

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

Ciò funzionerà se si dispone di un buon compilatore (ad esempio GCC) . Potrebbe essere necessario attivare la modalità C99 con --std=gnu99, non sono sicuro.

+0

molto interessante, sfortunatamente, non funziona sul mio (cattivo) compilatore: MSVC2008. :( – jim

+0

@jim: VC++ 2008 non è un compilatore "cattivo" (anche se certamente non è il migliore), ma non supporta C++ 11, che è necessario per quello che vuoi. – ildjarn

+1

Supports = good = GCC? È discutibile –

3

Se non ti preoccupi troppo dell'efficienza, potresti raddoppiarne l'assegnazione, ovvero creare una nuova istanza della struttura utilizzando l'inizializzazione di aggregazione, quindi copiarlo su:

 
struct Foo foo; 

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

assicurarsi di mantenere la parte avvolta in {} s in modo da scartare la variabile temporanea inutili non appena non è più necessario.

Nota questo non è efficiente come fare, ad esempio, una funzione 'set' nella struct (se C++) o fuori dalla struct, accettando un puntatore struct (se C). Ma se hai bisogno di un'alternativa rapida, preferibilmente temporanea, alla scrittura di un elemento elemento per elemento, questo potrebbe succedere.

+0

Anche i compilatori lo ottimizzeranno. –

+0

Perché fermarsi qui? 'struct Foo_noew (int bunch, int di, int things, int inizializzato) {struct Foo tmp = {bunch, of, things, initialized}; return tmp; }; struct Foo foo; foo = foo_new (1, 2, 3, 4); "Attaccalo in un file di intestazione come' inline' e l'impatto sulle prestazioni dovrebbe essere minimo con l'ottimizzazione. L'unico lato negativo è che non è possibile escludere qualsiasi cosa per essere azzerati alla fine (anche se alcuni potrebbero non considerare un aspetto negativo vista l'esistenza dell'avvertimento "initial-field-missing"). Ovviamente, lo stile '(Foo) {...}' è probabilmente il più semplice quando è disponibile. – JAB

2

Memory Footprint - Ecco un'interessante aggiunta di i386.

Dopo molta fatica, utilizzando ottimizzazione e memcpy sembra generare la più piccola orma utilizzando i386 con GCC e C99. Sto usando -O3 qui. stdlib sembra avere tutti i tipi di ottimizzazioni del compilatore divertenti a portata di mano, e questo esempio ne fa uso (la memcpy è in realtà compilata qui).

A tale scopo:

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

Risultato:

  • (.rodata) FOO_ASSIGN_VAL è memorizzato in .rodata
  • (.text) una sequenza di * movl FOO_ASSIGN_VAL ,% registers * occorrono
  • (.text) una sequenza di movl% re gisters, foo verificano

Esempio:

  • Say Foo era una struct 48 campo di valori uint8_t. È allineato in memoria.

  • (IDEALE) Su una macchina a 32 bit, questo POTREBBE essere il più velocemente 12 istruzioni movl di immediates fuori per lo spazio di indirizzi di pippo. Per me questo è 12 * 10 == 120bytes di dimensioni .text.

  • (ACTUAL) Tuttavia, utilizzando la risposta di AUTO è probabile che generi 48 istruzioni MOVB in .text. Per me questo è 48 * 7 == 336 byte di testo. !!

  • (SMALLEST *) Utilizzare la versione memcpy sopra. Se l'allineamento è preso cura di,

    • FOO_ASSIGN_VAL viene posto in .rodata (48 byte),
    • 12 MOVL nel registro%
    • 12 MOVL outof% registri vengono utilizzati in .text (24 * 10) == 240 byte.
    • Per me quindi questo è un totale di 288 byte.

Quindi, almeno per me con il mio codice i386,

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

* Il più piccolo qui significa 'impronta più piccolo che conosco'. Esegue anche più velocemente dei metodi precedenti (24 istruzioni vs 48). Naturalmente, la versione IDEAL è la più veloce &, ma non riesco ancora a capirlo.

-Justin

* Qualcuno sa come ottenere l'attuazione di 'IDEALE' di cui sopra? Mi sta dando fastidio!

7

In C++ 11 è possibile eseguire l'assegnazione multipla con "cravatta" (dichiarata nell'intestazione tupla)

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

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

Se la vostra espressione mano destra è di dimensione fissa e avete solo bisogno di ottenere alcuni dei elementi, è possibile utilizzare il segnaposto ignorare con cravatta

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

Se si trova lo std :: sintassi cravatta (FA, FB, FC) anche il codice ingombrare si potrebbe avere una funzione di membro di ritorno che tupla di riferimenti

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

Tutto questo naturalmente assumendo che sovraccaricare l'operatore di assegnazione non è un'opzione perché lo struct non è costruibile da tale sequenza di valori, nel qual caso si potrebbe dire

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

Se vi preoccupate per l'efficienza, è possibile definisci un'unione della stessa lunghezza della tua struttura, con un tipo che puoi assegnare contemporaneamente.

Per assegnare valori per elementi utilizzare la struttura della propria unione, per assegnare l'intero dato, utilizzare l'altro tipo di unione.

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 

fate attenzione circa la vostra organizzazione della memoria (è "a" MSB o LSB di "tutto"?).

Problemi correlati