Domanda

che sto cercando di risolvere due avvisi quando si compila un programma specifico utilizzando GCC. Le avvertenze sono:

Attenzione: dereferenziazione tipo puntatore-punned si romperà Norme rigorose-aliasing [-Wstrict-aliasing]

ed i due colpevoli sono:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

e

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

incoming_buf e outgoing_buf sono definiti come segue:

char                    incoming_buf[LIBIRC_DCC_BUFFER_SIZE];

char                    outgoing_buf[LIBIRC_DCC_BUFFER_SIZE];

Questo sembra sottilmente diverso rispetto agli altri esempi di questo avvertimento che ho esaminato. Io preferirei di risolvere il problema, piuttosto che controlli rigorosi-aliasing disabili.

Ci sono stati molti suggerimenti per utilizzare un sindacato -? Quello che potrebbe essere un sindacato adatto per questo caso

È stato utile?

Soluzione

Prima di tutto, esaminiamo perché si ottiene gli avvisi di violazione di aliasing.

Aliasing governa semplicemente dire che si può accedere solo un oggetto attraverso il suo tipo, il suo firmato tipo variant / unsigned, o attraverso un tipo di carattere (char, signed char, unsigned char).

C dice aver violato le regole di aliasing invoca comportamento non definito ( in modo da non farlo! ).

In questa riga del programma:

unsigned int received_size = ntohl (*((unsigned int*)dcc->incoming_buf));

anche se gli elementi dell'array incoming_buf sono di tipo char, si sta accedervi come unsigned int. Infatti il ??risultato dell'operatore dereferenziamento nella *((unsigned int*)dcc->incoming_buf) espressione è di tipo unsigned int.

Questa è una violazione delle regole di aliasing, perché hai solo il diritto di accesso elementi di matrice incoming_buf attraverso (vedi sintesi le regole di cui sopra!) char, signed char o unsigned char.

Avviso si hanno esattamente lo stesso problema aliasing nel vostro secondo colpevole:

*((unsigned int*)dcc->outgoing_buf) = htonl (dcc->file_confirm_offset);

Si accede agli elementi di char outgoing_buf attraverso unsigned int, quindi è una violazione di aliasing.

Soluzione proposta

Per risolvere il problema, si potrebbe cercare di avere gli elementi del vostro array direttamente definiti nel tipo si desidera accedere:

unsigned int incoming_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];
unsigned int outgoing_buf[LIBIRC_DCC_BUFFER_SIZE / sizeof (unsigned int)];

(A proposito della larghezza di unsigned int è implementazione definito, così si dovrebbe considerare l'utilizzo di uint32_t se il programma assume unsigned int è a 32 bit).

In questo modo è possibile memorizzare unsigned int oggetti nella propria matrice senza violare le regole di aliasing accedendo l'elemento attraverso il tipo char, in questo modo:

*((char *) outgoing_buf) =  expr_of_type_char;

o

char_lvalue = *((char *) incoming_buf);

Modifica

Sono completamente rielaborato la mia risposta, in particolare mi spiego il motivo per cui il programma ottiene gli avvertimenti aliasing dal compilatore.

Altri suggerimenti

Per risolvere il problema, non gioco di parole e l'alias ! L'unico modo "corretto" per leggere un tipo di T è quello di assegnare un tipo T e popolare la sua rappresentazione, se necessario:

uint32_t n;
memcpy(&n, dcc->incoming_buf, 4);

In breve: se si desidera un intero, è necessario effettuare un numero intero. Non c'è modo di barare in giro che in un modo linguaggio condonata.

L'unica conversione puntatore che si è permesso (a fini di I / O, in generale) è quello di trattare l'indirizzo del una variabile esistente di tipo T come char*, o meglio, come il puntatore al primo elemento di un array di caratteri di dimensioni sizeof(T).

union
{
    const unsigned int * int_val_p;
    const char* buf;
} xyz;

xyz.buf = dcc->incoming_buf;
unsigned int received_size = ntohl(*(xyz.int_val_p));

spiegazione semplificata 1. standard C ++ stati che si dovrebbe tentare di dati di allineare voi stessi, g ++ va un miglio supplementare per generare avvisi in materia. 2. si dovrebbe tentare solo se si capisce completamente l'allineamento dei dati della vostra architettura / sistema e dentro il codice (ad esempio il codice di cui sopra è una cosa sicura su Intel 32/64; allineamento 1; Win / Linux / BSD / Mac) 3. la ragione unica pratica di utilizzare il codice di cui sopra è quello di evitare avvisi del compilatore, quando e se si sa cosa si sta facendo

Se mi è consentito, secondo me, per questo caso, il problema è il design del ntohl e htonl e le API di funzioni correlate. Essi non avrebbero dovuto essere scritto come argomento numerico con ritorno numerico. (E sì, capisco il punto di ottimizzazione macro) Dovrebbero sono stati progettati come il lato 'n' essere un puntatore a un buffer. Quando questo è fatto, l'intero problema va via e la routine è accurato a seconda di quale Endian l'host è. Per esempio (senza tentare di ottimizzare):

inline void safe_htonl(unsigned char *netside, unsigned long value) {
    netside[3] = value & 0xFF;
    netside[2] = (value >> 8) & 0xFF;
    netside[1] = (value >> 16) & 0xFF;
    netside[0] = (value >> 24) & 0xFF;
};

Se si dispone di ragioni che non consentono di modificare il tipo di oggetto di origine (come è stato nel mio caso), e si è assolutamente certi che il codice è corretto e fa quello che intendeva fare con tale matrice char, a aVVERTENZE Evitare che si possono effettuare le seguenti operazioni:

unsigned int* buf = (unsigned int*)dcc->incoming_buf;
unsigned int received_size = ntohl (*buf);

puntatore Fusioni di unsigned e poi di nuovo al puntatore.

unsigned int received_size = ntohl (* ((* unsigned) ((unsigned) in DCC> incoming_buf)));

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top