2010-02-12 17 views
21

Quando si lavora con matrici e puntatori in C, si scopre rapidamente che non sono affatto equivalenti, anche se potrebbe sembrare così a prima vista. Conosco le differenze nei valori L e nei valori R. Ancora, recentemente ho cercato di trovare il tipo di un puntatore che ho potuto usare in combinazione con una matrice bidimensionale, IEInterpretazione di int (* a) [3]

int foo[2][3]; 
int (*a)[3] = foo; 

Tuttavia, non riesco proprio scoprire come il compilatore "capisce" la digitare la definizione di a nonostante le normali regole di precedenza degli operatori per * e []. Se invece dovessi usare un typedef, il problema diventa molto più semplice:

int foo[2][3]; 
typedef int my_t[3]; 
my_t *a = foo; 

In linea di fondo, qualcuno mi può rispondere alle domande su come il termine int (*a)[3] viene letto dal compilatore? int a[3] è semplice, int *a[3] è semplice. Ma allora, perché non è int *(a[3])?

MODIFICA: Certamente, invece di "typecast" intendevo "typedef" (era solo un refuso).

+0

Potresti essere interessato a leggere http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/pointer.html. – sand

+0

Solo una piccola parte del collegamento fornito è correlata alla mia domanda originale che è stata già risolta in dettaglio dalle risposte di seguito. Grazie comunque per la tua risposta !! – fotNelton

+0

Vedere anche [Leggere dichiarazioni di tipo C] (http://unixwiz.net/techtips/reading-cdecl.html), [Leggere le dichiarazioni C: una guida per i mistificati] (http://www.ericgiguere.com/articles /reading-c-declarations.html). – outis

risposta

18

In primo luogo, si intende "typedef" non "typecast" nella domanda.

In C, un puntatore a T può puntare a un oggetto di tipo T:

int *pi; 
int i; 
pi = &i; 

Quanto sopra è semplice da capire. Ora, rendiamolo un po 'più complesso. Sembra che tu sappia la differenza tra array e puntatori (ad esempio, sai che gli array non sono puntatori, a volte si comportano come loro). Quindi, si dovrebbe essere in grado di comprendere:

int a[3]; 
int *pa = a; 

Ma per completezza: nell'assegnazione, il nome a equivale a &a[0], vale a dire, un puntatore al primo elemento della matrice a. Se non siete sicuri su come e perché funziona, ci sono molte risposte che spiegano esattamente quando il nome di un array "decadimenti" ad un puntatore e quando non lo fa:

Sono sicuro che ci sono molte altre domande e risposte su SO, ho appena menzionato alcune che ho trovato da una ricerca.

Torna il tema: quando abbiamo:

int foo[2][4]; 

foo è di tipo "serie [2] di serie [3] di int". Ciò significa che foo[0] è un array di 3 int s e foo[1] è un array di 3 int s.

Ora diciamo che vogliamo dichiarare un puntatore e vogliamo assegnarlo a foo[0]. Cioè, vogliamo fare:

/* declare p somehow */ 
p = foo[0]; 

Quanto sopra non è diverso in forma alla linea int *pa = a;, perché i tipi di a e di foo[0] sono gli stessi. Quindi, abbiamo bisogno di int *p; come nostra dichiarazione di p.

Ora, la cosa principale da ricordare sugli array è che "la regola" sul nome dell'array che decompone a un puntatore al suo primo elemento si applica solo una volta. Se si dispone di una matrice di un array, quindi in contesti di valore, il nome dell'array non decadrà sul tipo "puntatore al puntatore", ma su "puntatore all'array". Tornando al foo:

/* What should be the type of q? */ 
q = foo; 

Il nome foo sopra è un puntatore al primo elemento di foo, cioè, possiamo scrivere quanto sopra come:

q = &foo[0]; 

Il tipo di foo[0] è "matrice [3] di int ".Quindi abbiamo bisogno q di essere un puntatore ad una "serie [3] di int": sono necessari

int (*q)[3]; 

Le parentesi attorno q perché [] si lega più strettamente di * in C, quindi int *q[3] dichiara q come un array di puntatori, e vogliamo un puntatore a un array. int *(q[3]) è, da sopra, equivalente a int *q[3], cioè un array di 3 puntatori a int.

Spero che questo aiuti. Dovresti anche leggere C for smarties: arrays and pointers per un ottimo tutorial su questo argomento.

Informazioni sulla lettura di dichiarazioni in generale: le leggi "inside-out", a partire dal nome della "variabile" (se ce n'è una). Andate a sinistra il più possibile a meno che non ci sia un [] alla destra immediata, e onorate sempre le parentesi. cdecl dovrebbe essere in grado di aiutarvi in ​​misura:

$ cdecl 
cdecl> declare p as pointer to array 3 of int 
int (*p)[3] 
cdecl> explain int (*p)[3] 
declare p as pointer to array 3 of int 

Per leggere

int (*a)[3]; 

     a   # "a is" 
    (*)   # parentheses, so precedence changes. 
        # "a pointer to" 
     [3]  # "an array [3] of" 
int  ;  # "int". 

Per

int *a[3]; 

    a    # "a is" 
     [3]   # "an array [3] of" 
    *    # can't go right, so go left. 
        # "pointer to" 
int  ;   # "int". 

Per

char *(*(*a[])())() 

      a   # "a is" 
      []  # "an array of" 
     *   # "pointer to" 
     ( )() # "function taking unspecified number of parameters" 
     (*  ) # "and returning a pointer to" 
       () # "function" 
char *    # "returning pointer to char" 

(Esempio da c-faq question 1.21 In pra. ce, se stai leggendo una dichiarazione così complicata, c'è qualcosa di gravemente sbagliato nel codice!)

+0

+1 per spiegare il decadimento del nome dell'array su un puntatore al primo elemento si applica solo una volta. – Andy

+0

Grazie mille per la tua risposta dettagliata. In effetti conoscevo già l'array e il puntatore, ma non sapevo nulla della regola how-to-read-type-declarations. Esempi molto belli! – fotNelton

2

Perché cosa è "non int *(a[3])" esattamente (in riferimento alla tua ultima domanda)? Lo int *(a[3]) è lo stesso del semplice int *a[3]. Le parentesi sono ridondanti. È un array di 3 puntatori a int e hai detto di sapere cosa significa.

Il int (*a)[3] è un puntatore a un array di 3 int (ovvero un puntatore al tipo int[3]). Le parentesi graffe in questo caso sono importanti. Lei stesso ha fornito un esempio corretto che effettua una dichiarazione equivalente attraverso un intermedio typedef.

In questo caso, la semplice regola di lettura delle dichiarazioni in C all'interno di questo metodo funziona perfettamente. Nella dichiarazione int *a[3] dovremmo iniziare da a e andare prima a destra, vale a dire ottenere a[3], che significa che a è una matrice di 3 qualcosa (e così via). Loin int (*a)[3] cambia, quindi non possiamo andare a destra e dobbiamo iniziare da *a invece: a è un puntatore a qualcosa. Continuando a decifrare la dichiarazione arriveremo alla conclusione che il qualcosa è un array di 3 int.

In ogni caso, trovare un buon tutorial sulla lettura di dichiarazioni C, di cui ce ne sono molte disponibili in Rete.

4

Probabilmente è più semplice da interpretare per il compilatore di quanto lo sia per voi. Un linguaggio definisce un insieme di regole per ciò che costituisce un'espressione valida e il compilatore usa queste regole per interpretare tali espressioni (potrebbe sembrare complicato ma i compilatori sono stati studiati in lunghezza e ci sono molti algoritmi e strumenti standardizzati disponibili, potresti voler leggi circa parsing). cdecl è uno strumento che tradurrà le espressioni C in inglese. Il codice sorgente è disponibile sul sito Web in modo da poterlo verificare. Il libro The Programming Programming di C++ includeva codice di esempio per scrivere un programma del genere se non mi sbaglio.

Per quanto riguarda l'interpretazione queste espressioni te stesso, ho trovato la migliore tecnica per farlo nel libro Thinking in C Volume ++ 1:

Per definire un puntatore ad una funzione che non ha argomenti e non restituisce alcun valore, tu dici:

void (* funcPtr)();

quando si sta guardando ad un complesso definizione in questo modo, il modo migliore per attacco è quello di iniziare a metà e il tuo lavoro fuori. "A partire da il mezzo" significa che inizia con il nome della variabile , che è funcPtr. “Lavorare la via d'uscita” significa guardare verso destra per l'elemento più vicino (nulla in questo caso, la parentesi destra si ferma a corto), quindi guardando a sinistra (un puntatore denotato dal asterisco), poi guardando al a destra (un elenco di argomenti vuoto che indica una funzione che non accetta argomenti ), quindi guardando a sinistra (vuoto, che indica la funzione non ha valore di ritorno). Questo movimento destra-sinistra-destra funziona con la maggior parte delle dichiarazioni .

Per rivedere, “start nel mezzo” (“funcptr è un ...”), andare a destra (niente là - si sta fermati dalla parentesi destra), andare a sinistra e trovare il '*' ("... puntatore a un ..."), andare a destra e trovare l'elenco degli argomenti vuoto ("... funzione che non accetta argomenti ..."), vai a sinistra e trovare il vuoto ("funcPtr è un puntatore a una funzione che non accetta argomenti e restituisce vuoto").

È possibile scaricare il libro gratuitamente su Bruce Eckel's download site. Proviamo ad applicarlo a int (*a)[3]:

  1. Inizia dal nome nel mezzo, in questo caso l'a. Finora si legge: 'a is a ...'
  2. Vai a destra, ti capita una parentesi chiusa che ti ferma, quindi vai a sinistra e trova *. Ora lo leggi: 'a è un puntatore a ...'
  3. Vai a destra e trovi [3], che significa un array di 3 elementi. L'espressione diventa: 'a è un puntatore a un array di 3 ...'
  4. Finalmente si va a sinistra e si trova l'int, quindi alla fine si ottiene 'a è un puntatore a un array di 3 pollici'.
+0

Grazie, anche questo si riduce a ciò che intendevo chiedere. – fotNelton

2

Si possono trovare utile questo articolo:

Ho fatto questo rispondere a una comunità wiki (in quanto è solo un link ad un articolo, non è una risposta), se qualcuno vuole aggiungere ulteriori risorse ad esso.

22

Dichiara un puntatore a un array di 3 int s.

Le parentesi sono necessarie come il seguente dichiara un array di 3 puntatori a int:

int* a[3]; 

Si ottiene una migliore leggibilità quando si utilizza typedef:

typedef int threeInts[3]; 
threeInts* pointerToThreeInts; 
+1

+1 Per valutare la leggibilità. –

+0

Scusa, ma non capisco il tuo punto. Questo è già stato detto nel PO? – fotNelton

+0

@kapuzineralex: Tutte le risposte dal 15 aprile sono state unite qui da mods da un'altra domanda e non mi sono davvero adattato qui credo:/ –

53

Ogni volta che si hanno dubbi con dichiarazioni complesse , è possibile utilizzare lo strumento cdecl in sistemi Unix:

[/tmp]$ cdecl 
Type `help' or `?' for help 
cdecl> explain int (*a)[10]; 
declare a as pointer to array 10 of int 

EDIT:

C'è anche una versione online di questo strumento a disposizione here.

Grazie a Tiberiu Ana e gf

+3

Brillante, ho imparato qualcosa, grazie! – Thomas

+3

wow Non sapevo questo, fantastico +1 – ant

+2

Non dimenticare il cdecl online: http://cdecl.org/ –

6

significa

dichiarare un puntatore a come matrice 3 su int

Vedere cdecl per riferimento futuro.

+0

Grazie, ma ero consapevole di cosa significasse. La mia domanda mirava al compilatore, non al significato finale dell'espressione. – fotNelton

2

In situazioni in cui non si conosce la dichiarazione, ho trovato molto utile il cosiddetto Right-left rule.

BTW. Una volta che l'hai imparato, tali dichiarazioni sono la pace del dolce :)