Forum: Mikrocontroller und Digitale Elektronik Konstanten const define rechenfehler


von Heinz B. (hez)


Lesenswert?

hello,

schon wieder ein Problem. Dieses mal glaube ich zu wissen, woran es 
liegt. Nur leider weiß ich nicht, wie ich es lösen soll.

1
#define iBufferMax 16 // max 16 Chars im Ringbuffer  
2
void main (void)
3
{
4
   char iX;
5
   iX = 5 % iBufferMax; // hat den Wert 1280!!!
6
}
Ich gehe mal davon aus, dass das irgendetwas mit einem Überlauf zu tun 
hat. Wie kann man denn den verhindern?

Ich will für iBufferMax unbedingt eine Konstante verwenden. Mit const 
habe ich es auch schon versucht, aber damit kann ich nicht arbeiten, 
weil dann das folgende nicht funktioniert:

1
const char iBufferMax = 16; // max 16 Chars im Ringbuffer  
2
void main (void)
3
{
4
   char iX = 5 % iBufferMax; // 1280
5
   char aY[iBufferMax];      // <- mag C nicht
6
}
(Ich hoffe, ich habe das jetzt richtig verkürzt hier reingetippt.)

Ich bräuchte also define. Aber wie kann ich das dann verwenden??

mfG 'Ringbuffer-Heinz' :)

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Das sind unterste Grundlagen, an denen du hier scheiterst. Sieh dir mal 
den Beitrag "Re: keil array => string" an.
1
  iX = 5 % iBufferMax; // hat den Wert 1280!!!
Fünf modulo 16 gibt Fünf. Woher hast du 1280?
1
 char aY[iBufferMax];      // <- mag C nicht
Richtig. Es ist ein signifikanter Unterschied zwischen einer Konstanten 
und einem Define. So würde es gehen:
1
define iBufferMax 16; // max 16 Chars im Ringbuffer  
2
:
3
   char aY[iBufferMax];

von Heinz B. (hez)


Lesenswert?

Ist mir schon klar, dass es zwischen define und const einen Unterschied 
gibt. define wird schon beim Kompilieren aktiv. Der Kompiler ersetzt 
dann die Platzhalter durch deren Werte (sofern ich das richtig 
verstanden habe). Und const wird erst zur Laufzeit aktiv. Und darum 
funktioniert das auch beim Array nicht.

Aber ich bekomme wirklich 1280 ausgegeben. Habe ich mit Keil-C 
kompiliert, in uC geladen und dann den Wert mittels printf ausgegeben 
und angeschaut.

Aber ich versuchs gleich noch einmal. Aber beim 4ten mal wird wohl auch 
nichts anderes rauskommen ... :)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Auf was für einer Zielplattform arbeitest du denn, wenn ein "char"
bei dir den Wert 1280 annehmen kann? ;-)  Davon abgesehen, dass
"char" hier der denkbar ungünstigste Datentyp ist, denn dessen
Vorzeichenhaftigkeit oder -losigkeit ist vom Standard nicht definiert.
Du solltest über uintfast8_t oder uint8_t (aus <stdint.h>) mal
nachdenken.

Dein Hauptproblem wird aber sein, dass du die Indexrechnung vorzeichen-
behaftet machst.  Die beiden Konstanten 5 und 16 sind vom Typ `int'.
Du solltest deine Rechnung als `unsigned int' machen.  Um die
Konstante unsigned werden zu lassen, kannst du ihr ein `u' (klein
oder groß) anhängen, also beispielsweise:
1
#define iBufferMax 16u

Es genügt, das einer von beiden Teilausdrücken des Terms unsigned
ist, da sich `unsigned int' über `signed int' durchsetzt bei der
Rechnung.  Was aber nicht genügt (und was gern verwechselt wird)
ist, dass die Ergebnisvariable (also die linke Seite der Zuweisung)
einen unsigned-Typ erhält; die Rechnung auf der rechten Seite (und
damit die Festlegung, ob diese vorzeichenbehaftet ist oder nicht)
erfolgt, noch bevor ggf. über eine Typkonvertierung für die
Zuweisung entschieden werden muss.

von Karl H. (kbuchegg)


Lesenswert?

Heinz B. schrieb:

> Aber ich bekomme wirklich 1280 ausgegeben. Habe ich mit Keil-C
> kompiliert, in uC geladen und dann den Wert mittels printf ausgegeben
> und angeschaut.

Ein char KANN nicht 1280 sein.
Zumindest nicht auf deinem System :-)

Also wird das Problem nicht beim % liegen, sondern bei der Ausgabe :-)

Was auch gerne übersehen wird, sind die Effekte, die entstehen, wenn 
Zahlen übereinander auf einem LCD ausgegeben werden.

Aus einer am LCD stehen Zahl "1080" und einem darüber geschriebenen "12" 
wird dann ganz schnell "1280"


Und noch was.
Gewöhn dir gleich von Anfang an, den Datentyp char aussschliesslich nur 
für Variablen zu benutzen, die im Sinne eines Character (also eines 
Zeichens) benutzt werden.
Willst du einen 'kleinen Integer' haben, dann IMMER entweder "signed 
char" oder "unsigned char", je nachdem ob du ein Vorzeichen brauchst 
oder nicht.

Bei char kann sich der Compiler aussuchen, ob er das als signed oder 
unsigned ansehen will. Und je nachdem kann das manchmal ganz schön ins 
Auge gehen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

p.s.: Nein, du hast noch irgendwo anders ein Problem.  Klar ergibt
5 mod 16 wieder 5.  Wenn du dir 1280 mal hexadezimal ansiehst, sind
das 0x500.  Irgendwie werden wohl bei dir da Bytes verwuselt...

(Auch wenn meine Analyse weiter oben falsch war, lasse ich sie mal
drin, denn ich bin nach wie vor der Meinung, dass man die Index-
rechnung eines Ringpuffers normalerweise vorzeichenlos machen
sollte.)

von Hc Z. (mizch)


Lesenswert?

> Aber ich bekomme wirklich 1280 ausgegeben. Habe ich mit Keil-C
> kompiliert, in uC geladen und dann den Wert mittels printf ausgegeben
> und angeschaut.

Ein char auf einem uC kann niemals den Wert 1280 annehmen, dafür müsste 
es mindestens 11 Bits groß sein.  Vermutlich hast Du mit %d ausgegeben 
und Keil unterscheidet bei der printf-Übergabe (nicht ganz 
standardkonform) zwischen 8 Bits und int.  Garbage in, garbage out.

von Heinz B. (hez)


Lesenswert?

>> %d
Stimmt. Weil mit %c (wie ich es verwenden wollte) habe ich nichts 
angezeigt bekommen.

So schaut das Programm aus:
1
#pragma iv(0x8000)    // Einsprungadresse fur Interrupts
2
#include <reg517a.h>  // special function register declarations
3
#include <stdio.h>    // prototype declarations for I/O function
4
#include <string.h>
5
6
#define iBufferMax 16 // max 16 Chars im Ringbuffer  
7
8
void main (void)
9
{
10
   char iX;
11
12
   iX = 5 % iBufferMax;
13
   printf("%d",iX);     // mit %c habe ich nichts bekommen, bei %d bekomme ich 1280 angezeigt
14
15
   while (1)
16
   {
17
   }
18
}

Irgendwas passt mit den Datentypen nicht. Ich versuche jetzt mal den 
Vorschlag vom Jörg. Dass diese define-Sachen automatisch vom Typ int 
sind, wusste ich noch nicht.

von Karl H. (kbuchegg)


Lesenswert?

Logo. %c würde als Character ausgeben.
Es gibt aber auf deinem Ausgabegerät kein sichtbares Zeichen, das den 
ASCII Code 5 hätte.
1
   printf("%d",iX);     // mit %c habe ich nichts bekommen, bei %d bekomme ich 1280 angezeigt

das %d im Formatstring will einen int haben. Dagegen kannst du nichts 
machen. Aber du kannst aus dem char einen int machen :-)
1
    printf( "%d", (int)iX);     // mit %c habe ich nichts bekommen, bei %d bekomme ich 1280 angezeigt

Eigentlich sollte diese Umwandlung nach int den C-Regeln entsprechend 
automatisch gemacht werden, aber da hat wohl Keil im Sinne der Effizenz 
den Sprachstandard ein wenig gebogen :-)

von Stefan E. (sternst)


Lesenswert?

> Stimmt. Weil mit %c (wie ich es verwenden wollte) habe ich nichts
> angezeigt bekommen.

Was erwartest du denn zu sehen beim ASCII-Code 5?

von Heinz B. (hez)


Lesenswert?

Aber eigentlich wollte ich ja keinen int. char würde vollkommen reichen. 
Muss nur von 0 bis ... hm ... kein Ahnung ... vielleicht 16 ... gehen.

von Karl H. (kbuchegg)


Lesenswert?

Heinz B. schrieb:

> Vorschlag vom Jörg. Dass diese define-Sachen automatisch vom Typ int
> sind, wusste ich noch nicht.

Das hat mit #define nichts zu tun.

Jede ganze Zahl ist in C automatisch immer ein int.
Es sei denn die Zahl ist zu gross für einen int. Dann ist sie 
automatisch ein long.

von Karl H. (kbuchegg)


Lesenswert?

Heinz B. schrieb:
> Aber eigentlich wollte ich ja keinen int. char würde vollkommen reichen.

Du meinst unsigned char :-)

> Muss nur von 0 bis ... hm ... kein Ahnung ... vielleicht 16 ... gehen.

Du kannst ja die Variable ruhig auf unsigned char lassen.
Nur zur Ausgabe musst du temporär einen int für printf daraus machen :-)

(Es sei denn Keil hat dem printf ein Formatzeichen für Character 
spendiert, welches einen signed/unsigned char in seiner numerischen Form 
ausgibt. Also das Äuivalent von %d für char -> Compiler Doku studieren)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Heinz B. schrieb:
> Aber eigentlich wollte ich ja keinen int.

Das ist aber bei printf der kleinste Datentyp, mit dem man eine
Ganzzahl ausgeben lassen kann.  Daher musst du ihn zumindest für
deine Ausgabe (die ja vermutlich nur eine Debug-Ausgabe ist, also
später wieder verschwindet) in int oder unsigned int wandeln.

> char würde vollkommen reichen.

Nein.  Ein char ist ein Zeichen, ein druckbares, oder eben auch
nicht, wie das Zeichen <ENQ> in deinem Beispiel (falls du es nach
dem Ausgeben als ASCII interpretierst).

Wie dir oben schon gesagt worden ist, für kleine Ganzzahlen nimmt
man uint8_t/uintfast8_t (falls dein Compiler nach 11 Jahren wenigstens
so viel C99 beherrscht, dass er ein <stdint.h> mitliefert) oder
ersatzweise die Typen unsigned char bzw. signed char, aber niemals
einfach so "char".

> Dass diese define-Sachen automatisch vom Typ int
> sind [...]

Sind sie nicht.  Bitte vergegenwärtige dir als allererstes, was
genau ein #define ist.  Es ist eine pure Textersetzung, die da
gemacht wird.  Die hat (im Rahmen des #define) noch gar keinen
Typ.  Was aber vom Typ int ist, ist die Konstante ("integer
literal" im Englischen) 5, genauso wie die Konstante 16.

[Edith: da hat mich der Karl Heinz mal wieder überholt...]

von Heinz B. (hez)


Lesenswert?

Hm ... ich glaube, ich habe das Problem gefunden.
Habe jetzt alles auf unsigned int umgestellt.
Mit der Ausgabe
printf("%u",iX);
bekomme ich nichts (wie beim Versuch mit Datentyp char). Mit der Ausgabe
printf("xxx %u",iX);
aber genau das richtige.
So ein Käse!

von Karl H. (kbuchegg)


Lesenswert?

Bitte zeige immer das komplette Programm!

Geht auch viel einfacher als einzelne Teile abzutippen:
Im Editor markieren (die meisten Editoren unterstützen Strg+A für "Alles 
markieren". Dann Strg+C für 'In die Zwischenablage kopieren'. Wechseln 
hier ins Browserfenster und im Texteingabefeld Strg+V für 'Aus der 
Zwischenablage einfügen'. Und schwupps hast du mit 3 Tastendrücken 
deinen Code vollständig und fehlerfrei ins Forum übertragen.

von Heinz B. (hez)


Lesenswert?

C ist zum Verzweifeln. Wenn ich das mit dem Ringbuffer habe, schaue ich 
das nicht mehr an :(

Ich versuche immer noch, es irgendwie mit char hinzubekommen. In diesem 
Fall mit unsinged char:
1
#pragma iv(0x8000)    // Einsprungadresse fur Interrupts
2
#include <reg517a.h>  // special function register declarations
3
#include <stdio.h>    // prototype declarations for I/O function
4
#include <string.h>
5
6
#define iBufferMax 16 // max 16 Chars im Ringbuffer  
7
8
// --- Hauptprogramm:
9
void main (void)
10
{
11
   unsigned char iX;
12
   unsigned char iBufferRead = 5;
13
   iX = (unsigned char)(iBufferRead % (unsigned char)iBufferMax);
14
   printf("xx %c",iX);
15
   while (1)
16
   {
17
   }
18
}
Mit %c bekomme ich nur das "xx", mit %c "xx 1280" ausgegeben.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Heinz B. schrieb:

> Mit der Ausgabe
> printf("%u",iX);
> bekomme ich nichts (wie beim Versuch mit Datentyp char). Mit der Ausgabe
> printf("xxx %u",iX);
> aber genau das richtige.

Sagen wir mal so: es wäre nicht der erste Bug in einem Compiler der
Firma Keil, falls dem wirklich so ist.

Nein, normal ist das nicht, beide Ausgaben sollten sich nur im
Vorhandensein bzw. Fehlen von "xxx " unterscheiden.

von Karl H. (kbuchegg)


Lesenswert?

Heinz B. schrieb:

>    printf("xx %c",iX);

Probier doch mal die Variante, die ich dir weiter oben schon mal 
vorgeschlagen hatte

    printf("xx %d", (int)iX);

von Heinz B. (hez)


Lesenswert?

// falls dem wirklich so ist
Einfach selbst ausprobieren:

1
#pragma iv(0x8000)    // Einsprungadresse fur Interrupts
2
#include <reg517a.h>  // special function register declarations
3
#include <stdio.h>    // prototype declarations for I/O function
4
#include <string.h>
5
6
#define iBufferMax 16u // max 16 Chars im Ringbuffer  
7
8
void main (void)
9
{
10
   unsigned int iX;
11
12
   unsigned int iBufferRead = 5u;
13
   iX = iBufferRead % iBufferMax; 
14
   printf("%u",iX);               // gar keine Ausgabe
15
   //printf("xxx %u",iX);           // korrekte Ausgabe
16
17
   while (1)
18
   {
19
   }
20
}

von Heinz B. (hez)


Lesenswert?

>> printf("xx %d", (int)iX);
Ja, damit gehts. Danke! :)

Aber ich bleibe trotzdem dabei. Den Ringbuffer versuche ich noch fertig 
zu machen, aber dann ist Schluss mit C.

von Karl H. (kbuchegg)


Lesenswert?

Heinz B. schrieb:
> // falls dem wirklich so ist
> Einfach selbst ausprobieren:

:_)

Von uns hier hat niemand diesen Compiler

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:

> Von uns hier hat niemand diesen Compiler

Und ob man ihn nach derartigen Ungereimtheiten freiwillig benutzen
würde, bleibt mal dahin gestellt. ;-)

Heinz, du solltest nicht C (das gewiss seine Eier hat) mit der
Qualität dieses Compilers gleich setzen.  Vielleicht probierst du's
ja beim nächsten Mal mit einem Compiler, der ein wenig mehr Wert
auf Konformität zum C-Standard legt...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.