2011-01-05 2 views
34

La mia comprensione era che gli array erano semplicemente puntatori costanti di una sequenza di valori, e quando hai dichiarato una matrice in C, stavi dichiarando un puntatore e assegnando lo spazio per la sequenza a cui punta.In C, sono puntatori di array o utilizzati come puntatori?

Ma questo mi confonde: il seguente codice:

char y[20]; 
char *z = y; 

printf("y size is %lu\n", sizeof(y)); 
printf("y is %p\n", y); 
printf("z size is %lu\n", sizeof(z)); 
printf("z is %p\n", z); 

quando compilato con Apple GCC ha pronunciato la seguente risultato:

y size is 20 
y is 0x7fff5fbff930 
z size is 8 
z is 0x7fff5fbff930 

(la mia macchina è a 64 bit, i puntatori sono lunghi 8 byte) .

Se "y" è un puntatore costante, perché ha una dimensione di 20, come la sequenza di valori a cui punta? Il nome della variabile 'y' è sostituito da un indirizzo di memoria durante il tempo di compilazione quando è appropriato? Gli array sono, quindi, una sorta di zucchero sintattico in C che viene appena tradotto in una puntatore quando viene compilato?

+2

possibile duplicato di una domanda dal C++ del sito - faq: [Il nome dell'array è un puntatore in C?] (Http://stackoverflow.com/questions/1641957/is-array-name-a-pointer-in- c) –

risposta

49

Ecco la lingua esatta dallo standard C (n1256):

6.3.2.1 lvalue, array e designatori funzione
...
3 Tranne quando è l'operando della sizeof operatore o l'unario operatore & o è un valore letterale stringa utilizzato per inizializzare un array, un'espressione con tipo '' array di tipo '' viene convertita in un'espressione con tipo '' puntatore su tipo ’Che punti all'elemento iniziale dell'oggetto matrice e non è un lvalue. Se l'oggetto array ha una classe di archiviazione di registro, il comportamento non è definito.

La cosa importante da ricordare è che c'è una differenza tra un oggetto (in termini di C, che significa qualcosa che occupa memoria) e l'espressione usato per riferirsi a tale oggetto.

Quando si dichiara una matrice come

int a[10]; 

l'oggetto designato dal espressionea è una matrice (cioè, un blocco contiguo di memoria abbastanza grande da contenere 10 valori int e il tipo di espressione a è "matrice 10 elementi di int" o int [10]. Se l'espressione a viene visualizzata in un contesto diverso da quello degli operandi degli operatori sizeof o &, il suo tipo viene convertito implicitamente in int * e il suo valore è l'indirizzo del primo elemento.

Nel caso dell'operatore sizeof, se l'operando è un'espressione di tipo T [N], allora il risultato è il numero di byte nell'oggetto matrice, non in un puntatore a tale oggetto: N * sizeof T.

Nel caso dell'operatore &, il valore è l'indirizzo della matrice, che è la stessa come l'indirizzo del primo elemento della matrice, ma il tipo dell'espressione è differente: data la dichiarazione T a[N];, il tipo di espressione &a è T (*)[N] o puntatore all'array di elementi N di T. Il valore è lo stesso di a o &a[0] (l'indirizzo dell'array è uguale all'indirizzo del primo elemento nel array), ma la differenza nei tipi è importante. Ad esempio, dato il codice

int a[10]; 
int *p = a; 
int (*ap)[10] = &a; 

printf("p = %p, ap = %p\n", (void *) p, (void *) ap); 
p++; 
ap++; 
printf("p = %p, ap = %p\n", (void *) p, (void *) ap); 

vedrete uscita dell'ordine di

p = 0xbff11e58, ap = 0xbff11e58 
p = 0xbff11e5c, ap = 0xbff11e80 

IOW, avanzando p aggiunge sizeof int (4) al valore originale, mentre avanzando ap aggiunge 10 * sizeof int (40).

Più linguaggio standard:

6.5.2.1 Array indicizzazione di

Vincoli

1 Una delle espressioni avranno tipo '' puntatore a oggetto tipo '', l'altra espressione deve avere il tipo intero e il risultato ha tipo '' tipo ''.

semantica

2 un'espressione postfissa seguita da un'espressione tra parentesi quadre [] è una designazione subscripted di un elemento di un oggetto array. La definizione dell'operatore di pedice [] è che E1[E2] è identico a (*((E1)+(E2))). A causa delle regole di conversione applicabili all'operatore + binario, se E1 è un oggetto array (equivalentemente, un puntatore all'elemento iniziale di un oggetto matrice) e E2 è un numero intero, E1[E2] indica l'elemento E2 -th di E1 (conteggio da zero).

Così, quando si pedice un'espressione di matrice, ciò che accade sotto il cofano è che l'offset dall'indirizzo del primo elemento dell'array è calcolato e il risultato è dereferenziato.L'espressione

a[i] = 10; 

equivale a

*((a)+(i)) = 10; 

che equivale a

*((i)+(a)) = 10; 

che equivale a

i[a] = 10; 

Sì, matrice indicizzazione di in C è commutativo; per amore di Dio, non farlo mai nel codice di produzione.

Dal gamma subscripting è definito in termini di operazioni di puntatore, è possibile applicare l'operatore pedice alle espressioni di tipo puntatore così come tipo di matrice:

int *p = malloc(sizeof *p * 10); 
int i; 
for (i = 0; i < 10; i++) 
    p[i] = some_initial_value(); 

Ecco una tabella utile per ricordare alcuni di questi concetti:

 
Declaration: T a[N]; 

Expression Type Converts to  Value 
---------- ---- ------------ ----- 
     a T [N] T *    Address of the first element in a; 
             identical to writing &a[0] 
     &a T (*)[N]    Address of the array; value is the same 
             as above, but the type is different 
    sizeof a size_t     Number of bytes contained in the array 
             object (N * sizeof T) 
     *a T      Value at a[0] 
     a[i] T      Value at a[i] 
    &a[i] T *      Address of a[i] 

Declaration: T a[N][M]; 

Expression  Type  Converts to  Value 
----------  ----  ------------ ----- 
      a T [N][M] T (*)[M]  Address of the first subarray (&a[0]) 
     &a T (*)[N][M]     Address of the array (same value as 
              above, but different type) 
    sizeof a size_t      Number of bytes contained in the 
              array object (N * M * sizeof T) 
     *a T [M]  T *    Value of a[0], which is the address 
              of the first element of the first subarray 
              (same as &a[0][0]) 
     a[i] T [M]  T *    Value of a[i], which is the address 
              of the first element of the i'th subarray 
     &a[i] T (*)[M]     Address of the i-th subarray; same value as 
              above, but different type 
sizeof a[i] size_t      Number of bytes contained in the i'th subarray 
              object (M * sizeof T) 
     *a[i] T       Value of the first element of the i'th 
              subarray (a[i][0]) 
    a[i][j] T       Value at a[i][j] 
    &a[i][j] T *       Address of a[i][j] 

Declaration: T a[N][M][O]; 

Expression  Type    Converts to 
----------  ----    ----------- 
     a  T [N][M][O]  T (*)[M][O] 
     &a  T (*)[N][M][O] 
     *a  T [M][O]   T (*)[O] 
     a[i]  T [M][O]   T (*)[O] 
    &a[i]  T (*)[M][O] 
    *a[i]  T [O]   T * 
    a[i][j]  T [O]   T * 
    &a[i][j]  T (*)[O] 
    *a[i][j]  T 
a[i][j][k]  T 

Da qui, il modello per gli array di dimensioni superiori deve essere chiaro.

Quindi, in breve: gli array non sono puntatori. Nella maggior parte dei contesti, le espressioni dell'array vengono convertite in tipi di puntatore.

23

Le matrici non sono puntatori, sebbene nella maggior parte delle espressioni un nome di matrice valuti un puntatore al primo elemento dell'array. Quindi è molto, molto facile usare un nome di array come puntatore. Vedrai spesso il termine "decadimento" usato per descriverlo, come in "l'array decaduto in un puntatore".

Un'eccezione è l'operando per l'operatore sizeof, in cui il risultato è la dimensione dell'array (in byte, non in elementi).

Un ulteriore paio di questioni legate a questo:

un parametro array a una funzione è una finzione - il compilatore passa veramente un puntatore normale (questo non si applica parametri di riferimento a-array in C++) , quindi non è possibile determinare la dimensione effettiva di un array passato a una funzione - è necessario passare tali informazioni in un altro modo (magari utilizzando un parametro aggiuntivo esplicito o utilizzando un elemento sentinella - come le stringhe C)

Inoltre, un idioma comune per ottenere il numero di elementi in una matrice è utilizzare una macro come:

#define ARRAY_SIZE(arr) ((sizeof(arr))/sizeof(arr[0])) 

Questo ha il problema di accettare o il nome di un array, dove funzionerà, o un puntatore, dove darà un risultato senza senso senza preavviso dal compilatore. Esistono versioni più sicure della macro (in particolare per C++) che generano un avviso o un errore quando viene utilizzato con un puntatore anziché un array. Vedere i seguenti articoli SO:


Nota: C99 VLA (array di lunghezza variabile) potrebbe non seguire tutte queste regole (in particolare, possono essere passati come parametri con la dimensione dell'array conosciuta dalla funzione chiamata). Ho poca esperienza con gli VLA e, per quanto ne so, non sono ampiamente utilizzati. Tuttavia, voglio sottolineare che la discussione di cui sopra potrebbe applicarsi in modo diverso agli VLA.

+0

quindi è stata fatta l'eccezione sizeof perché era utile ... non sapevo che esistesse un modo per conoscere le dimensioni di un array! (anche se non è ancora così utile perché trova solo la dimensione degli array con dimensioni fisse, ma suppongo sia meglio che definire molte costanti per lo stesso scopo) –

6

sizeof viene valutato in fase di compilazione e il compilatore sa se l'operando è una matrice o un puntatore. Per gli array fornisce il numero di byte occupati dall'array. Il tuo array è un char[] (e sizeof(char) è 1), quindi sizeof capita di darti il ​​numero di elementi.Per ottenere il numero di elementi nel caso generale, un linguaggio comune (qui per int):

int y[20]; 
printf("number of elements in y is %lu\n", sizeof(y)/sizeof(int)); 

Per puntatori sizeof indica il numero di byte occupati dal tipo di puntatore grezzo.

-1

Se 'y' è un puntatore costante, perché deve un formato di 20, come la sequenza di valori a cui punta ?

Perché z è l'indirizzo della variabile e restituirà sempre 8 per la macchina. È necessario utilizzare il puntatore di dereferenziazione (&) per ottenere il contenuto di una variabile.

EDIT: Una buona distinzione tra i due: http://www.cs.cf.ac.uk/Dave/C/node10.html

+0

Lui sta chiedendo di y, e tu stai rispondendo a proposito di z, che è confuso . È chiaro perché z ha una dimensione di 8. Non è chiaro per l'OP perché no; a ciò non date una risposta. Inoltre, '&' è l'operatore di indirizzo in C; l'operatore di dereferenziazione è '*'. –

1

In

char hello[] = "hello there" 
int i; 

e

char* hello = "hello there"; 
int i; 

Nel primo caso (attualizzazione allineamento) 12 byte verrà memorizzato per ciao con lo spazio allocato inizializzato a ciao lì mentre nel secondo ciao ci viene memorizzato altrove (possibilmente spazio statico) e hello viene inizializzato in modo che punti alla stringa specificata.

hello[2] e *(hello + 2) restituiranno 'e' in entrambi gli esempi.