2009-11-23 8 views
7

Dato il codice seguente:somiglianze e le differenze tra vettori e puntatori attraverso un esempio pratico

#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 
    int a[1]; 
    int * b = malloc(sizeof(int)); 

    /* 1 */ 
    scanf("%d", &a); 
    printf("%d\n", a[0]); 

    /* 2 */ 
    scanf("%d", &b); 
    printf("%d\n", b[0]); 

    return 0; 
} 

i seguenti avvisi si ottengono quando si compila (i686-apple-darwin9-gcc-4.0.1):

array.c: In function 'main': 
array.c:9: warning: format '%d' expects type 'int *', but argument 2 has type 'int (*)[0u]' 
array.c:14: warning: format '%d' expects type 'int *', but argument 2 has type 'int **' 

ma, perché un'esecuzione errori si verificano nella seconda printf, nel frattempo lavora per la prima printf?

Ancor più, perché si ottiene lo stesso risultato, se la prima scanf è sostituito da scanf ("% d", a);?

Grazie molto in anticipo

risposta

10

Nella maggior parte dei contesti, un'espressione di tipo di matrice sarà convertita implicitamente da un "array di elementi N di T" a "puntatore a T" e il suo valore verrà impostato in modo che punti al primo elemento dell'array. Le eccezioni a questa regola sono quando la matrice è un operando degli operatori & o sizeof o se la matrice è una stringa letterale utilizzata per inizializzare un'altra matrice in una dichiarazione.

Quindi, in che modo tutto ciò si riferisce al codice?

Nella linea

scanf("%d", &a); 

Si applica l'operatore & alla matrice. Questo sopprime la conversione implicita da "array di T" a "puntatore a T" e restituisce un valore di tipo "puntatore a matrice di T" o T (*)[N] (da qui il primo avviso). Ora si scopre che il valore di un puntatore a un array e il valore di un puntatore al primo elemento dell'array sono gli stessi, hanno solo tipi diversi.Quindi, supponendo che a è all'indirizzo 0x0001000:

expression  type   value   note 
----------  ----   -----   ---- 
     a  int *   0x0001000  implicitly converted to pointer 
     &a  int (*)[N] 0x0001000  
    &a[0]  int *   0x0001000 

Ecco perché la prima chiamata a scanf() "funziona"; stai passando il puntatore destro valore, ma il compilatore si sta lamentando perché il tipo dell'espressione non corrisponde a ciò che la funzione si aspetta. Avevi scritto

scanf("%d", a); 

non avrebbe ricevuto alcun avviso, dal momento che il tipo di a saranno adottate per essere int *, che è ciò scanf() aspetta. Si noti che questo è identico a chiamare

scanf("%d", &a[0]); 

Per quanto riguarda b ...

Si dichiara esplicitamente b come un puntatore a int e assegnare un blocco di memoria ad esso. Quando si applica l'operatore &, viene restituito l'indirizzo della variabile b con il tipo int ** (da cui il secondo avviso), non l'indirizzo a cui fa riferimento b.

expression  type   value   note 
----------  ----   -----   ---- 
     b  int *   0x0040000  value contained in b 
     &b  int **  0x0001000  address of b 

Per questo caso, è sufficiente passare il undecorated b:

scanf("%d", b); 
5

L'array a è posto sulla pila, l'indirizzo del primo elemento è lo stesso come l'indirizzo di un. & a [0] e & a è lo stesso indirizzo

L'array b è allocato con malloc, l'indirizzo per la memoria è sul mucchio, mentre l'indirizzo per il puntatore b è in pila. & b [0] non è lo stesso indirizzo di & b.

Ecco perché il primo scanf e printf funziona ma non il secondo.

Il C-FAQ lo spiega in modo molto più dettagliato.

2

Nel primo scanf si passa un riferimento a un array. Nei matrici C sono i puntatori a un blocco di memoria del tipo allocato, nel tuo caso int * e un'espressione come a[0] viene convertita in *(a + 0) (che btw dà origine alla variante divertente 0[a] che verrà effettivamente compilata.) Questo array è allocato nello stack . Il secondo array è allocato sull'heap e lo stack contiene la variabile pointer su quell'array.

In entrambi i casi non si passa un puntatore alla prima voce dell'array, ma rispettivamente all'array e al puntatore all'array.

Il tuo primo scanf sovrascrive quello che è l'array, dato che è allocato nello stack, il tuo valore finisce (per fortuna) nell'array.

Il secondo scanf sovrascrive il puntatore sull'array, cambiando il puntatore in un indirizzo di memoria che probabilmente non esiste nel segmento di dati. Ciò comporta l'errore di esecuzione.

1

Questo è normale ...

Per prima cosa, scanf richiede un puntatore. "a" e "b" sono già indicatori! Quindi:

/* 1 */ 
scanf("%d", a); 
printf("%d\n", a[0]); 

/* 2 */ 
scanf("%d", b); 
printf("%d\n", b[0]); 

funzionerà.

Normalmente/* 1 */non dovrebbe funzionare. Ma gcc trasforma "& a" di "a" perché "& a" non ha alcun senso.

printf("&a = %p\n", &a); 
printf("a = %p\n", a); 
printf("&b = %p\n", &b); 
printf("b = %p\n", b); 

&a = 0x7ffff6be67d0 
a = 0x7ffff6be67d0 
&b = 0x7ffff6be67c8 
b = 0xb0b010 

Non è possibile prendere l'indirizzo di a. Ma b è una "variabile normale" di tipo puntatore, e quindi puoi prendere il suo indirizzo "& b".

On/* 2 */si sta inserendo il valore inserito dall'utente in b e quindi, * b (o b [0]) si arresta in modo anomalo a meno che l'utente inserisca un indirizzo di memoria leggibile valido.

1

Nel tuo caso, ciò che sta accadendo è che stai passando entrambe le variabili a e b con l'operatore & alla funzione scanf. Ciò che questo operatore fa è "chiedere" l'indirizzo di memoria della variabile e passare quell'indirizzo alla funzione scanf. Ma, dato che entrambe le variabili sono puntatori, quello che hanno effettivamente è un indirizzo di memoria, quindi quando passi & a o & b stai passando la memoria del puntatore, non l'indirizzo di memoria che contiene.

Esempio:

int x; 
int *ptr; 

x = 10; 

supporre l'indirizzo di memoria di x è 1000. si memorizza il numero 10 presso l'indirizzo di memoria 1000. Ora si esegue questa operazione:

ptr = &x; 

si memorizza la indirizzo 1000 nel puntatore. Ma 1000, a parte essere un indirizzo, è un numero stesso, quindi il puntatore, così come x, ha ancora bisogno di un indirizzo di memoria per memorizzare quell'informazione. Supponiamo che la posizione di memoria del puntatore è 1004. Ora guardate l'esempio:

*ptr == 10; //x content 
ptr == 1000 //x memory address 
&ptr == 1004 // ptr memory address. 

Quindi, se si desidera passare a scanf alla variabile x, ma utilizzando il puntatore, è necessario passare l'indirizzo x memorizzato in esso

scanf("%d", ptr); 

Solo per ilustrate un altro esempio di puntatori e vettori

int main 
{ 
    int vet[5]; 
    int *ptr; 

    ptr = vet; 

    for(int i = 0; i < 5; ++i) 
    { 
     scanf("%d", (ptr+i)); 
    } 
} 

Qui potete leggere il vettore utilizzando il puntatore. Inoltre, usando l'aritmetica del puntatore è possibile scorrere gli indirizzi di memoria del vettore.

Problemi correlati