2013-10-05 12 views
10

gcc 4.8 mi danno un errore quando ho costruireerrore snprintf. argomento per sizeof è la stessa come meta

#include <string.h> 
#include <stdio.h> 

static inline void toto(char str[3]) 
{ 
    snprintf(str, sizeof(str), "XX"); 
} 

int main(){ 
    char str[3]; 
    toto(str); 
    return 0; 
} 

Qui è l'errore gcc

errore: argomento 'sizeof' a chiamata 'snprintf' è il stessa espressione della destinazione; intendevi fornire una lunghezza esplicita?

Nota: utilizzo i flag -Wall -Werror che convertono l'avviso in errore.

There is something similar here In un commento, qualcuno ha risposto questo

"per i buffer di lunghezza fissa, io di solito uso strncpy (dest, src, sizeof (dest)); dest [sizeof (dest) -1] = ' \ 0 '; Ciò garantisce la terminazione NULL ed è meno problematico di snprintf, senza contare che molte persone usano snprintf (dest, sizeof (dest), src) e invece sono molto sorpresi quando i loro programmi si bloccano in modo arbitrario. "

Ma questo è sbagliato: gcc 4.8 dicono

"errore:? Argomento 'sizeof' a chiamata 'strncpy' è la stessa espressione come destinazione; Cercavi per fornire una lunghezza esplicita [ -Werror = sizeof-pointer-MemAccess]"

in gcc 4.8 documentation, they are talking about this issue: dicono:

il comportamento di -Wall è cambiato e ora include la nuova bandiera di avvertimento -Wsizeof-p ointer-MemAccess. Ciò potrebbe comportare nuovi avvertimenti nel codice compilati in modo pulito con le versioni precedenti di GCC.

Per esempio,

include string.h 

struct A { }; 

int main(void) 
{ 
    A obj; 
    A* p1 = &obj; 
    A p2[10]; 

    memset(p1, 0, sizeof(p1)); // error 
    memset(p1, 0, sizeof(*p1)); // ok, dereferenced 
    memset(p2, 0, sizeof(p2)); // ok, array 
    return 0; 
} 

ha pronunciato la seguente diagnosi: avvertimento: argomento 'sizeof' in 'vuoto memset (void *, int, size_t)' chiamata è la stessa espressione come il destinazione; intendevi dereferenziarlo? [-Wsizeof-pointer-memaccess] memset (p1, 0, sizeof (p1)); // error ^ Sebbene questi avvisi non causino errori di compilazione, spesso viene utilizzato WALL in combinazione con -Werror e, di conseguenza, i nuovi avvisi vengono trasformati in nuovi errori. Per correggere, o riscrivere per utilizzare memcpy o dereferenziare l'ultimo argomento nella chiamata memset offendente. *

Bene, nel loro esempio, è ovvio che il codice era sbagliato, ma nel mio caso, con snprintf/strncpy, Non vedo perché, e penso che sia un errore falso positivo di gcc. Destra ?

grazie per il vostro aiuto

+0

Stranamente, la documentazione per '-Wsizeof-pointer-memaccess' mi fa pensare che ti avvertirebbe se avessi scritto' char * str' nel tuo primo esempio, ma che non dovrebbe avvisare per 'char str [3 ] '. – hobbs

+0

con g ++, non ho ricevuto alcun errore. – PersianGulf

+0

Con gcc 4.8.1, non è stato possibile riprodurre l'avviso. Sicuramente stai usando 'char str [3]' e non 'char * str'? –

risposta

8

Un array decade in un puntatore al primo elemento quando si passato a una funzione. Quindi, quello che hai in

static inline void toto(char str[3]) {..}

non è un array, ma un puntatore.

Quindi, gcc giustamente avverte.

Se si specifica la dimensione nel parametro funzione o no non importa come:

static inline void toto(char str[3]) 

e

static inline void toto(char str[]) 

e

static inline void toto(char *str) 

sono tutte equivalenti.

Leggi qui su questo: what is array decaying?

4

test.c:

#include <stdio.h> 

void bar(char foo[1000]) 
{ 
    printf ("sizeof foo = %d\n", (int)(sizeof foo)); 
} 

int main() 
{ 
    char foo[1000]; 
    bar(foo); 
} 

esecuzione:

bash $ ./test 
4 
bash $ 

Ecco perché.

1
static inline void toto(char str[3]) 

definisce una funzione che può assumere un array di qualsiasi dimensione. Lo 3 viene ignorato e lo str viene considerato come un puntatore (prova printf("%d\n", sizeof(str)): sulla mia macchina, stampa 8 per la dimensione di un puntatore a 64 bit). Il compilatore ha effettivamente ragione in questo caso.

Clang dà un avvertimento utile qui:

test.c:6:25: warning: sizeof on array function parameter will return size of 
     'char *' instead of 'char [3]' [-Wsizeof-array-argument] 
    snprintf(str, sizeof(str), "XX"); 
2

Nella definizione di funzione, la dichiarazione del parametro:

static inline void toto(char str[3]) 

non dichiara str come un array (C non dispone di parametri di tipo array). Piuttosto, è esattamente equivalente a:

static inline void toto(char *str) 

Il 3 è tranquillamente ignorato.

Quindi sizeof(str) non ha nulla a che fare con il numero di caratteri che la stringa può contenere, è semplicemente la dimensione di un puntatore char*.

Questo risulta da una regola che dice che un parametro dichiarato con un tipo di matrice è "regolato" per essere di tipo puntatore. Questo è distinto dalla regola che dice che le espressioni del tipo di matrice sono convertite implicitamente (o "decadimento") ai puntatori nella maggior parte dei contesti. Le due regole lavorano insieme per rendere il codice che si occupa di array come se si trattasse direttamente degli array, quando funziona davvero con i puntatori agli elementi degli array.

La relazione tra array C e puntatori è spesso confusa. Raccomando di leggere la sezione 6 di comp.lang.c FAQ; fa un ottimo lavoro di spiegarlo.

+0

Non sono sicuro del motivo per cui qualcuno avrebbe downvotato questo ... +1. –