mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Konstanten const define rechenfehler


Autor: Heinz B. (hez)
Datum:

Bewertung
0 lesenswert
nicht 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.

#define iBufferMax 16 // max 16 Chars im Ringbuffer  
void main (void)
{
   char iX;
   iX = 5 % iBufferMax; // hat den Wert 1280!!!
}
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:

const char iBufferMax = 16; // max 16 Chars im Ringbuffer  
void main (void)
{
   char iX = 5 % iBufferMax; // 1280
   char aY[iBufferMax];      // <- mag C nicht
}
(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' :)

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

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

Autor: Heinz B. (hez)
Datum:

Bewertung
0 lesenswert
nicht 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 ... :)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
#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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.)

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Heinz B. (hez)
Datum:

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

So schaut das Programm aus:
#pragma iv(0x8000)    // Einsprungadresse fur Interrupts
#include <reg517a.h>  // special function register declarations
#include <stdio.h>    // prototype declarations for I/O function
#include <string.h>

#define iBufferMax 16 // max 16 Chars im Ringbuffer  

void main (void)
{
   char iX;

   iX = 5 % iBufferMax;
   printf("%d",iX);     // mit %c habe ich nichts bekommen, bei %d bekomme ich 1280 angezeigt

   while (1)
   {
   }
}

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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
   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 :-)
    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 :-)

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Heinz B. (hez)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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...]

Autor: Heinz B. (hez)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Heinz B. (hez)
Datum:

Bewertung
0 lesenswert
nicht 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:
#pragma iv(0x8000)    // Einsprungadresse fur Interrupts
#include <reg517a.h>  // special function register declarations
#include <stdio.h>    // prototype declarations for I/O function
#include <string.h>

#define iBufferMax 16 // max 16 Chars im Ringbuffer  

// --- Hauptprogramm:
void main (void)
{
   unsigned char iX;
   unsigned char iBufferRead = 5;
   iX = (unsigned char)(iBufferRead % (unsigned char)iBufferMax);
   printf("xx %c",iX);
   while (1)
   {
   }
}
Mit %c bekomme ich nur das "xx", mit %c "xx 1280" ausgegeben.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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);

Autor: Heinz B. (hez)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
// falls dem wirklich so ist
Einfach selbst ausprobieren:

#pragma iv(0x8000)    // Einsprungadresse fur Interrupts
#include <reg517a.h>  // special function register declarations
#include <stdio.h>    // prototype declarations for I/O function
#include <string.h>

#define iBufferMax 16u // max 16 Chars im Ringbuffer  

void main (void)
{
   unsigned int iX;

   unsigned int iBufferRead = 5u;
   iX = iBufferRead % iBufferMax; 
   printf("%u",iX);               // gar keine Ausgabe
   //printf("xxx %u",iX);           // korrekte Ausgabe

   while (1)
   {
   }
}

Autor: Heinz B. (hez)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Heinz B. schrieb:
> // falls dem wirklich so ist
> Einfach selbst ausprobieren:

:_)

Von uns hier hat niemand diesen Compiler

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.