Perché dovremmo scrivere una struttura così spesso in C?


407

Ho visto molti programmi costituiti da strutture come quella qui sotto

typedef struct 
{
    int i;
    char k;
} elem;

elem user;

Perché è necessario così spesso? Qualche motivo specifico o area applicabile?


14
Risposta più completa e precisa: stackoverflow.com/questions/612328/...
AbiusX

Ha degli svantaggi, penso che non sia possibile creare un elenco di collegamenti con una struttura anonima perché la riga struct * ptrall'interno della struttura causerà un errore
Raghib Ahsan,

9
La "risposta più completa e precisa" è la differenza tra struct e typedef struct in C ++ , e ci sono differenze significative tra C e C ++ in quest'area che rendono tale risposta non del tutto appropriata per una domanda su C.
Jonathan Leffler,

Questa domanda ha una duplice definizione typidef struct vs struct che ha anche risposte stellari.
Jonathan Leffler,

Risposte:


455

Come ha detto Greg Hewgill, il typedef significa che non devi più scrivere ovunque struct. Ciò non solo consente di risparmiare la pressione dei tasti, ma può anche rendere il codice più pulito poiché fornisce una smidgen più astrazione.

Cose come

typedef struct {
  int x, y;
} Point;

Point point_new(int x, int y)
{
  Point a;
  a.x = x;
  a.y = y;
  return a;
}

diventa più pulito quando non è necessario vedere la parola chiave "struct" in tutto il luogo, sembra più che ci sia davvero un tipo chiamato "Point" nella tua lingua. Il che, dopo il typedef, è il caso immagino.

Nota anche che mentre il tuo esempio (e il mio) ha omesso di nominare struct se stesso, in realtà è utile anche quando vuoi fornire un tipo opaco. Quindi avresti il ​​codice come questo nell'intestazione, ad esempio:

typedef struct Point Point;

Point * point_new(int x, int y);

e quindi fornire la structdefinizione nel file di implementazione:

struct Point
{
  int x, y;
};

Point * point_new(int x, int y)
{
  Point *p;
  if((p = malloc(sizeof *p)) != NULL)
  {
    p->x = x;
    p->y = y;
  }
  return p;
}

In quest'ultimo caso, non è possibile restituire il punto in base al valore, poiché la sua definizione è nascosta agli utenti del file di intestazione. Questa è una tecnica ampiamente utilizzata in GTK + , ad esempio.

AGGIORNAMENTO Si noti che ci sono anche progetti C molto apprezzati in cui questo uso di typedefnascondere structè considerato una cattiva idea, il kernel Linux è probabilmente il progetto più noto. Vedi il capitolo 5 del documento Linux Kernel CodingStyle per le parole arrabbiate di Linus. :) Il mio punto è che il "dovrebbe" nella domanda forse non è incastonato nella pietra, dopo tutto.


58
Non utilizzare identificatori con un carattere di sottolineatura seguito da una lettera maiuscola, sono riservati (vedere la sezione 7.1.3, paragrafo 1). Sebbene sia improbabile che costituisca un grosso problema, si tratta di un comportamento tecnicamente indefinito quando li si utilizza (7.1.3 paragrafo 2).
dreamlax,

16
@dreamlax: Nel caso in cui non fosse chiaro agli altri, questo sta solo iniziando un identificatore con un trattino basso e un maiuscolo che non dovresti fare; sei libero di usarlo nel mezzo di un identificatore.
brianmearns,

12
È interessante notare che l'esempio fornito qui (in cui il typedef impedisce l'uso di "struct" "ovunque") è in realtà più lungo dello stesso codice senza typedef, poiché salva esattamente un uso della parola "struct". Lo smidgen dell'astrazione guadagnato raramente si confronta con l'offuscamento aggiuntivo.
William Pursell,

7
@Rerito fyi, pagina 166 della bozza C99 , Tutti gli identificatori che iniziano con un carattere di sottolineatura e una lettera maiuscola o un altro carattere di sottolineatura sono sempre riservati per qualsiasi uso. E tutti gli identificatori che iniziano con un carattere di sottolineatura sono sempre riservati per l'uso come identificatori con ambito di file sia nello spazio dei nomi ordinario che in quello dei tag.
e2-e4,

10
È interessante notare che le linee guida per la codifica del kernel di Linux dicono che dovremmo essere molto più prudenti sull'uso di typedefs (sezione 5): kernel.org/doc/Documentation/CodingStyle
gsingh2011

206

È incredibile quante persone sbagliano. PER FAVORE non digitare le strutture in C, inquina inutilmente lo spazio dei nomi globale, che in genere è molto inquinato già nei grandi programmi C.

Inoltre, le strutture typedef'd senza un nome tag sono la causa principale dell'imposizione inutile delle relazioni di ordinamento tra i file di intestazione.

Prendere in considerazione:

#ifndef FOO_H
#define FOO_H 1

#define FOO_DEF (0xDEADBABE)

struct bar; /* forward declaration, defined in bar.h*/

struct foo {
  struct bar *bar;
};

#endif

Con una tale definizione, non usando typedefs, è possibile che un'unità compiland includa foo.h per arrivare alla FOO_DEFdefinizione. Se non tenta di dereferenziare il membro 'bar' della foostruttura, non sarà necessario includere il file "bar.h".

Inoltre, poiché gli spazi dei nomi sono diversi tra i nomi dei tag e i nomi dei membri, è possibile scrivere codice molto leggibile come:

struct foo *foo;

printf("foo->bar = %p", foo->bar);

Poiché gli spazi dei nomi sono separati, non vi sono conflitti nelle variabili di denominazione coincidenti con il nome del tag struct.

Se devo mantenere il tuo codice, rimuoverò le tue strutture dattiloscritte.


37
La cosa più sorprendente è che 13 mesi dopo aver dato questa risposta, sono il primo a votarla! la struttura di battitura a macchina è uno dei più grandi abusi di C e non ha spazio in un codice ben scritto. typedef è utile per de-offuscare i tipi di puntatori di funzioni convolute e in realtà non ha altri scopi utili.
William Pursell,

28
Anche Peter van der Linden si oppone a una battitura dattiloscritta nel suo libro illuminante "Expert C Programming - Deep C Secrets". L'essenza è: VUOI sapere che qualcosa è una struttura o unione, non NASCONDI.
Jens,

34
Lo stile di codifica del kernel Linux proibisce esplicitamente le strutture di battitura a macchina. Capitolo 5: Typedefs: "È un errore usare typedef per strutture e puntatori." kernel.org/doc/Documentation/CodingStyle
jasso

63
Quali vantaggi offre esattamente la "strutturazione" ripetuta? E a proposito di inquinamento, perché dovresti avere una struttura e una funzione / variabile / typedef con lo stesso nome in uno spazio dei nomi globale (a meno che non sia un typedef per quella stessa funzione)? Lo schema sicuro è da usare typedef struct X { ... } X. In questo modo è possibile utilizzare la forma abbreviata Xper indirizzare la struttura ovunque sia disponibile la definizione, ma è comunque possibile dichiarare e utilizzare in avanti struct Xse desiderato.
Pavel Minaev il

7
Personalmente molto raramente se mai dovessi usare typedef, non direi che altre persone non dovrebbero usarlo, non è solo il mio stile. Mi piace vedere una struttura prima di un tipo variabile, quindi so subito che è una struttura. L'argomento più facile da digitare è un po 'scadente, avere variabili che sono una singola lettera è anche più facile da scrivere, anche con i completamenti automatici quanto sia difficile digitare struct al giorno d'oggi ovunque.
Nathan Day

138

Da un vecchio articolo di Dan Saks ( http://www.ddj.com/cpp/184403396?pgno=3 ):


Le regole del linguaggio C per la denominazione delle strutture sono un po 'eccentriche, ma sono piuttosto innocue. Tuttavia, quando estese alle classi in C ++, quelle stesse regole aprono piccole crepe per la scansione dei bug.

In C, il nome appare in

struct s
    {
    ...
    };

è un tag. Il nome di un tag non è un nome di tipo. Data la definizione sopra, dichiarazioni come

s x;    /* error in C */
s *p;   /* error in C */

sono errori in C. È necessario scriverli come

struct s x;     /* OK */
struct s *p;    /* OK */

I nomi dei sindacati e delle enumerazioni sono anche tag anziché tipi.

In C, i tag sono distinti da tutti gli altri nomi (per funzioni, tipi, variabili e costanti di enumerazione). I compilatori C mantengono i tag in una tabella dei simboli concettualmente se non fisicamente separata dalla tabella che contiene tutti gli altri nomi. Pertanto, è possibile che un programma C abbia sia un tag che un altro nome con lo stesso spelling nello stesso ambito. Per esempio,

struct s s;

è una dichiarazione valida che dichiara variabili di tipo struct s. Potrebbe non essere una buona pratica, ma i compilatori C devono accettarlo. Non ho mai visto una logica per cui C è stato progettato in questo modo. Ho sempre pensato che fosse un errore, ma è così.

Molti programmatori (incluso il tuo) preferiscono pensare ai nomi di struttura come a nomi di tipo, quindi definiscono un alias per il tag usando un typedef. Ad esempio, definendo

struct s
    {
    ...
    };
typedef struct s S;

ti permette di usare S al posto di struct s, come in

S x;
S *p;

Un programma non può usare S come nome di un tipo e di una variabile (o funzione o costante di enumerazione):

S S;    // error

Questo è buono.

Il nome del tag in una definizione di struct, union o enum è facoltativo. Molti programmatori piegano la definizione della struttura nel typedef e rinunciano del tutto al tag, come in:

typedef struct
    {
    ...
    } S;

L'articolo collegato ha anche una discussione su come il comportamento C ++ di non richiedere un typedefpuò causare problemi di occultamento dei nomi sottili. Per prevenire questi problemi, è una buona idea anche per le typedeftue classi e strutture in C ++, anche se a prima vista sembra non essere necessario. In C ++, con il typedefnome nascosto diventa un errore di cui ti parla il compilatore piuttosto che una fonte nascosta di potenziali problemi.


2
Un esempio di dove il nome del tag è lo stesso di un nome non tag è nel programma (POSIX o Unix) con la int stat(const char *restrict path, struct stat *restrict buf)funzione. Lì hai una funzione statnello spazio dei nomi ordinario e struct statnello spazio dei nomi dei tag.
Jonathan Leffler,

2
la tua dichiarazione, SS; // errore .... È SBAGLIATO Funziona bene. Intendo la tua affermazione che "non possiamo avere lo stesso nome per i tag typedef e var" è SBAGLIATO ... per favore controlla
eRaisedToX

63

L'uso di a typedefevita di dover scrivere structogni volta che dichiari una variabile di quel tipo:

struct elem
{
 int i;
 char k;
};
elem user; // compile error!
struct elem user; // this is correct

2
ok non abbiamo questo problema in C ++. Quindi, perché nessuno rimuove quel problema tecnico dal compilatore di C e lo rende uguale a quello in C ++. Ok C ++ ha alcune aree di applicazione diverse e quindi ha le funzionalità avanzate. Ma non possiamo ereditarne alcune in C senza cambiare il C originale?
Manoj dubita il

4
Manoj, il nome del tag ("struct foo") è necessario quando è necessario definire una struttura che faccia riferimento a se stessa. ad es. il puntatore "successivo" in un elenco collegato. Più precisamente, il compilatore implementa lo standard, ed è quello che lo standard dice di fare.
Michael Carman,

41
Non è un problema tecnico nel compilatore C, fa parte del progetto. Lo hanno cambiato per C ++, che penso semplifichi le cose, ma ciò non significa che il comportamento di C sia sbagliato.
Herms,

5
sfortunatamente molti "programmatori" definiscono una struttura quindi la digitano con un nome "non correlato" (come struct myStruct ...; typedef struct myStruct susan *; In quasi tutti i casi una typedef non porta a nulla se non a ingombrare il codice, nascondendo la definizione effettiva di una variabile / parametro e conduce erroneamente tutti, incluso lo scrittore originale del codice.
user3629249,

1
@utente3629249 Concordo con te sul fatto che lo stile di programmazione menzionato sia orribile, ma questo non è un motivo per denigrare le typedefstrutture in generale. Potresti anche fare typedef struct foo foo;. Naturalmente la structparola chiave non è richiesta più di allora, il che può essere un suggerimento utile che l'alias di tipo che guardiamo è un alias per una struttura ma non è male in generale. Considerate anche un caso in cui l'identificatore del tipo alias risultante typedefindica che è un alias per una struttura, fe: typedef struct foo foo_struct;.
RobertS supporta Monica Cellio il

38

Un altro buon motivo per scrivere sempre enumerazioni e strutture risulta da questo problema:

enum EnumDef
{
  FIRST_ITEM,
  SECOND_ITEM
};

struct StructDef
{
  enum EnuumDef MyEnum;
  unsigned int MyVar;
} MyStruct;

Notare il refuso in EnumDef nella struttura (Enu u mDef)? Questo viene compilato senza errori (o avvisi) ed è (a seconda dell'interpretazione letterale dello standard C) corretto. Il problema è che ho appena creato una nuova definizione di enumerazione (vuota) all'interno della mia struttura. Non sto (come previsto) usando la definizione precedente EnumDef.

Con un typdef un simile tipo di errori di battitura avrebbe comportato errori del compilatore per l'utilizzo di un tipo sconosciuto:

typedef 
{
  FIRST_ITEM,
  SECOND_ITEM
} EnumDef;

typedef struct
{
  EnuumDef MyEnum; /* compiler error (unknown type) */
  unsigned int MyVar;
} StructDef;
StrructDef MyStruct; /* compiler error (unknown type) */

Vorrei sostenere SEMPRE strutture ed enumerazioni dattiloscritte.

Non solo per salvare un po 'di battitura (nessun gioco di parole previsto;)), ma perché è più sicuro.


1
Ancora peggio, il tuo refuso potrebbe coincidere con un tag diverso. Nel caso di una struttura ciò potrebbe comportare la compilazione corretta dell'intero programma e un comportamento indefinito in fase di esecuzione.
MM

3
questa definizione: 'typedef {FIRST_ITEM, SECOND_ITEM} EnumDef;' non definisce un enum. Ho scritto centinaia di programmi enormi e ho avuto la sfortuna di eseguire interventi di manutenzione su programmi scritti da altri. Dalla dura mano dell'esperienza, l'uso di typedef su una struttura porta solo a problemi. Si spera che il programmatore non sia così handicappato da avere problemi a digitare una definizione completa quando dichiarano un'istanza di struct. C non è di base, quindi digitare qualche altro carattere non è dannoso per il funzionamento del programma.
user3629249,

2
Per coloro che provano un po 'di orrore nel scrivere più del numero minimo assoluto di caratteri, posso suggerire di unirmi a qualche gruppo che cerca di scrivere applicazioni con il numero minimo di battute. Basta non usare la loro nuova "abilità" in un ambiente di lavoro, in particolare un ambiente di lavoro che esegue rigorosamente revisioni tra pari
user3629249,

Nota, di solito è sconsigliato l'uso enumcome tipo, poiché molti compilatori emettono strani avvisi quando li usano "sbagliato". Ad esempio, l'inizializzazione di uno enuma 0 potrebbe dare un avviso "costante intera non in enum". Non enumè inoltre consentito dichiarare in avanti an . Si dovrebbe usare int(o unsigned int) invece.
yyny,

5
Questo esempio non viene compilato, né me lo sarei aspettato. Compilazione Debug / test.o test.c: 10: 17: errore: il campo ha un tipo incompleto 'enum EnuumDef' enum EnuumDef MyEnum; ^ test.c: 10: 8: nota: dichiarazione anticipata di 'enum EnuumDef' enum EnuumDef MyEnum; ^ 1 errore generato. gnuc, con std = c99.
natersoz,

30

Stile di codifica del kernel Linux Il capitolo 5 offre grandi vantaggi e svantaggi (principalmente contro) dell'uso typedef.

Per favore, non usare cose come "vps_t".

È un errore usare typedef per strutture e puntatori. Quando vedi a

vps_t a;

nella fonte, cosa significa?

Al contrario, se dice

struct virtual_container *a;

puoi effettivamente dire cos'è "a".

Molte persone pensano che dattilografia "aiuti la leggibilità". Non così. Sono utili solo per:

(a) oggetti totalmente opachi (dove il typedef è attivamente usato per nascondersi ciò che l'oggetto è).

Esempio: "pte_t" ecc. Oggetti opachi a cui è possibile accedere solo utilizzando le funzioni di accesso appropriate.

NOTA! Opacità e "funzioni accessorie" non sono buone in se stesse. Il motivo per cui li abbiamo per cose come pte_t ecc. È che ci sono davvero zero informazioni accessibili in modo portabile lì.

(b) Cancella tipi interi, in cui l'astrazione aiuta a evitare confusione se è "int" o "lungo".

u8 / u16 / u32 sono dattiloscritti perfettamente fini, sebbene rientrino nella categoria (d) meglio di qui.

NOTA! Ancora una volta - ci deve essere una ragione per questo. Se qualcosa è "unsigned long", non c'è motivo di farlo

typedef unsigned long myflags_t;

ma se c'è una chiara ragione per cui in determinate circostanze potrebbe essere un "unsigned int" e in altre configurazioni potrebbe essere "unsigned long", allora vai avanti e usa un typedef.

(c) quando si usa sparse per creare letteralmente un nuovo tipo per il controllo del tipo.

(d) Nuovi tipi identici ai tipi C99 standard, in determinate circostanze eccezionali.

Anche se ci vorrebbe solo una breve quantità di tempo per abituare gli occhi e il cervello ai tipi standard come 'uint32_t', alcune persone si oppongono comunque al loro uso.

Pertanto, sono consentiti i tipi "u8 / u16 / u32 / u64" specifici di Linux e i loro equivalenti firmati che sono identici ai tipi standard, sebbene non siano obbligatori nel nuovo codice personale.

Quando si modifica il codice esistente che utilizza già l'uno o l'altro set di tipi, è necessario conformarsi alle scelte esistenti in quel codice.

(e) Tipi sicuri per l'uso nello spazio utente.

In alcune strutture che sono visibili allo spazio utente, non possiamo richiedere i tipi C99 e non possiamo usare il modulo 'u32' sopra. Pertanto, usiamo __u32 e tipi simili in tutte le strutture condivise con userspace.

Forse ci sono anche altri casi, ma la regola dovrebbe fondamentalmente essere quella di NON MAI MAI MAI usare un typedef a meno che non sia possibile abbinare chiaramente una di quelle regole.

In generale, un puntatore o una struttura che ha elementi a cui si può ragionevolmente accedere direttamente non dovrebbe mai essere un refuso.


4
"L'opacità e le" funzioni di accesso "non sono buone in se stesse". Qualcuno può spiegare perché? Penso che nascondere e incapsulare le informazioni sarebbe un'ottima idea.
Yawar il

5
@Yawar Ho appena letto questo documento e ho avuto esattamente lo stesso pensiero. Certo, C non è orientato agli oggetti, ma l'astrazione è ancora una cosa.
Baldrickk,

12

Si scopre che ci sono pro e contro. Un'utile fonte di informazioni è il libro fondamentale "Expert C Programming" ( Capitolo 3 ). In breve, in C hai più spazi dei nomi: tag, tipi, nomi dei membri e identificatori . typedefintroduce un alias per un tipo e lo localizza nello spazio dei nomi dei tag. Vale a dire,

typedef struct Tag{
...members...
}Type;

definisce due cose. Un tag nello spazio dei nomi dei tag e un tipo nello spazio dei nomi dei tipi. Così si può fare entrambe le cose Type myTypee struct Tag myTagType. Dichiarazioni simili struct Type myTypeo Tag myTagTypeillegali. Inoltre, in una dichiarazione come questa:

typedef Type *Type_ptr;

definiamo un puntatore al nostro Tipo. Quindi se dichiariamo:

Type_ptr var1, var2;
struct Tag *myTagType1, myTagType2;

quindi var1, var2e myTagType1sono puntatori a Tipo ma myTagType2non.

Nel libro sopra menzionato, menziona che le strutture di battitura a macchina non sono molto utili in quanto salva il programmatore solo dalla scrittura della parola struct. Tuttavia, ho un'obiezione, come molti altri programmatori C. Anche se a volte si trasforma in offuscamento di alcuni nomi (ecco perché non è consigliabile in basi di codice di grandi dimensioni come il kernel) quando si desidera implementare il polimorfismo in C, aiuta molto a cercare qui i dettagli . Esempio:

typedef struct MyWriter_t{
    MyPipe super;
    MyQueue relative;
    uint32_t flags;
...
}MyWriter;

tu puoi fare:

void my_writer_func(MyPipe *s)
{
    MyWriter *self = (MyWriter *) s;
    uint32_t myFlags = self->flags;
...
}

Quindi puoi accedere a un membro esterno ( flags) dalla struttura interna ( MyPipe) attraverso il casting. Per me è meno confuso lanciare l'intero tipo che farlo (struct MyWriter_ *) s;ogni volta che si desidera eseguire tale funzionalità. In questi casi, un breve riferimento è molto importante soprattutto se si utilizza fortemente la tecnica nel proprio codice.

Infine, l'ultimo aspetto con i typedeftipi di ed è l'incapacità di estenderli, in contrasto con le macro. Se ad esempio hai:

#define X char[10] or
typedef char Y[10]

puoi quindi dichiarare

unsigned X x; but not
unsigned Y y;

Non ci interessa davvero per le strutture perché non si applica agli specificatori di archiviazione ( volatilee const).


1
MyPipe *s; MyWriter *self = (MyWriter *) s;e hai appena rotto il rigoroso aliasing.
Jonathon Reinhart,

@JonathonReinhart Sarebbe illustrativo menzionare come ciò possa essere evitato, ad esempio come la GTK + enormemente felice cast lavora attorno ad esso: bugzilla.gnome.org/show_bug.cgi?id=140722 / mail.gnome.org/archives/gtk -devel-list / 2004-aprile / msg00196.html
underscore_d

"typedef introduce un alias per un tipo e lo localizza nello spazio dei nomi dei tag. Vale a dire" typedef struct Tag{ ...members... }Type; "definisce due cose" non ha molto senso. se typedef definisce i tag, anche 'Type' qui dovrebbe essere un tag. La verità è che la definizione definisce 2 tag e 1 di tipo (o 2 tipi e 1 tag non è sicuro.): struct Tag, TagE Type. struct Tagè sicuramente un tipo. Tagè un tag. ma la confusione è se si Typetratta di un tag o di un tipo
Qwertyzw,

12

Non credo che le dichiarazioni a termine siano neppure possibili con typedef. L'uso di struct, enum e union consente l'inoltro di dichiarazioni quando le dipendenze (a conoscenza) sono bidirezionali.

Stile: l'uso di typedef in C ++ ha abbastanza senso. Può quasi essere necessario quando si tratta di modelli che richiedono parametri multipli e / o variabili. Il typedef aiuta a mantenere la denominazione corretta.

Non così nel linguaggio di programmazione C. L'uso di typedef molto spesso non ha altro scopo se non quello di offuscare l'utilizzo della struttura dei dati. Poiché per dichiarare un tipo di dati vengono utilizzati solo {struct (6), enum (4), union (5)} numero di tasti, non è quasi utile per l'aliasing della struttura. Questo tipo di dati è un'unione o una struttura? L'uso della semplice dichiarazione non tipizzata ti consente di sapere subito di che tipo è.

Notate come Linux viene scritto con il rigoroso elusione di questo aliasing typedef senza senso. Il risultato è uno stile minimalista e pulito.


10
Clean non si ripeterebbe structdappertutto ... Typedef crea nuovi tipi. Cosa usi? Tipi. Non ci importa se si tratta di una struttura, unione o enum, ecco perché lo scriviamo.
GManNickG

13
No, noi facciamo importa se si tratta di una struttura o unione, contro un enum o un certo tipo atomico. Non è possibile forzare una struttura in un numero intero o in un puntatore (o in qualsiasi altro tipo, per quella materia), che è tutto ciò che a volte è necessario memorizzare un certo contesto. Avere le parole chiave "struct" o "union" in giro migliora la località del ragionamento. Nessuno dice che devi sapere cosa c'è dentro la struttura.
Bernd Jendrissek il

1
@BerndJendrissek: Le strutture e i sindacati sono diversi dagli altri tipi, ma il codice del cliente dovrebbe preoccuparsi di quale di queste due cose (struttura o unione) sia qualcosa come un FILE?
supercat

4
@supercat FILE è un buon uso di typedef. Penso che typedef sia abusato , non che sia un malfunzionamento della lingua. IMHO che usa typedef per tutto è l'odore del codice "speculative overgenerality". Si noti che si dichiarano le variabili come FILE * foo, mai come FILE foo. Per me questo conta.
Bernd Jendrissek,

2
@supercat: "Se le variabili identificative dei file fossero di tipo FILE anziché FILE * ..." Ma questa è esattamente l'ambiguità che abilita typedefs! Siamo abituati a riprendere a prendere un FILE *, quindi non ci preoccupiamo, ma ogni volta che aggiungi typedef stai introducendo un altro po 'di sovraccarico cognitivo: questa API vuole foo_t args o foo_t *? Trasportare esplicitamente la "struttura" migliora la località del ragionamento, se al costo di qualche carattere in più per definizione di funzione.
Bernd Jendrissek,

4

Cominciamo con le basi e procediamo verso l'alto.

Ecco un esempio di definizione della struttura:

struct point
  {
    int x, y;
  };

Qui il nome pointè facoltativo.

Una struttura può essere dichiarata durante la sua definizione o dopo.

Dichiarazione durante la definizione

struct point
  {
    int x, y;
  } first_point, second_point;

Dichiarazione dopo la definizione

struct point
  {
    int x, y;
  };
struct point first_point, second_point;

Ora, nota attentamente l'ultimo caso sopra; devi scrivere struct pointper dichiarare Strutture di quel tipo se decidi di creare quel tipo in un secondo momento nel tuo codice.

Enter typedef. Se hai intenzione di creare una nuova struttura (Structure è un tipo di dati personalizzato) in un secondo momento nel tuo programma usando lo stesso modello, usarlo typedefdurante la sua definizione potrebbe essere una buona idea poiché puoi salvare un po 'di battitura andando avanti.

typedef struct point
  {
    int x, y;
  } Points;

Points first_point, second_point;

Un avvertimento durante la denominazione del tipo personalizzato

Nulla ti impedisce di utilizzare il suffisso _t alla fine del nome del tipo personalizzato, ma lo standard POSIX si riserva l'uso del suffisso _t per indicare i nomi dei tipi di libreria standard.


3

il nome che (facoltativamente) dai alla struttura è chiamato nome del tag e, come è stato notato, non è un tipo in sé. Per arrivare al tipo è necessario il prefisso struct.

GTK + a parte, non sono sicuro che il tagname sia usato come qualcosa di simile al typedef al tipo di struttura, quindi in C ++ questo è riconosciuto e puoi omettere la parola chiave struct e usare anche il tagname come nome del tipo:


    struct MyStruct
    {
      int i;
    };

    // The following is legal in C++:
    MyStruct obj;
    obj.i = 7;


1

typedef non fornirà un insieme co-dipendente di strutture di dati. Questo non si può fare con typdef:

struct bar;
struct foo;

struct foo {
    struct bar *b;
};

struct bar {
    struct foo *f;
};

Ovviamente puoi sempre aggiungere:

typedef struct foo foo_t;
typedef struct bar bar_t;

Qual è esattamente il punto di ciò?


1

A> a typdef aiuta nel significato e nella documentazione di un programma consentendo la creazione di sinonimi più significativi per i tipi di dati . Inoltre, aiutano a parametrizzare un programma contro i problemi di portabilità (K&R, pg147, C prog lang).

B> una struttura definisce un tipo . Structs consente un raggruppamento conveniente di una raccolta di var per comodità di gestione (K&R, pg127, lingua prog.) Come una singola unità

C> la battitura a macchina di una struttura è spiegata in A sopra.

D> Per me, le strutture sono tipi o contenitori personalizzati o raccolte o spazi dei nomi o tipi complessi, mentre un typdef è solo un mezzo per creare più soprannomi.


0

Si scopre che è richiesto typedef C99. È obsoleto, ma molti strumenti (ala HackRank) usano c99 come sua pura implementazione in C. E typedef è richiesto lì.

Non sto dicendo che dovrebbero cambiare (forse hanno due opzioni C) se il requisito è cambiato, quelli di noi che studiano per le interviste sul sito sarebbero SOL.


2
" typedefÈ necessario in C99 ." Cosa intendi?
Julien Lopez,

La domanda riguarda Cno C++. In Ctypedefs sono "obbligatori" (e molto probabilmente lo saranno sempre). 'Richiesto' come in, non sarai in grado di dichiarare una variabile Point varName;e far sì che il tipo sia sinonimo di struct Point;senza typedef struct Point Point;.
yyny,

0

Nel linguaggio di programmazione "C" la parola chiave "typedef" viene utilizzata per dichiarare un nuovo nome per alcuni oggetti (struct, array, function..enum type). Ad esempio, userò una 'struct-s'. In "C" spesso dichiariamo una "struttura" al di fuori della funzione "principale". Per esempio:

struct complex{ int real_part, img_part }COMPLEX;

main(){

 struct KOMPLEKS number; // number type is now a struct type
 number.real_part = 3;
 number.img_part = -1;
 printf("Number: %d.%d i \n",number.real_part, number.img_part);

}

Ogni volta che decido di utilizzare un tipo di struttura, ho bisogno di questa parola chiave "struct" qualcosa "" nome "." Typedef "rinominerà semplicemente quel tipo e posso usare quel nuovo nome nel mio programma ogni volta che voglio. Quindi il nostro codice sarà:

typedef struct complex{int real_part, img_part; }COMPLEX;
//now COMPLEX is the new name for this structure and if I want to use it without
// a keyword like in the first example 'struct complex number'.

main(){

COMPLEX number; // number is now the same type as in the first example
number.real_part = 1;
number.img)part = 5;
printf("%d %d \n", number.real_part, number.img_part);

}

Se hai qualche oggetto locale (struct, array, prezioso) che verrà utilizzato nell'intero programma, puoi semplicemente assegnargli un nome usando un 'typedef'.


-2

A tutti, in linguaggio C, struct / union / enum sono macroistruzione elaborate dal preprocessore del linguaggio C (non confondere con il preprocessore che tratta "#include" e altri)

così :

struct a
{
   int i;
};

struct b
{
   struct a;
   int i;
   int j;
};

la struttura b viene spesa come questa:

struct b
{
    struct a
    {
        int i;
    };
    int i;
    int j;
}

e così, in fase di compilazione, si evolve in pila come qualcosa del tipo: b: int ai int i int j

questo è anche il motivo per cui è difficile avere strutture autoreferenti, preprocessore C arrotondato in un ciclo di dichiarazione che non può terminare.

typedef è un identificatore di tipo, ciò significa che solo il compilatore C lo elabora e può fare come vuole per ottimizzare l'implementazione del codice assembler. Inoltre, non spende membri di tipo par stupidamente come fanno i preprocessori con le strutture ma usa algoritmi di costruzione di riferimento più complessi, quindi costruzioni come:

typedef struct a A; //anticipated declaration for member declaration

typedef struct a //Implemented declaration
{
    A* b; // member declaration
}A;

è consentito e perfettamente funzionante. Questa implementazione dà anche accesso alla conversione del tipo di compilatore e rimuove alcuni effetti di bugging quando il thread di esecuzione lascia il campo di applicazione delle funzioni di inizializzazione.

Ciò significa che in C i typedef sono più vicini alla classe C ++ che alle strutture solitarie.


3
Non è affatto difficile avere strutture autoreferenziali. struct foo {struct foo * successivo; cosa int; }
Bernd Jendrissek,

4
...che cosa? Dire il preprocessore per descrivere la risoluzione di structsand typedefs è abbastanza male, ma il resto dei tuoi scritti è così confuso che trovo difficile ottenere qualsiasi messaggio da esso. Una cosa che posso dire, tuttavia, è che l'idea che un non- typedefd structnon possa essere dichiarato in avanti o utilizzato come membro opaco (puntatore) è totalmente falsa. Nel tuo primo esempio, struct bpuò contenere banalmente un struct a *, non typedefrichiesto. Le affermazioni che structsono semplicemente stupide parti di macro-espansione, e che typedefdanno loro nuovi poteri rivoluzionari, sono dolorosamente errate
underscore_d
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.