www.mikrocontroller.net

Forum: Compiler & IDEs "Geisterwerte" in einer Variable trotz Initialisierung


Autor: JayJay (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo alle zusammen,

ich hab ein kleines Problem mit einer Variable, die ein gewisses 
eigenleben entwickelt hat.

Ich bastle gerade einen kleinen Taschenrechner und habe erstmal eine 
einfache Addition implementiert. Alles funktioniert prima... zumindest 
bis man versucht Zahlen über 999 miteinander zu addieren. Sobald ein 
Summand 4-stellig wird lässt sich etwas interessantes beobachten: In der 
nächsten Addition werden die beiden letzten Zahlen des vorherigen ersten 
Summanden ab der 2. eingegeben Zahl hinten angehängt! :-D Ich weiß wie 
bekloppt das klingt, aber mein lcd lügt mich nicht an. Ein Beispiel:

1. Addition
Eingabe:
1122 + 3
Ausgabe:
1125

2. Addition
Eingabe:
22 + 3
Ausgabe:
2225, weil 22 die letzten beiden Stellen aus der vorhergehenden Addition 
abbekommt und zu 2222 wurde.

Ich dachte natürlich es liegt daran, dass ich die Variablen oder den 
Zwischenspeicher nicht wieder auf 0 setzte, bevor ich die nächste 
Addition mache, aber daran liegt es ganz sicher nicht (s. Quellcode) und 
die letzten beiden Stellen werden ja auch erst angehängt, nachdem die 2. 
Zahl eingegeben wurde.

Mich macht das irgendwie ganz schön konfus... hier jetzt erstmal der 
Quellcode:
uint16_t add_zahl (char zahl, char ausg){  // Zahl an den Zwischenspeicher hängen
  if (flag == 1){                    // Bildschirm nach fertiger Rechnung löschen
    lcd_clear();
    flag =0;
  }

  lcd_string(ausg);                 // eingegebene Zahl ausgeben
  wert[i] = zahl;                   // Zahl an den Zwischensp. hängen
  i++;                              // Position im Zwischensp. erhöhen
  if(i > 5){                        // Abfrage ob eingegebene Zahl zu groß für 16-bit Variablen
    lcd_home();
    lcd_string("zu lang!");
    _delay_ms(2000);
    lcd_clear();
    i = 0;
  }
  return 0;
}
 
int main( void )
{

 
// unwichtiges Initialisierungsblabla

  int16_t a = 0;
  int16_t b = 0;
  char erg[8];
  sprintf(wert, "0");
    
    while(1){
    
    if( get_key_short( 1<<KEY1 )){          // Taster für die Eingabe von "1"
      add_zahl(49, "1");              // Übergabe von ASCII-Wert und der Zahl als char *
    }
    
               // weitere Taster

    if( get_key_short( 1<<KEY_PLUS )){      // Plus Taste
                a = atoi(wert);                 // in int umwandeln
      lcd_string("+");
      sprintf(wert, "0");             // Zwischenspeicher nullen
      i=0;        // Nummernlänge zurücksetzen
    }

    if( get_key_short( 1<<KEY_GLEICH )){    // Gleich Taste
      b = atoi(wert);                 // in int umwandeln
      b = a+b;                        // die Addition
      itoa(b, erg, 10);               // zurückwandeln
 
      set_cursor(0,2);
      lcd_string(erg);                // Ausgeben
      
      sprintf(wert, "0");             // alles auf 0 setzten
      i=0;
      flag = 1;
      a=0;
      b=0;
    }

  }
}

* kann man das nicht auch irgendwie einheitlich machen? Zumal der letzte 
Übergabewert beim compilieren immer die Warnung erzeugt:
"warning: passing argument 2 of 'add_zahl' makes integer from pointer 
without a cast"

Ich programmiere eigentlich Java und das auch nicht besonders viel, 
daher kann es gut sein, dass mein C Code wegen Unleserlichkeit und 
Umständlichkeit einigen Unmut bei euch hervorruft. Korrigiert mich bitte 
bei allem, was man auch einfacher lösen kann, schließlich lerne ich 
gerne dazu!

Gruß,
JayJay

Autor: JayJay (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
upps hatte die globalen Variablen vergessen:
char wert [5];
uint8_t i = 0;
uint8_t flag = 0; 

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Typischer Fehler beim Umgang mit Strings.
Der String in wert[] wird nicht terminiert und außerdem ist das Array zu 
klein. Und du schreibst ggf. auch hinter das Array.

Autor: JayJay (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So jetzt hats sichs ausgegeistert. Indem ich vor jeder Umwandlung noch 
eine Endekennung hinten ans Char-Array gepackt habe funktioniert alles 
wunderbar. Ich bin doch schon zu sehr an Java gewöhnt...

Ein bisschen Verwirrung haben noch die Funktionen atoi() und itoa() 
gestiftet, die ich in atol() und ltoa() geändert habe, damit ich mit 
32bit Variablen rechnen kann.

Ärgerlich ist nur, dass die beiden Funktionen keine Errormeldungen 
zurückgeben, wenn das Ergebnis außerhalb des Wertebreichs liegt. Bei 
normalem C sollte das eigentlich so sein, aber AVR GCC sagt in der 
Header-Datei stdlib.h:
"In contrast to

\code (int)strtol(s, (char **)NULL, 10); \endcode

this function does not detect overflow (\c errno is not changed and
the result value is not predictable), [blabla]"

wie strol() die Errormeldung bei strol aber genau funktioniert hab ich 
nicht so recht verstanden.

Für Fließkommazahlen gibt es schließlich atof() aber kein ftoa(). Wie 
soll ich Fließkommazahlen dann auf dem LCD ausgeben?

Naja und eine Frage bleibt: Gibts irgendeine elegante Möglichkeit diese 
doofen Warnungen loszuwerden:"warning: passing argument 2 of 'add_zahl' 
makes integer from pointer without a cast"

so long...

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

Bewertung
0 lesenswert
nicht lesenswert
> Naja und eine Frage bleibt...
Ja, "1" ist ein String :-o
besser wäre
  add_zahl(49, '1');  // Übergabe von ASCII-Wert und der Zahl als char 

Oder (wenn der Kommentar stimmen soll)
uint16_t add_zahl (char zahl, char* ausg){  // Zahl an den Zwischenspeicher hängen
:
  lcd_string(ausg);   ???? was ist der Uebergabewert an lcd_string()? ein Pointer?
:
  add_zahl(49, "1");  // Übergabe von ASCII-Wert und der Zahl als char Pointer


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

Bewertung
0 lesenswert
nicht lesenswert
JayJay wrote:

> Ärgerlich ist nur, dass die beiden Funktionen keine Errormeldungen
> zurückgeben, wenn das Ergebnis außerhalb des Wertebreichs liegt. Bei
> normalem C sollte das eigentlich so sein,

Nein.  Du solltest dir den C-Standard mal ansehen...

> wie strol() die Errormeldung bei strol aber genau funktioniert hab ich
> nicht so recht verstanden.

Die ,,Meldung'' besteht daraus, eine 0 als Ergebnis der Konvertierung
zu liefern.  Falls das Ergebnis zu groß oder klein wird, wird
errno auf ERANGE gesetzt und die Konvertierung liefert LONG_MAX
bzw. LONG_MIN.

Die Erkennung eines Fehlers erfolgt, indem man die Adresse einer
Zeigervariablen bei endptr angibt.  Nach der Konvertierung, die 0
ergeben hat, schaut man dann nach, worauf der Zeiger zeigt.  Zeigt
er auf ein Zeichen '\0', dann war die Konvertierung (mit dem Wert
0) erfolgreich, da alle Zeichen bis zum Ende des Strings konvertiert
werden konnten.  Zeigt der Zeiger auf was anderes, dann ist das
das erste nicht mehr konvertierbare Zeichen.  Ob du nun einen String
"12x" als gültigen Wert 12 akzeptieren willst (endptr würde auf das
Zeichen 'x' zeigen) oder nicht, musst du mit deiner Applikation
vereinbaren.

All das ist Standard-C; das kannst du besser an einem PC ausprobieren
als auf einem Controller.  Wenn du verstanden hast, wie's funktio-
niert, übernimmst du dann den Code in den Controller.

> Für Fließkommazahlen gibt es schließlich atof() aber kein ftoa(). Wie
> soll ich Fließkommazahlen dann auf dem LCD ausgeben?

sprintf (dann bitte aber gegen die Gleitkommaversion linken), oder
als Einfach-Varianten dtostre/dtostrf (das sind private Erweiterungen
der avr-libc gegenüber dem Standard).

> Naja und eine Frage bleibt: Gibts irgendeine elegante Möglichkeit diese
> doofen Warnungen loszuwerden:"warning: passing argument 2 of 'add_zahl'
> makes integer from pointer without a cast"

Natürlich: sauberen Code schreiben.  Wenn dein zweites Argument ein
Zeiger ist, warum deklarierst du add_zahl dann so, dass sie einen
Integer übernimmt?

Autor: JayJay (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ich hab mich jetzt ein bisschen in Zeiger und Vektoren eingelesen. In 
der Tat gibt es keine Warnungen mehr und alles funktioniert prima, wenn 
der Funktionskopf so aussieht:
uint8_t add_zahl (char zahl, char *ausg){
und so aufgerufen wird:
add_zahl(49, "1");
Nur irgendwie verstehe ich nur so halb warum, denn der 
Dereferenzierungsoperator * sagt doch nur, dass auf den Inhalt der 
übergeben Adresse zugegriffen wird und wird deswegen so etwa in dieser 
Form benutzt:
void cquadrat(int *wert){
  *wert=(*wert)*(*wert);
}

void main (void){
  int x;
  scanf("%i",&x);
  cquadrat(&x);
  //irgendeine Ausgabe
}
Aber
add_zahl(49, "1");
 übergibt doch aber keine Adresse einer Variable sondern einen 
stinknormalen char. Oder behandelt C das wie char-Array der Länge 2 (
{'1',0}
) bei der Übergabe, sodass noch dereferenziert muss (schließlich ist ein 
char-Array ja immer nur die Adresse eines Variablenfeldes)?


> Nein.  Du solltest dir den C-Standard mal ansehen...

Hupps.. ich hab mir ne C++ Referenz angeschaut und gedacht in diesen 
elementaren Funktionen der stdlib hätte sich nix geändert... falsch 
gedacht wie sich rausstellt. ^^

so long...

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

Bewertung
0 lesenswert
nicht lesenswert
>>> "1" <<<
Ist wie von dir beobachtet ein String = Char-Array {'1',0}.

Ein Char ist:
>>> '1' <<<

Autor: Daniel -------- (root)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
void cquadrat(int *wert){
  *wert=(*wert)*(*wert);
}

Das ist eine grosse pfui kacka in C und C++. Wie es Java und C# 
behandeln
weiss ich nicht. C und C++ haben so etwas wie Sequenzpunkt, dh alle
Nebeneffekte klingen ab nach einem Sequenzpunkt.
Hier modifizierst du Quelle und Ziele in einem Sequenzpunkt ..
das ist analog zu i = i++;

weitere Fehlerquellen ähnlicher Art
int foo(int a, int b){return a*b;}
int x=1;
foo(x,++x);

Ich glaube die C Profis sind da sensibilisiert dadrauf und wenn sie
andere Sprachen lernen, dann fragen die sich wie macht es wohl Java
an der Stelle? Die meisten Javaprogrammierer achten viel weniger
dadrauf, meine Meinung.

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

Bewertung
0 lesenswert
nicht lesenswert
Daniel -------- wrote:

> das ist analog zu i = i++;

Nein, es ist analog zu i = i * i, und das ist völlig legal.
Undefiniert wäre sowas wie:

*wert = (*wert)++ * (*wert);

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel -------- wrote:
>
> void cquadrat(int *wert){
>   *wert=(*wert)*(*wert);
> }
> 

Das ist doch ok -- im Gegensatz zu
i = i++;

oder
foo (a++, a++);

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Aber
> add_zahl(49, "1");
> übergibt doch aber keine Adresse einer Variable

Doch - naja, nicht die einer Variablen, aber die eines "string literal".

> sondern einen stinknormalen char. Oder behandelt C das wie char-
> Array der Länge 2 ({'1',0}) bei der Übergabe,

Das wird nicht nur wie ein char-Array behandelt, sondern es ist eins. 
Bei der Übergabe an die Funktion wird dann ein Zeiger auf das erste 
Zeichen daraus. Wie sollte das auch sonst funktionieren? So ein String 
Literal ist immer gleich aufgebaut, egal wieviele Zeichen da drinstehen.
Wie du ja selbst oben schreibst, mußt du '1' draus machen, wenn du 
wirklich nur ein einzelnes Zeichen haben willst und kein Array.

> sodass noch dereferenziert muss (schließlich ist ein
> char-Array ja immer nur die Adresse eines Variablenfeldes)?

Das stimmt nicht. Ein char-Array ist nicht die Adresse, aber es kann in 
den meisten (aber nicht allen Fällen) genauso verwendet werden. Es ist 
wichtig, den Unterschied zu erkennen.

> Hupps.. ich hab mir ne C++ Referenz angeschaut und gedacht in
> diesen elementaren Funktionen der stdlib hätte sich nix geändert...
> falsch gedacht wie sich rausstellt. ^^

Eigentlich hat sich da auch nichts geändert. C++ übernimmt einfach die 
komplette Standardlib von C und hängt noch ein paar Sachen dazu.

Autor: JayJay (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Eigentlich hat sich da auch nichts geändert.

Auf cplusplus.com findet man für atoi() und seine Freunde:

>On success, the function returns the converted integral number as an int value.
>If no valid conversion could be performed, a zero value is returned.
>If the correct value is out of the range of representable values, INT_MAX >or 
INT_MIN is returned.


Hmm... ich finde diese ganzen Zeiger relativ kompliziert... Zwar gibt es 
auch bei Java Vektoren aber die benutzt man selten. Hier wird bei der 
Übergabe nicht auf Adressen oder ähnliches geachtet, es muss nix 
dereferenziert werden und das macht die geschichte angenehm einfach.

Ich denk mal das Thema ist damit abgeschlossen.

Danke für eure Hilfe.

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.