2013-11-14 11 views
6

In alcuni codici C incorporati per il microcontrollore MC9S12C32, ho una coda circolare (nota anche come buffer circolare) implementata con un array di byte di dimensioni statiche e due "puntatori" per la parte anteriore e posteriore della coda, che sono in realtà solo indici per l'array della coda.Il modulo C senza segno causa il warning del compilatore

// call unsigned chars bytes 
typedef unsigned char byte; 
byte trear = 0; // SCI transmit display buffer IN index 
byte tfront = 0; // SCI transmit display buffer OUT index 
byte tsize = 16; // size of transmit buffer 
byte tbuf[16]= {0};// SCI transmit display buffer 

noti che trear è l'indice effettivo dell'elemento posteriore, ma tfront è uno inferiore indice effettivo dell'elemento anteriore (soggetto a con modulo 16 naturalmente). Così, per esempio, se il mio tampone conteneva "ciao", che potrebbe assomigliare a questo (dove gli slot vuoto sono i valori della spazzatura):

_________________________________ 
| | |h|e|l|l|o| | | | | | | | | | 
^  ^
front  rear 

Quando è il momento di rimuovere un byte dalla coda, faccio questo:

// increment front index 
tfront++; 
// wrap front index if it exceeded bounds 
tfront %= tsize;       // (A) 
// get character to transmit 
byte outputChar = tbuf[tfront]; 

Questo funziona perfettamente - almeno, il mio programma non ha mostrato bug relativi a questo frammento. Tuttavia, quando ho compilare questo programma, il mio compilatore mi mette in guardia circa la linea tracciata (A) nel frammento di cui sopra, lamentandosi:

Attenzione: C2705: possibile perdita di dati

linea main.c 402

La riga 402 è la linea (A). Devo notare che non sto usando gcc o simili; Sto compilando l'IDE CodeWarrior di Freescale, che a volte mi ha dato altri avvertimenti in qualche modo mistificanti. Nel tentativo di sbarazzarsi di avvertimento, ho riscritto il frammento di cui sopra come:

// increment front index mod tsize 
tfront = (tfront + 1 >= tsize) ? 0 : tfront + 1;  // (B) 
// get character to transmit 
byte outputChar = tbuf[tfront]; 

Tuttavia, la mia compilatore emette sempre lo stesso avvertimento, questa volta sulla linea (B). Forse il compilatore mi sta dicendo che nella dichiarazione (tfront + 1 >= tsize), tfront potrebbe essere 255 prima dell'esecuzione e overflow. Certo, so che questo non accadrà, ma il mio compilatore no.

Se questo è il caso, perché la linea (A) è stata un problema? Fondamentalmente, mi piacerebbe sapere di cosa sia infelice il compilatore.


Dal momento che digitando la mia domanda, ho risolto cambiando tsize da un tipo di variabile a una definizione di preprocessore (vale a dire, #define TSIZE 16). La mia domanda è ancora valida, però.


Alcune questioni connesse:
unsigned overflow with modulus operator in C
modulus operator with unsigned chars

+1

Nota che se 'tsize' può essere garantito per essere una potenza di 2, è più efficiente di utilizzare una maschera di bit, e che anche evita ordinatamente questo avvertimento senza un cast. Quindi per un buffer di 16 elementi, 'tfront | = 0x0f' piuttosto che' tfront% = 16'. Nel caso generale per qualsiasi 'tsize' che è una potenza di 2, la maschera mod richiesta è' tsize - 1'. – Clifford

+0

@Clifford Se l'avviso è causato dal assegnazione espansione per 'lval = expr;' 'dove lval' ha tipo' 'byte' e expr' è di tipo' int', quindi utilizzando una maschera di bit non evita l'avvertimento: ' tfront & = maschera; '' espande tfront = tfront & mask; 'e' tfront & mask' ha tipo 'int' per la stessa ragione' tfront% size' fa (C99 6.3.1.1:2 e 6.3.1.8:1 “la le promozioni intere vengono eseguite su entrambi gli operandi "). –

+0

@Clifford Inoltre, è necessario ottenere l'operatore bit a destra. Intendevi '&'? –

risposta

5

L'avviso del compilatore probabilmente deriva dal fatto che nel tfront %= tsize;, che equivale a tfront = tfront % tsize;, a causa delle regole di promozione in C l'espressione tfront % tsize ha (*) digitare int.

È possibile disattivare il compilatore se si scrive tfront = (byte)(tfront % tsize);.

Non c'è un motivo particolare di preoccuparsi, e il compilatore emette strani avvertimenti anzi: anche se l'espressione tfront % tsize ha tecnicamente digitare int, i suoi valori tutti in forma in un byte a causa del modo in cui viene calcolato. Anche se i valori non si adattavano tutti in un byte, il comportamento di wrap-around è garantito dallo standard C per i tipi di numeri interi senza segno (in modo tale da giustificare l'utilizzo di questo comportamento di wrap-around).

(*) a meno che sulla vostra piattaforma compilation int non può contenere tutti i valori che un unsigned char può prendere, nel qual caso sarebbe di tipo unsigned int e probabilmente non vedrebbe l'avvertimento.

+0

Proprio come hai suggerito, sembra che questo sia il problema. Molto perspicace, grazie. – ravron

+2

L'avviso è singolarmente inutile; i falsi positivi sono peggio dei falsi negativi perché minano la serie mentale "la compilazione senza avvisi è buona". Segnala un bug al fornitore del compilatore. –

+0

@ JonathanLeffler Ho già segnalato questo stesso bug, alcuni anni fa. Non sono sicuro che sia ancora lì. Vedi la mia risposta per i dettagli. – Lundin

3

Questo particolare allarme è un bug noto in tutti i compilatori CodeWarrior. La possibile perdita di avviso dei dati è inconsistente e buggata. A volte avvisa perché c'è il rischio di promozioni di tipo implicito, a volte no. Vedi this discussion. Posso confermare che questo vale anche per CW per S12C (almeno per quanto riguarda la versione 5.1, che è quello che sto usando).

È possibile disattivare questo avviso, ma io non lo consiglio, dal momento che trovare il codice pericoloso a volte, insieme con i falsi allarmi. Come nel tuo caso specifico: l'avvertimento è corretto.

State facendo aritmetica su piccoli tipi interi, senza cast espliciti. Tale codice è pericoloso e potrebbe contenere bug nascosti, poiché C promuove piccoli numeri interi in toated. Uno standard di codifica come MISRA-C, che applica cast esplicito, avrebbe aiutato qui.

Inoltre, l'operatore?: Può anche essere pericoloso se non si sa come funziona. Bilancia il 2 ° e il 3 ° operando l'uno contro l'altro, quale forse non si aspettava.

Ritengo pertanto cambiare il codice a:

tfront = (byte)(tfront % tsize); 
... 

if((byte)(tfront +1) >= tsize) 
{ 
    tfront = 0; 
} 
else 
{ 
    tfront++; 
} 
+0

Non sono convinto dal tuo suggerimento '(byte) (tfront +1)> = tsize'. L'unica opportunità che deve essere diversa dalla condizione originale nella domanda è se '(byte) (tfront +1)' è diverso da 'tfront + 1', e quando lo è, il risultato della tua condizione suggerita non è probabilmente cosa intendeva il programmatore.Sei sicuro di non lasciare che le considerazioni di avviso del compilatore ti costringano a inserire un bug in cui normalmente non ci sarebbe stato? Se vuoi abbinare i tipi, '(unsigned int) tfront +1> = (unsigned int) tsize' è probabilmente migliore. –

+0

@PascalCuoq Sì, il cast su quella particolare riga è un po 'schizzinoso, causerebbe solo problemi in qualche raro caso come 'tfront = 255'. Il wrap-around a zero è ben definito per le variabili non firmate, quindi se il programmatore ha scritto il codice basandosi su quel comportamento, non funzionerebbe come previsto perché "tfront" viene implicitamente promosso. Il problema con bug e stranezze legate alla promozione implicita è sempre questo: cosa intendeva realmente il programmatore? Si aspettavano che succedesse la stranezza o si trattava di una sorpresa? È molto importante inserire commenti nel codice in questi casi. – Lundin

Problemi correlati