2015-01-09 13 views
31

Cercando un certo codice mi sono reso conto che il seguente codice compilato:Come restituire una struttura anonima in C?

struct { int x, y; } foo(void) { 
} 

Sembra come se stiamo definendo una funzione denominata foo che restituisce un anonimo struct.

Ora, la mia domanda è: si verifica solo con il mio compilatore o è legale C (99)? In tal caso, qual è la sintassi corretta per un'istruzione return e come posso assegnare correttamente il valore restituito a una variabile?

+0

sembra che un po 'di hack di gcc.With C99 rigorosa sta dando l'errore http://ideone.com/665vM2 – Ankur

+4

@Shan: la sintassi della definizione della funzione è ancora legale. Si lamenta semplicemente della dichiarazione di reso mancante (e la mia domanda chiede come eseguire una dichiarazione di reso corretta in questa situazione). – Askaga

+0

Non esistono strutture anonimo nel C99. Puoi creare _compiuti letterali_ ma hanno sempre un ambito locale. – Lundin

risposta

23

La struttura che stai restituendo non è una struttura anonima. Lo standard C definisce una struttura anonima come membro di un'altra struttura che non utilizza un tag. Quello che stai restituendo è una struttura senza tag, ma dal momento che non è un membro, non è anonimo. Gcc utilizza il nome < anonimo> per indicare una struttura senza tag.

Diciamo che si tenta di dichiarare una struttura identica nella funzione.

struct { int x, y; } foo(void) 
{ 
    return (struct { int x, y; }){ 0 } ; 
} 

gcc si lamenta: tipi incompatibili In caso di restituzione di tipo 'struct < anonima>' ma 'struct < anonima>' era previsto

A quanto pare i tipi non sono compatibili. Guardando nello standard vediamo che:

6.2.7 tipo compatibile e tipo composito

1: Due tipi hanno tipo compatibile se i loro tipi sono gli stessi. Ulteriori regole per determinare se due tipi sono compatibili sono descritti in 6.7.2 per gli specificatori di tipi, in 6.7.3 per i qualificatori di tipi e in 6.7.6 per i dichiaratori. Inoltre, due tipi di struttura, unione o enumerazione dichiarati in unità di traduzione separata sono compatibili se i tag e i membri soddisfano i seguenti requisiti: Se uno viene dichiarato con un tag, l'altro deve essere dichiarato con lo stesso tag.Se entrambi sono completati in qualsiasi punto all'interno delle rispettive unità di traduzione, si applicano i seguenti requisiti aggiuntivi: deve esserci una corrispondenza uno a uno tra i membri in modo tale che ciascuna coppia di membri corrispondenti sia dichiarata con tipi compatibili; se un membro della coppia è dichiarato con un identificatore di allineamento, l'altro è dichiarato con un identificatore di allineamento equivalente; e se un membro della coppia è dichiarato con un nome, l'altro è dichiarato con lo stesso nome. Per due strutture, i membri corrispondenti devono essere dichiarati nello stesso ordine. Per due strutture o unioni, i campi di bit corrispondenti devono avere le stesse larghezze. Per due enumerazioni, i membri corrispondenti devono avere gli stessi valori.

La seconda parte in grassetto, spiega che se entrambe le struct sono prive del tag, come in questo esempio, devono seguire i requisiti aggiuntivi elencati in seguito a tale parte, cosa che fanno. Ma se noti la prima parte in grassetto che devono essere in unità di traduzione separate, le strutture nell'esempio non lo sono. Quindi non sono compatibili e il codice non è valido.

E 'impossibile per rendere il codice corretto in quanto se si dichiara una struct e utilizzarlo in questa funzione, è necessario utilizzare un tag, che viola la regola che entrambi hanno le strutture devono avere lo stesso tag:

struct t { int x, y; } ; 

struct { int x, y; } foo(void) 
{ 
    struct t var = { 0 } ; 

return var ; 
} 

Ancora gcc si lamenta: tipi incompatibili quando il tipo 'struct t' ma tornando 'struct < anonima>' che ci si aspettava

+0

Hmm, interessante non ho mai letto quella parte dello standard prima. "Due tipi hanno un tipo compatibile se i loro tipi sono gli stessi" mi sembra una totale assurdità (e potrebbe applicarsi a questa situazione, chissà), ma non è quello che volevo commentare. Posso concludere che sebbene i due tipi nel tuo esempio non siano compatibili, sarebbero ** entrambi ** compatibili con un tipo simile definito in una diversa unità di traduzione? E quindi che la compatibilità non è una relazione transitiva? –

+0

@MarcvanLeeuwen I tipi devono essere uguali, con molte eccezioni. Le strutture senza tag sarebbero compatibili se definite in diverse unità di traduzione. – 2501

8

Probabilmente non può esplicitamentereturn qualche valore aggregato dalla vostra funzione (a meno che non si utilizza un'estensione typeof per ottenere il tipo di risultato).

La morale della storia è che, anche se è possibile dichiarare una funzione che restituisce un anonimastruct si dovrebbe praticamentemai farlo.

Invece, il nome di codice struct e:

struct twoints_st { int x; int y; }; 
struct twoints_st foo (void) { 
    return ((struct twoints_st) {2, 3}); 
}; 

Avviso che è sintatticamente ok, ma il comportamento generalmente definito in esecuzione, per avere una funzione senza return (ad esempio, si può chiamare exit al suo interno). Ma perché vorresti codificare il codice (probabilmente legale):

struct { int xx; int yy; } bizarrefoo(void) { exit(EXIT_FAILURE); } 
12

Questo funziona nella mia versione di GCC, ma sembra un hack totale. Forse utile nel codice generato automaticamente in cui non si vuole affrontare la complessità aggiuntiva della generazione di tag di struttura univoci, ma mi sto sforzando di trovare anche quella razionalizzazione.

struct { int x,y; } 
foo(void) { 
    typeof(foo()) ret; 
    ret.x = 1; 
    ret.y = 10; 
    return ret; 
} 

main() 
{ 
    typeof(foo()) A; 

    A = foo(); 

    printf("%d %d\n", A.x, A.y); 
} 

Inoltre, è dipendente da typeof() essendo presente nel compilatore - GCC e LLVM sembrano sostenerlo, ma sono sicuro che molti compilatori non lo fanno.

+1

Questa tecnica potrebbe essere più ragionevole di quanto si pensi. Guarda come il linguaggio D ha una comoda sintassi per esso: [https://dpaste.dzfl.pl/c5880e348812](https://dpaste.dzfl.pl/c5880e348812) – Cauterite

1

Questo funziona fino alla versione più recente di GCC. È particolarmente utile per la creazione di array dinamici con macro. Per esempio:

#define ARRAY_DECL(name, type) struct { int count; type *array; } name 

allora si può fare la matrice con realloc, ecc Questo è utile perché allora si può creare un array dinamico con qualsiasi tipo, e non v'è un modo per rendere tutti loro. Altrimenti si finirebbe usando un sacco di void * e poi si scrivono le funzioni per ottenere effettivamente i valori di ritorno con i cast e così via. Puoi scorciatoiare tutto con macro; questa è la loro bellezza

0

Oppure si potrebbe creare una ricorsione infinita:

struct { int x, y; } foo(void) { 
    return foo(); 
} 

Che credo è completamente legale.

+0

Che ne dici di restituire un puntatore al tipo di struct? Alla prima invocazione, malloc un po 'di memoria e memorizza un puntatore in un vuoto globale *. Dopodiché, restituisci semplicemente il vuoto globale *. Penso che se uno facesse quello potrebbe effettivamente usare il tipo uno dichiarato. – supercat

0

Ecco un modo per restituire le strutture anonime in C++ 14 senza alcun hack che ho appena scoperto.
(C++ 11 dovrebbe essere sufficiente, suppongo)
Nel mio caso una funzione intersect() rendimenti std::pair<bool, Point> il che non è molto descrittivo così ho deciso di fare un tipo personalizzato per il risultato.
Avrei potuto creare uno struct separato ma non ne valeva la pena dato che ne avrei avuto bisogno solo per questo caso speciale; è per questo che ho usato una struttura anonima.

auto intersect(...params...) { 
    struct 
    { 
     Point point; 
     bool intersects = false; 
    } result; 

    // do stuff... 

    return result; 
} 

Ed ora, al posto del brutto

if (intersection_result.first) { 
    Point p = intersection_result.second 

posso usare il molto più bello:

if (intersection_result.intersects) { 
    Point p = intersection_result.point; 
+0

La domanda riguarda C e non C++. – Ruslan