<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
		<id>https://rn-wissen.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=NorbertBehrens</id>
		<title>RN-Wissen.de - Benutzerbeiträge [de]</title>
		<link rel="self" type="application/atom+xml" href="https://rn-wissen.de/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=NorbertBehrens"/>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Spezial:Beitr%C3%A4ge/NorbertBehrens"/>
		<updated>2026-04-11T20:44:50Z</updated>
		<subtitle>Benutzerbeiträge</subtitle>
		<generator>MediaWiki 1.25.1</generator>

	<entry>
		<id>https://rn-wissen.de/wiki/index.php?title=Fallstricke_bei_der_C-Programmierung&amp;diff=12254</id>
		<title>Fallstricke bei der C-Programmierung</title>
		<link rel="alternate" type="text/html" href="https://rn-wissen.de/wiki/index.php?title=Fallstricke_bei_der_C-Programmierung&amp;diff=12254"/>
				<updated>2007-06-12T12:38:28Z</updated>
		
		<summary type="html">&lt;p&gt;NorbertBehrens: /* Zuweisung statt Vergleich */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Tippfehler=&lt;br /&gt;
&lt;br /&gt;
Tippfehler können immer passieren. Besonders fies ist es, wenn der Tippfehler nicht zu einer Warnung oder zu einer Fehlermeldung führt, weil der entstandene Code korrekter C-Code ist.&lt;br /&gt;
&lt;br /&gt;
==Ein &amp;lt;tt&amp;gt;;&amp;lt;/tt&amp;gt; zu viel==&lt;br /&gt;
Ein reflexartig eingetippter oder nach Änderungen stehen gebliebener &amp;lt;tt&amp;gt;;&amp;lt;/tt&amp;gt; hat schon so manches &lt;br /&gt;
Programm ausgeknockt:&lt;br /&gt;
 if (a == 0);&lt;br /&gt;
 {&lt;br /&gt;
    /* mach was */&lt;br /&gt;
 }&lt;br /&gt;
Wenn &amp;lt;tt&amp;gt;a == 0&amp;lt;/tt&amp;gt; ist, dann wird &amp;lt;tt&amp;gt;;&amp;lt;/tt&amp;gt; ausgeführt (also im Endeffekt garnichts). Danach kommt der Block, der im &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; stehen sollte. Der wird immer ausgeführt, denn er gehört nicht mehr zum &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==Zuweisung statt Vergleich==&lt;br /&gt;
 if (a = 0)&lt;br /&gt;
 {&lt;br /&gt;
    /* mach was */&lt;br /&gt;
 }&lt;br /&gt;
Zuerst wird &amp;lt;tt&amp;gt;a = 0&amp;lt;/tt&amp;gt; gesetzt und dann überprüft, ob die &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt;-Bedingung erfullt ist. Der Wert eines Ausdrucks (hier: &amp;lt;tt&amp;gt;a = 0&amp;lt;/tt&amp;gt;) ist immer gleich dem Wert, der zugewiesen wird (hier: &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;). Wenn die Auswertung des Ausdrucks &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; ergibt, entspricht das &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. Alle anderen Werte werden als &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; interpretiert.&lt;br /&gt;
&lt;br /&gt;
Abhilfe schafft, indem man sich angewöhnt zu schreiben&lt;br /&gt;
 if (0 == a)&lt;br /&gt;
Wenn man dann eine Zuweisung eintippt, gibt's einen Compiler-Fehler.&lt;br /&gt;
&lt;br /&gt;
==Signal/Interrupt-Name vertippt (avr-gcc)==&lt;br /&gt;
&lt;br /&gt;
 SIGNAL (SIG_OVEFRLOW0)&lt;br /&gt;
 {&lt;br /&gt;
    /* mach was */&lt;br /&gt;
 }&lt;br /&gt;
Nicht alle Compiler-Versionen meckern da. Der [[ISR]]-Code wird nicht in die Interrupt-Tabelle eingetragen. Kommt es zum [[Interrupt]], dann landet man in RESET.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Mangelnde C-Kenntnis=&lt;br /&gt;
&lt;br /&gt;
==Warnung ignoriert==&lt;br /&gt;
&lt;br /&gt;
::&amp;quot;''Wieso soll das Probleme machen? Das ist doch nur eine Warnung!''&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Warnungen zur Compile-Zeit werden gerne zu Fehlern zur Laufzeit. Letztere sind deutlich schwerer zu finden, als angewarnten Code zu korrigieren.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void foo (long*);&lt;br /&gt;
&lt;br /&gt;
void bar (int *p)&lt;br /&gt;
{&lt;br /&gt;
    foo (p+1); &lt;br /&gt;
    // Was soll das sein?!&lt;br /&gt;
    // ((long*) p) + 1       oder  &lt;br /&gt;
    // (long*) (p + 1)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;gt; gcc -c -o prog.o prog.c&lt;br /&gt;
&lt;br /&gt;
prog.c: In function `bar':&lt;br /&gt;
prog.c:5: warning: passing arg 1 of `foo' from incompatible pointer type&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Array-Index==&lt;br /&gt;
&lt;br /&gt;
Informatiker am Bahnhof:&lt;br /&gt;
:''&amp;quot;0, 1, 2, ... Wo ist mein dritter Koffer?!&amp;quot;''&lt;br /&gt;
Gleiches gilt für Arrays:&lt;br /&gt;
 #define NUM 10;&lt;br /&gt;
 int a[NUM]; // für die N Werte a[0] ... a[N-1]&lt;br /&gt;
Ein Zugriff auf &amp;lt;tt&amp;gt;a[N]&amp;lt;/tt&amp;gt; greift irgendwo hin. &lt;br /&gt;
Wahlweise liest man Schrott oder überschreibt andere Daten, die in der Nähe liegen, und&lt;br /&gt;
plötzlich seltsame Werte enthalten.&lt;br /&gt;
&lt;br /&gt;
==Bitweise vs. Logische Operatoren==&lt;br /&gt;
&lt;br /&gt;
Die Operatoren AND, OR und NOT gibt es in C in zwei Ausprägungen&lt;br /&gt;
;bitweise: Die Operatoren &amp;lt;tt&amp;gt;^&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;|&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;&amp;amp;&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;~&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;|=&amp;lt;/tt&amp;gt;,  &amp;lt;tt&amp;gt;^=&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;&amp;amp;=&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;|=&amp;lt;/tt&amp;gt; operieren bitweise. Der entsprechende Operator wird also auf alle Bits des Wertes parallel angewandt und das Ergebnis für ein Bit ist unabhängig vom Inhalt der anderen Bits.&lt;br /&gt;
;logisch:  Die Operatoren &amp;lt;tt&amp;gt;||&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;&amp;amp;&amp;amp;&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;!&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;&amp;amp;&amp;amp;=&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;||=&amp;lt;/tt&amp;gt; operieren auf dem ganzen (int) Wert und berücksichtigen nur, ob der Wert 0 (false) oder ungleich 0 (true) ist.&lt;br /&gt;
Dementsprechend liefert &amp;lt;tt&amp;gt;&amp;amp;&amp;amp;&amp;lt;/tt&amp;gt; i.d.R. ein anderes Ergebnis als &amp;lt;tt&amp;gt;&amp;amp;&amp;lt;/tt&amp;gt;:&lt;br /&gt;
 if (a &amp;amp;&amp;amp; b) // erfüllt, wenn a!=0 und b!=0 &lt;br /&gt;
 if (a &amp;amp; b)  // erfüllt, wenn in a und b an der gleichen Stelle ein Bit gesetzt ist&lt;br /&gt;
Ein Verwechseln bzw. unkorrektes Einsetzen der Operatoren gibt also ein falsches Programm.&lt;br /&gt;
&lt;br /&gt;
==TRUE ist nicht 1==&lt;br /&gt;
&lt;br /&gt;
Da C die Begriffe TRUE und FALSE eigentlich nicht kennt, wurde schon öfter beobachtet, daß folgendes definiert wurde: &lt;br /&gt;
 #define TRUE 1&lt;br /&gt;
 #define FALSE 0&lt;br /&gt;
solange man damit nur Schalter setzt und abfragt, ist dagegen nichts zu sagen. &lt;br /&gt;
 a = TRUE; b = FALSE;&lt;br /&gt;
 if ( (a == TRUE) &amp;amp;&amp;amp; (b == FALSE)) {..&amp;quot;this is true&amp;quot;..} &lt;br /&gt;
wenn man aber dann schreibt&lt;br /&gt;
 if ( (a &amp;amp; b) == TRUE)  {..&amp;quot;this is true&amp;quot;..}&lt;br /&gt;
kann man einen herbe Enttäuschung erleben. Man müßte für ein richtiges Ergebnis schreiben&lt;br /&gt;
 if ( (a &amp;amp; b) != FALSE)  {..&amp;quot;this is true&amp;quot;..}&lt;br /&gt;
Damit ist aber eine gute Lesbarkeit endgültig dahin.&lt;br /&gt;
&lt;br /&gt;
Besser sind Definition wie&lt;br /&gt;
 #define FALSE (0!=0)&lt;br /&gt;
 #define TRUE  (0==0)&lt;br /&gt;
denn damit gilt einerseits, daß für alle boolschen Werte einschliesslich dieser Konstanten der&lt;br /&gt;
Zusammenhang &amp;lt;tt&amp;gt;x = !!x&amp;lt;/tt&amp;gt; besteht. &lt;br /&gt;
Mit der allerersten Definition hat man das unerwünschte Ergebnis &amp;lt;tt&amp;gt;TRUE != !FALSE&amp;lt;/tt&amp;gt;.&lt;br /&gt;
Andererseits kann man direkt gegen diese Konstanten vergleichen:&lt;br /&gt;
 a = (x &amp;lt; y);&lt;br /&gt;
 ...&lt;br /&gt;
 if (a == TRUE) // oder die 'klassische' Variante: if (a)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch: http://www.dclc-faq.de/kap8.htm#8.2&lt;br /&gt;
&lt;br /&gt;
==Ein , anstatt . in Konstante==&lt;br /&gt;
&lt;br /&gt;
In C sowie im angloamerikanischen Sprachraum werden Dezimalbrüche mit einem &amp;lt;tt&amp;gt;.&amp;lt;/tt&amp;gt; (Punkt) geschrieben und nicht wie im Deutschen mit einem &amp;lt;tt&amp;gt;,&amp;lt;/tt&amp;gt; (Komma): &lt;br /&gt;
 float pi;&lt;br /&gt;
 pi = 3,14;         // *AUTSCH* soll wohl heissen 3.14&lt;br /&gt;
Die zweite Zeile besteht aus zwei durch ein Komma getrennten Anweisungen, so daß das ganze&lt;br /&gt;
etwa gleichbedeutend ist mit&lt;br /&gt;
 float pi;&lt;br /&gt;
 pi = 3;&lt;br /&gt;
 14;&lt;br /&gt;
Jedenfalls ist es korrekter C-Code!&lt;br /&gt;
Die &amp;lt;tt&amp;gt;14;&amp;lt;/tt&amp;gt; ist ein Ausdruck, der nicht weiter gebraucht wird und daher wegfällt, da&lt;br /&gt;
er im Gegensatz zu einer void-Funktion keine Wirkung hat.&lt;br /&gt;
Man rechnet also mit dem Wert&amp;amp;nbsp;3 für &amp;lt;math&amp;gt;\pi&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Das falsche Komma hat auch schon so manchen ebay-Freak aufs Kreuz gelegt...&lt;br /&gt;
&lt;br /&gt;
==Führende 0 in Konstanten==&lt;br /&gt;
&lt;br /&gt;
In C kennzeichnet eine führende 0 bei einer Zahlenkonstante, &lt;br /&gt;
daß die Zahl oktal dargestellt ist. Somit ist 010 nicht gleich 10.&lt;br /&gt;
 if (a == 030) // ist a gleich 24?&lt;br /&gt;
&lt;br /&gt;
=Nicht-atomarer Code=&lt;br /&gt;
Verwendet man in einem Programm [[IRQ|IRQs]] ([[Interrupt|Interrupts]]) und ändert in der [[ISR]] (Service Routine) ein Datum (Variable, SFR, ...), dann wird diese Änderung möglicherweise überschrieben, wenn die IRQ zu einem ungünstigen Zeitpunkt auftritt. &lt;br /&gt;
&lt;br /&gt;
Ein ausführlicheres Beispiel, das zeigt, was passieren kann, findet sich im Artikel [[avr-gcc]] im Abschnitt &amp;quot;[[avr-gcc#Zugriff auf einzelne Bits|Zugriff auf einzelne Bits]]&amp;quot;. Ein weiteres Beispiel ist das Lesen, Schreiben oder Testen einer mehrbytigen Variable:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
int volatile i;&lt;br /&gt;
   ...&lt;br /&gt;
   if (0 == i)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Weil &amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt; länger als 1 Byte ist, kann es je nach Architektur nicht in einem Befehl gelesen werden;&lt;br /&gt;
daher kann während des Lesens eine IRQ auftreten, in deren ISR der Wert von &amp;lt;tt&amp;gt;i&amp;lt;/tt&amp;gt; möglicherweise &lt;br /&gt;
verändert wird. Der obige Code könnte etwa so assembliert werden (hier mit [[avr-gcc]]):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   ; i wird vom SRAM in das Registerpaar r24:r25 geladen&lt;br /&gt;
   ; low-Teil laden&lt;br /&gt;
   lds r24,i&lt;br /&gt;
   ; high-Teil laden&lt;br /&gt;
   ; wenn hier eine IRQ zuschlägt, in der i verändert wird, ist der Registerinhalt korrupt&lt;br /&gt;
   lds r25,(i)+1&lt;br /&gt;
   or r24,r25&lt;br /&gt;
   brne ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Natürlich können auch komplexere Datenstrukturen von diesem Phänomen betroffen sein.&lt;br /&gt;
Besonders unangenehm an dieser Klasse von Fehlern ist, daß sie nur sporadisch auftauchen&lt;br /&gt;
und man sie daher selbst mit einem guten Debugger sehr schlecht orten kann, da die zugehörige&lt;br /&gt;
Codestelle fast immer korrekt abgearbeitet wird.&lt;br /&gt;
&lt;br /&gt;
Eine Möglichkeit dem zu begegnen ist, den ganzen betroffenen Block ununterbrechbar (atomar) zu machen.&lt;br /&gt;
Wieder ein Beispiel für avr-gcc:&lt;br /&gt;
 #include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
 #include &amp;lt;avr/interrupt.h&amp;gt;&lt;br /&gt;
 ...&lt;br /&gt;
    {{ccomment|ein lokale(!) Variable, die das Status-Register und }}&lt;br /&gt;
    {{ccomment|insbesondere das im folgenden geänderte I-Flag merkt }}&lt;br /&gt;
    {{ccomment|Falls man an der Codestelle immer weiß, daß IRQs }}&lt;br /&gt;
    {{ccomment|aktiviert sind, kann man die zu atomisierende Sequenz }}&lt;br /&gt;
    {{ccomment|auch in cli()...sei() einschachteln }}&lt;br /&gt;
    {{ccomment|Falls IRQs global deaktiviert sind brauch man natürlich }}&lt;br /&gt;
    {{ccomment|keine besonderen Vorkehrungen zu treffen, da dann aller Code }}&lt;br /&gt;
    {{ccomment|atomar ist. }}&lt;br /&gt;
    unsigned char sreg = SREG;&lt;br /&gt;
 &lt;br /&gt;
    {{ccomment|Interrupts global deaktivieren (I-Flag &amp;amp;#61; 0) }}&lt;br /&gt;
    cli();&lt;br /&gt;
 &lt;br /&gt;
    if (0 == i)&lt;br /&gt;
    {&lt;br /&gt;
        i = 1;&lt;br /&gt;
        {{ccomment|IRQs sobald als möglich wieder zulassen }}&lt;br /&gt;
        {{ccomment|(I-Flag wieder herstellen) }}&lt;br /&gt;
        SREG = sreg;&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        {{ccomment|IRQs so bald als möglich wieder zulassen }}&lt;br /&gt;
        {{ccomment|(I-Flag wieder herstellen) }}&lt;br /&gt;
        SREG = sreg;&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    ...&lt;br /&gt;
Oder je nach Programmstruktur sichert man den Wert, z.B. in eine lokale Variable oder deaktiviert nur selektiv die kritischen Interrupts, also solche, die Einfluss auf die kritischen Daten nehmen.&lt;br /&gt;
&lt;br /&gt;
= Weitere Fallstricke =&lt;br /&gt;
* [[Warteschleife]]&lt;br /&gt;
&lt;br /&gt;
= Siehe auch =&lt;br /&gt;
* [[C-Tutorial]]&lt;br /&gt;
* [[avr-gcc]]&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:Quellcode C|!]]&lt;br /&gt;
[[Kategorie:Software]]&lt;/div&gt;</summary>
		<author><name>NorbertBehrens</name></author>	</entry>

	</feed>