2013-11-14 10 views
6

In einigen eingebetteten C-Code für den MC9S12C32-Mikrocontroller habe ich eine zirkuläre Warteschlange (aka Ringspeicher) implementiert mit einem statisch großen Byte-Array und zwei "Zeigern" für die Vorder- und Rückseite der Warteschlange, Das sind in Wirklichkeit nur Indizes für das Array der Warteschlange.C Modul ohne Vorzeichen verursacht Compiler-Warnung

// 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 

anzumerken, dass trear der tatsächliche Index des hinteren Elements ist, aber tfront eins weniger als die tatsächliche Index des Frontelement (je 16 natürlich auf Modulo). So zum Beispiel, wenn mein Puffer enthielt „Hallo“, es könnte wie folgt aussehen (wo leere Slots sind Müll-Wert):

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

Wenn es Zeit ist, einen Byte aus der Warteschlange zu entfernen, ich dies tun:

Das alles funktioniert gut - zumindest hat mein Programm keine Fehler in Bezug auf dieses Fragment gezeigt. Allerdings, wenn ich dieses Programm zu kompilieren, mein Compiler warnt mich über die Linie markiert (A) im Fragment oben, beschwerte:

Warnung: C2705: Möglicher Datenverlust

main.c Linie 402

Linie 402 ist Linie (A). Ich sollte beachten, dass ich nicht gcc oder ähnliches verwende; Ich kompiliere in der CodeWarrior IDE von Freescale, die mir manchmal andere etwas mysteriöse Warnungen gegeben hat. In einem Versuch, loszuwerden, die Warnung zu erhalten, schrieb ich das Fragment wie oben:

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

jedoch mein Compiler gibt immer noch die gleiche Warnung, diesmal über Linie (B). Vielleicht sagt der Compiler mir, dass in der Anweisung (tfront + 1 >= tsize), tfront 255 vor der Ausführung und Überlauf sein könnte. Natürlich weiß ich, dass das nicht passieren wird, aber mein Compiler nicht. Wenn das der Fall ist, warum war Linie (A) ein Problem? Im Grunde würde ich gerne wissen, worüber der Compiler unglücklich ist.


Da meine Frage eingeben aus, habe ich es gelöst, indem tsize von einem Variablentyp zu einem Präprozessordefinition ändert (das heißt, #define TSIZE 16). Meine Frage steht immer noch.


Einige verwandte Fragen:
unsigned overflow with modulus operator in C
modulus operator with unsigned chars

+1

Beachten Sie, dass, wenn 'tsize' garantiert werden kann, eine Potenz von 2 sein, es effizienter ist, eine Bit-Maske zu verwenden, und das auch ordentlich vermeidet diese Warnung ohne Besetzung. Also für einen Puffer von 16 Elementen, 'tfront | = 0x0f' statt' tfront% = 16'. Im allgemeinen Fall ist für jede 'tsize', die eine Potenz von 2 ist, die erforderliche Mod-Maske 'tsize - 1'. – Clifford

+0

@Clifford Wenn die Warnung durch die Erweiterung der Erweiterung auf 'lval = expr;' verursacht wird, wobei 'lval' den Typ' byte' und 'expr' den Typ' int' hat, vermeidet die Verwendung einer Bitmaske nicht die Warnung: ' tfront & = mask; 'expandiert nach' tfront = tfront & mask; 'und' tfront & mask' hat den Typ 'int' aus dem gleichen Grund 'tfront% size' tut (C99 6.3.1.1:2 und 6.3.1.8:1" the Integer-Promotions werden für beide Operanden ausgeführt "). –

+0

@Clifford Zusätzlich muss der bitweise Operator richtig eingestellt werden. Meintest du "&"? –

Antwort

5

Der Compiler Warnung aus der Tatsache, wahrscheinlich kommt, dass in tfront %= tsize;, die tfront = tfront % tsize; entspricht, wegen Förderung Regeln in C der Ausdruck tfront % tsize hat (*) Geben Sie int ein.

Es kann den Compiler stummschalten, wenn Sie stattdessen tfront = (byte)(tfront % tsize); schreiben.

Es gibt keinen besonderen Grund zur Sorge, und Ihr Compiler gibt seltsame Warnungen in der Tat: obwohl der Ausdruck tfront % tsize hat technisch Typ int, alle seine Werte wegen der Art und Weise eine byte passen es berechnet wird. Selbst wenn die Werte nicht alle in eine byte passen, wird das Umbruchverhalten durch den C-Standard für vorzeichenlose Integer-Typen garantiert (so dass Sie dieses Wrap-Around-Verhalten absichtlich verwenden könnten).

(*), es sei denn auf Ihrer Compilation Plattform int können nicht alle Werte enthalten, die ein unsigned char nehmen kann, in diesem Fall ist es der Typ wäre unsigned int und Sie würden wahrscheinlich nicht die Warnung sehen.

+0

Genau wie Sie vorgeschlagen haben, scheint dies das Problem zu sein. Sehr aufschlussreich, danke. – ravron

+2

Die Warnung ist einzigartig wenig hilfreich; Falsche Positive sind schlimmer als falsche Negative, weil sie die Denkweise untergraben: "Kompilation ohne Warnungen ist gut". Melden Sie dem Compiler-Lieferanten einen Fehler. –

+0

@ JonathanLeffler Ich habe diesen Bug bereits vor einigen Jahren selbst gemeldet. Nicht sicher, ob es noch da ist. Siehe meine Antwort für Details. – Lundin

3

Diese spezielle Warnung ist ein bekannter Fehler in allen Codewarrior-Compilern. Die Warnung vor möglichen Datenverlusten ist inkonsistent und fehlerhaft. Manchmal warnt es, weil das Risiko von impliziten Werbeaktionen besteht, manchmal nicht. Siehe this discussion. Ich kann bestätigen, dass dies auch für CW für S12C gilt (zumindest bis Version 5.1, die ich verwende).

Sie können diese Warnung deaktivieren, aber ich würde sie nicht empfehlen, da sie manchmal gefährlichen Code zusammen mit den falschen Warnungen findet. Wie in Ihrem speziellen Fall: Die Warnung ist korrekt.

Sie arithmetisch für kleine Integer-Typen, ohne explizite Umwandlungen. Ein solcher Code ist gefährlich und enthält möglicherweise versteckte Fehler, weil C kleine ganze Zahlen zu signierten Ints fördert. Ein Kodierungsstandard wie MISRA-C, der explizite Umwandlungen erzwingt, hätte hier geholfen.

Darüber hinaus kann der Operator?: Auch gefährlich sein, wenn Sie nicht wissen, wie es funktioniert. Es gleicht den 2. und 3. Operand gegeneinander aus, was man vielleicht nicht erwartet hat.

Ich würde daher vorschlagen, um den Code zu ändern:

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

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

Ich bin nicht von Ihrem '(byte) (tfront +1)> = tsize'-Vorschlag überzeugt. Die einzige Möglichkeit, die sich von der ursprünglichen Bedingung in der Frage unterscheidet, ist, wenn '(Byte) (tfront +1)' sich von 'tfront + 1' unterscheidet, und wenn dies der Fall ist, ist das Ergebnis Ihrer vorgeschlagenen Bedingung wahrscheinlich nicht was der Programmierer beabsichtigt hat.Sind Sie sicher, dass Sie nicht zulassen, dass Überlegungen zum Compiler-Warnen Sie dazu zwingen, einen Bug einzubauen, wo es normalerweise keinen gab? Wenn Sie Typen abgleichen wollen, ist '(unsigned int) tfront +1> = (unsigned int) tsize' wahrscheinlich besser. –

+0

@PascalCuoq Ja, die Besetzung dieser bestimmten Zeile ist etwas wählerisch, sie würde nur in seltenen Fällen wie 'tfront = 255' Probleme verursachen. Ein Umbruch auf Null ist für vorzeichenlose Variablen gut definiert. Wenn also der Programmierer den Code geschrieben hat, der auf diesem Verhalten beruht, würde er nicht wie erwartet funktionieren, weil 'tfront' implizit befördert wird. Das Problem mit Bugs und Kuriositäten im Zusammenhang mit impliziter Werbung ist immer nur das: Was hat der Programmierer eigentlich vor? Haben sie erwartet, dass die Kuriosität passiert oder kommt es ihnen überraschend vor? In solchen Fällen ist es sehr wichtig, Kommentare in den Code zu schreiben. – Lundin