2010-02-05 12 views
12

Si consideri il seguente codice:sizeof comportarsi in modo imprevisto

#include <stdio.h> 
    int main(void) 
    { 
    int a[10]; 
    printf("%d",(int)sizeof(a)); //prints 10*sizeof(int) (40 on my compiler) 
    printf("%d",(int)sizeof(a-3)); //prints sizeof(int) (4 on my compiler) 

    } 

So che sizeof() è un operatore di tempo di compilazione, ma sono rimasto sorpreso di vedere il risultato del secondo printf(). Quale potrebbe essere la ragione? Esiste una conversione implicita dell'argomento di sizeof() da un tipo di matrice a un tipo intero?

+3

Il secondo in realtà stampa 'sizeof (int *)', non 'sizeof (int)'. 'sizeof (int *)' capita anche di essere 4 sulla tua piattaforma. – AnT

+0

Inoltre, il modo corretto di stampare un valore 'size_t' (il valore restituito da' sizeof') è con '"% zu "', supponendo che il compilatore supporti quella particolare caratteristica C99. In caso contrario, una scommessa migliore sarebbe quella di lanciarla su un 'unsigned' o' unsigned long'. –

risposta

30

L'operatore sizeof non valuta la sua tesi, solo looks at the type del suo operando.

Supponiamo di disporre di un array a con tipo "array [N] di tipo T". Quindi, nella maggior parte dei casi, il tipo di nome a è "puntatore a T" (T *) e il valore del puntatore è l'indirizzo del primo elemento dell'array (&a[0]). Cioè, il nome di un array "decade" da un puntatore al suo primo elemento. Il "decomposizione" non avviene nei seguenti casi:

  • quando a viene utilizzato con l'operatore indirizzo-di (&),
  • nella inizializzazione a (è illegale assegnare agli array in C) e
  • quando a è l'operando dell'operatore sizeof.

Quindi, sizeof a ti dà N volte sizeof(T).

Quando si esegue sizeof(a-3), il tipo di operando su sizeof è determinato dall'espressione a-3. Poiché a in viene utilizzato in un value context (vale a dire, nessuno dei tre contesti sopra), il suo tipo è "puntatore a int" e il nome a decade in un puntatore a a[0]. Pertanto, il calcolo di a-3 è un comportamento non definito, ma dal momento che sizeof non valuta il suo argomento, a-3 viene utilizzato solo per determinare il tipo di operando, quindi il codice è OK (vedere il primo collegamento sopra per ulteriori informazioni).

Da quanto sopra, sizeof(a-3) equivale a sizeof(int *), che è 4 sul computer.

La "conversione" è dovuta all'operatore di sottrazione. Potete vedere una simile, e forse più sorprendente, risultato con l'operatore virgola:

printf("%zu\n", sizeof(1, a)); 

stampa anche sizeof(int *), a causa del l'operatore virgola con conseguente a abituarsi in un contesto di valore.

+0

+1 Molto più chiaro del mio. –

5

(a-3) ha il tipo int* e stampa sizeof(int*) che è 4 sulla piattaforma.

E notare che sizeof() non è più costante in fase di compilazione in C99 (a causa di array di lunghezza variabile).

+6

Per essere chiari - il risultato di 'sizeof' non è una costante di tempo del compilatore solo quando l'operando è una matrice di lunghezza variabile. È ancora una costante per altri tipi di operandi. –

1

No, nel secondo caso l'argomento viene interpretato come un puntatore int* che si verifica anche con dimensioni pari a 4 sulla macchina.

1

sizeof() restituisce la dimensione di un tipo, quindi il tipo è ciò che è importante.

Inoltre, non deve essere stampato con %d. Perlomeno, trasmetterlo esplicitamente a unsigned long o unsigned long long e utilizzare l'identificatore di formato appropriato. Quando insegnavo a C, ho avuto uno studente che ha ottenuto la risposta sbagliata stampando size_t con %d come erroneamente ha detto il libro di testo.

In ogni caso, a è un tipo di matrice. In C, i tipi di array decadono in tipi di puntatore se si fa quasi tutto con loro o si starnutisce forte, quindi quasi tutto ciò che si fa a a produce un tipo di puntatore. Come hai scoperto, l'aggiunta o la sottrazione di un numero decadranno. (Dopo tutto, un array non può essere usato in aritmetica, ma un puntatore può.)

+0

@David: finchè il risultato non trabocca, se si lancia l'espressione come '(int)', la stampa con '"% d "' va bene. In questo caso, è quasi certo che la dimensione si inserirà in un 'int'. In C99, si userebbe '"% zu "' ovviamente. Per C89, il tuo suggerimento è migliore della stampa con '"% d "' dopo il cast come '(int)' perché è più incline agli overflow. –

+0

@Alok: è vero, ma fare il casting è molto più importante di quello a cui hai scelto. Inoltre, ho molta familiarità con C89 e non ho usato C99, e probabilmente lo mostra. –

+0

L'OP ha il cast nella sua domanda. Sono d'accordo con te - senza il cast, '"% d "' è sbagliato, ma la chiamata 'printf()' dell'OP è OK fino a quando non c'è overflow. –

Problemi correlati