Forum: Mikrocontroller und Digitale Elektronik "ordentliches" Programmieren


von Azubi (Gast)


Lesenswert?

Hallo


Mein Lehrer meint, meine Programme sind logisch sehr gut, aber nicht 
sehr ordentlich und deswegen gibt es immer wieder Punktabzug.

Kann mir mal einer sagen, ob dieser Programmschnipsel ordentlich ist ?
Wie kann man das besser machen, hat jmd. vlt. eine Homepage wo solche 
Regeln fur Kaoten wie mich notiert sind?

ISR(TIMER1_COMPA_vect)
{
  sekunde++;

  if (sekunde == 60)
  {
    minute_d++;
    sekunde = 0;

    if (minute_d == 10)
    {
      minute_d = 0;
      minute_c++;

      if (minute_c == 6)
      {
        minute_c = 0;
        stunde_b++;
        if (stunde_b == 10)
        {
          stunde_b = 0;
          stunde_a++;

          if (stunde_a == 3)
          {
            stunde_a = 0;
          }
        }
      }

    }

  }
}

von Sebastian H. (technik_freak)


Lesenswert?

Warum fragst Du die Minuten nicht wie die Sekunden ab (genau so wie die 
Stunden)?

Der Lehrer meint mit ordentlich wahrscheinlich, ob es nicht 
übersichtlicher ist, entweder das Ganze in eine Funktion zu verpacken 
oder aber, dass deine Schritte mit Kommentaren Versehen werden, damit 
auch zum Zeitpunkt x erkennbar ist, was Du meinst bzw. willst.

von PaukerPlusPlus (Gast)


Lesenswert?

Lehrer sind Vorbilder - Vorbilder sind Bilder - Bilder sollte man 
aufhängen.

->
Frage er-sie-es und du erfährst wie es klingt, das Klatschen nur der 
einen Hand...

von Thomas E. (thomase)


Lesenswert?

Sebastian H. schrieb:
> Warum fragst Du die Minuten nicht wie die Sekunden ab (genau so wie die
> Stunden)?
Damit hat er für Minuten und Stunden 2 BCD-Zähler, die er direkt auf 
sein 4-stelliges Display geben kann. Da die Sekunden bei seiner Uhr 
nicht angezeigt werden, braucht er das da nicht.

> Der Lehrer meint mit ordentlich wahrscheinlich, ob es nicht
> übersichtlicher ist, entweder das Ganze in eine Funktion zu verpacken
> oder aber, dass deine Schritte mit Kommentaren Versehen werden, damit
> auch zum Zeitpunkt x erkennbar ist, was Du meinst bzw. willst.

Störend sind vielleicht die Variablennamen. minute_1 oder minute_10 wäre 
aussagekräftiger. stunde_a, stunde_b, minute_c, minute_d finde ich ein 
wenig unglücklich.
Ansonsten ist der Code vernünftig eingerückt und auch ohne Kommentare, 
wenn man sich nicht demonstrativ blöd anstellt, leicht zu verstehen.
Daß mit dem Blödanstellen ist allerdings eine Grundeigenschaft eines 
Lehrers. Die eingefügten Leerzeilen liessen sich noch etwas homogener 
verteilen.
Was sagt er denn, wie es besser aussähe?

mfg.

von Leerer (Gast)


Lesenswert?

Der Einsatz von "==" ist an Fehlernfällig. Nimm lieber den ">" Operator.

Also "(sekunde == 60)" ersetzt Du durch "(sekunde > 59)".

Dann sind Zahlen im Code nicht immer selbserklärend. Also statt Zahlen 
einen selbsterklärenden Namen definieren.
1
#define MAX_SEKUNDEN 59
2
3
if (sekunde > MAX_SEKUNDEN) {

Und Du solltest darauf achten, innerhalb einer ISR so wenig wie möglich 
Code unter zu bringen. Funktionsaufrufe innerhalb einer ISR ist auch 
keine gute Idee.

z.B.
1
#define FALSE 0
2
#define TRUE !0
3
4
ISR(TIMER1_COMPA_vect)
5
{
6
  sekunde++;
7
8
  sekundeGeaendert = TRUE;
9
}
10
11
int main()
12
{
13
   if (sekundeGeaendert)
14
  {
15
       // Sekunden auswerten
16
       sekundeGeaendert = FALSE;
17
  }
18
}

von Thomas E. (thomase)


Lesenswert?

Leerer schrieb:
> Der Einsatz von "==" ist an Fehlernfällig. Nimm lieber den ">" Operator.
>
> Also "(sekunde == 60)" ersetzt Du durch "(sekunde > 59)".
Darüber lief hier schon mal eine längere Diskussion, die zum Ergbebnis 
hatte, daß es sich um reine Paranoia handelt, eine Variable 
grundsätzlich, wie in diesem Fall mit >59 oder >=60 abzufragen.

> Dann sind Zahlen im Code nicht immer selbserklärend. Also statt Zahlen
> einen selbsterklärenden Namen definieren.
Grundsätzlich nicht falsch. Aber bei einer Uhr vollkommen überflüssig, 
da es sich hierbei um eine jedem bekannte Konstante handelt. Und da das 
Programm eindeutig als Uhr zu erkennen ist, auch selbsterklärend und nur 
unnötige Schreiberei.

> Und Du solltest darauf achten, innerhalb einer ISR so wenig wie möglich
> Code unter zu bringen. Funktionsaufrufe innerhalb einer ISR ist auch
> keine gute Idee.
Auch wenn man das immer und immer wiederholt. Nein, man darf auch in 
einer ISR ein bisschen rechnen. Dann braucht man sich auch keine Sorgen 
machen, daß der Controller mal irgendwo aufgehalten wird und die Sekunde 
vielleicht doch auf 61 erhöht werden könnte.

Aber wahrscheinlich hast du Recht und der Lehrer will genau das sehen.

mfg.

von Reinhard Kern (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> die zum Ergbebnis
> hatte, daß es sich um reine Paranoia handelt

deswegen muss das noch lange nicht richtig sein - man muss bloss die 
Schrittweite des Increments ändern und schon läuft == ins Leere, 
Ergebnis Endlosschleife.

Aber was ein Lehrer unter ordentlich versteht, kann nur er selbst sagen, 
wenn überhaupt, und es dürfte auch bei jedem etwas anderes sein.

Gruss Reinhard

von Thomas E. (thomase)


Lesenswert?

Reinhard Kern schrieb:
> Thomas Eckmann schrieb:
>> die zum Ergbebnis
>> hatte, daß es sich um reine Paranoia handelt
>
> deswegen muss das noch lange nicht richtig sein - man muss bloss die
> Schrittweite des Increments ändern und schon läuft == ins Leere,
> Ergebnis Endlosschleife.
Noch mehr an den Haaren herbeigezogen geht es nicht?
1
Sek = Sek + 1;
Also die Oberparanoikervariante.

mfg.

von Leerer (Gast)


Lesenswert?

Einem Programmierer wird ja eine gesunde Faulheit nachgesagt. Und so 
halte ich es auch mit dem "<" Operator. Da dieser bei einer "for()" zum 
Einsatz kommt, setzt ich diesen auch zur Prüfung von Grenzen bei "if()" 
ein. Da muss ich mir nicht so viel merken. ;-)

Ich möchte nur sich gehen, dass in jedem Fall die Überschreitung der 
Grenze bemerkt wird.

Ein Beispiel für so einen Fall hast du ja selbst beschieben!

> Dann braucht man sich auch keine Sorgen machen, daß der Controller mal irgendwo
> aufgehalten wird und die Sekunde vielleicht doch auf 61 erhöht werden könnte.
Und schon würde bis zum Überlauf der Variable "sekunde" gezählt werden 
mit allen Folgefehlern. Viel Spass beim Fehlersuchen. Ganz miese 
SW-Qualität!

von Zeigefinger (Gast)


Lesenswert?

Da es um Ausbildung geht, könnten ein paar Kommentare helfen. Auch wenn 
das so selbsterklrärend ist.

von Thomas E. (thomase)


Lesenswert?

Leerer schrieb:
 >> Dann braucht man sich auch keine Sorgen machen, daß der Controller 
mal irgendwo
>> aufgehalten wird und die Sekunde vielleicht doch auf 61 erhöht werden könnte.
> Und schon würde bis zum Überlauf der Variable "sekunde" gezählt werden
> mit allen Folgefehlern. Viel Spass beim Fehlersuchen. Ganz miese
> SW-Qualität!
Deswegen rechnet man das ja auch in der ISR. Und hängt sich nicht 
ständig an diesem bescheuerten 
In-einer-ISR-so-wenig-Code-wie-möglich-Dogma auf. Irgendwo muss das 
gerechnet werden. Und in diesem Fall wird es in der ISR auf jeden Fall 
richtig gerechnet. Und wenn das bisschen Code Probleme bereitet, hat man 
woanders ein viel grösseres Problem.

mfg.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Es fehlt natürlich die Kommentierung, insbesondere weil der unbedarfte 
Leser des Quelltextes beim Stichwort "Uhr" natürlich an eine mit 12h- 
oder 24h-Periodizität denkt. Die hier implementierte Uhr hat jedoch 
einen 30h-Zyklus.

von cppler (Gast)


Lesenswert?

Also entweder Dein Lehrer gibt Euch mal eine verbindliche 
Rahmenbedingung an, z.B.:
1
...
2
if(bedingung){
3
  machwas;
4
}
5
...
bzw.
1
...
2
if(bedingung)
3
{
4
5
   machwas;
6
7
}

Oder Du schreibst halt nach eigenem Gusto.
Sinnvolle Variablennamen helfen vor allem auch beim Debuggen, gleiches 
gilt für Funktionsnamen:
1
void zeigeDatum(uint8_t uDisplaynummer)
ist selbsterklärend, während:
1
void blubber(int foo)
nur im gesamten Kontext erfaßt werden kann.
Es gibt keine allgemeingültigen Regeln daher einigt man sich bei 
gemeinsamen Projekten vorher auf den Stil, also Klammernsetzung, 
Einrückung und Variablennamen.

von Jan H. (j_hansen)


Lesenswert?

Zuerst einmal: hier ist es nicht wirklich notwendig, weil das Programm 
ja sehr kurz und übersichtlich ist. Aber es gibt ein paar Grundsätze die 
man beachten sollte, damit auch größere Programme beherrschbar bleiben.

- In Zählschleifen nicht auf Gleichheit prüfen, sondern auf >= (auch 
wenn es oben als "Paranoia" bezeichnet wurde - es schadet nicht sich das 
anzugewöhnen)
- Nicht zu viele Ebenen einrücken. Normalerweise sagt man es sollten 
höchstens drei Ebenen sein - da bist du ohne Grund darüber. Also die IFs 
nicht schachteln, sondern nacheinander schreiben
- Kommentare (hier nicht unbedingt notwendig, aber ganz zu Beginn zu 
schreiben was das Ding überhaupt macht schadet bestimmt nicht)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Jan Hansen schrieb:
> Also die IFs
> nicht schachteln, sondern nacheinander schreiben

Na, ganz sicher nicht! ich muss den Stundenüberlauf nur prüfen, wenn ich 
vorher einen Minutenüberlauf hatte. Und den mus sich nur prüfen, wenn 
vorher ein Sekundenuüerlauf da war. So gesehen also aus meiner sicht 
vollkommen richtig.

Wenn man schon Einrückungen sparen will, könnte man an der Stelle das 
nicht ganz so böse "hidden goto" = return einsetzen.

von Jan H. (j_hansen)


Lesenswert?

Michael Reinelt schrieb:
> Na, ganz sicher nicht! ich muss den Stundenüberlauf nur prüfen, wenn ich
> vorher einen Minutenüberlauf hatte. Und den mus sich nur prüfen, wenn
> vorher ein Sekundenuüerlauf da war. So gesehen also aus meiner sicht
> vollkommen richtig.

Es ging auch nicht um nicht "richtig", sondern um "ordentliche" 
Programmierung.

> Wenn man schon Einrückungen sparen will, könnte man an der Stelle das
> nicht ganz so böse "hidden goto" = return einsetzen.

Genau. Aber warum schreibst du dann zu Beginn "Na, ganz sicher nicht!"?

von Thomas E. (thomase)


Lesenswert?

Jan Hansen schrieb:
> - In Zählschleifen nicht auf Gleichheit prüfen, sondern auf >= (auch
> wenn es oben als "Paranoia" bezeichnet wurde - es schadet nicht sich das
> anzugewöhnen)
Wenn eine Variable dort geprüft wird, wo sie auch verändert wurde, also 
bei der Sekunde direkt danach, ist es egal. Dann kann man auch auf == 
prüfen. Es schadet nicht, auf >= zu prüfen, bringt aber auch keinen 
Vorteil.

Prüft man die Sekunde aber erst in main, statt in der ISR, könnte sie, 
wodurch auch immer, größer als 60 werden. Eine Prüfung auf >= würde das 
Programm scheinbar normal weiterlaufen lassen. Die Uhr geht jetzt aber 
1s nach. Und das schadet dann sehr wohl. Weil es einen Fehler 
produziert, den man, da die Sekunden nicht angezeigt werden, nicht 
bemerkt und man gibt dem Quarz die Schuld, weil die Uhr am Ende des 
Tages ein paar Sekunden hinterher hängt.
Dabei liegt es nur an der Dummheit des Programmierers.

Das mal zum Thema ganz miese SW-Qualität, Herr Leerer.

mfg.

von Martin K. (maart)


Lesenswert?

Die Überprüfung der Sekunde würde ich auch in der ISR machen: Aus den 
Augen, aus dem Sinn. Dann brauche ich in Zukunft nicht mehr dran denken, 
ob main() auch wirklich immer schnell genug ausgeführt wird.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Keiner weiß, was der Lehrer als ordentlich empfindet. Falls auch meine
bescheidene Meinung gefragt ist:

Unordentlich ist, dass in der äußersten if-Anweisung die aktuelle Ziffer
(sekunde) auf 0 zurückgesetzt wird, nachdem die nächste Ziffer
(minute_d) inkrementiert wird. In allen darauffolgenden if-Anweisung ist
es andersherum, was ich auch als logischer empfinde.

Ein klein wenig unordentlich ist auch, dass vor jeder if-Zeile eine
Leerzeile steht, nur vor if (stunde_b ...) nicht.

Zwischen die schließenden Blockklammern haben sich zwei Leerzeilen
eingeschlichen, die ebenfalls etwas unordentlich aussehen.

Bis auf diese Kleinigkeiten ist der Code für meinen Geschmack aber
völlig in Ordnung.


Wenn Ordentlichkeit allerhöchste Priorität hat, hätte ich noch folgendes
anzubieten:
1
ISR(TIMER1_COMPA_vect)
2
{
3
  (sek  = (sek  + 1) % 60) ||
4
  (min0 = (min0 + 1) % 10) ||
5
  (min1 = (min1 + 1) %  6) ||
6
  (std0 = (std0 + 1) % 10) ||
7
  (std1 = (std1 + 1) %  3) ;
8
}

Man vermeidet damit die oben schon kritisierte tiefe Verschachtelung,
ohne auf das "hidden goto" (wie Michael es nennt) zurückgreifen zu
müssen.

Der Code ist aber trotzdem kein gutes Beispiel, da er 5 unnötige
Modulooperationen enthält, die gerade kleine Mikrocontroller schon
etwas plagen. Außerdem könnte er einen C-Anfänger verwirren :)

von P. M. (o-o)


Lesenswert?

Sebastian H. schrieb:
> mit Kommentaren Versehen

Ein gutes Programm braucht nicht viele Kommentare. Eine Beschreibung, 
was die Funktion oder der Programmabschnitt tut, reicht meist völlig 
aus.

von H.Joachim S. (crazyhorse)


Lesenswert?

Ich persönlich würde in der ISR auschliesslich Sekunden zählen (bis 
>=86400)
Die Zerlegung in BCD dann erst bei fälliger Ausgabe.

von hallo (Gast)


Lesenswert?

Yalu X. schrieb:
> Wenn Ordentlichkeit allerhöchste Priorität hat, hätte ich noch folgendes
> anzubieten:
> ISR(TIMER1_COMPA_vect)
> {
>   (sek  = (sek  + 1) % 60) ||
>   (min0 = (min0 + 1) % 10) ||
>   (min1 = (min1 + 1) %  6) ||
>   (std0 = (std0 + 1) % 10) ||
>   (std1 = (std1 + 1) %  3) ;
> }
>
> Man vermeidet damit die oben schon kritisierte tiefe Verschachtelung,
> ohne auf das "hidden goto" (wie Michael es nennt) zurückgreifen zu
> müssen.
>
> Der Code ist aber trotzdem kein gutes Beispiel, da er 5 unnötige
> Modulooperationen enthält, die gerade kleine Mikrocontroller schon
> etwas plagen. Außerdem könnte er einen C-Anfänger verwirren :)

Finde ich auch nicht schön, wenn schon
1
sek = ++sek % ...

von Yalu X. (yalu) (Moderator)


Lesenswert?

hallo schrieb:
> Finde ich auch nicht schön, wenn schon
>
> sek = ++sek % ...

Das wäre aber ein Fehler (bzw. undefiniert). ++sek ist nicht dasselbe 
wie sek+1.

von hallo (Gast)


Lesenswert?

Wo ist der Unterschied zwischen ++sek und sek++ ?
Ein Ausdruck wird immer ausgewertet, also geht doch s = ++s;

von Martin K. (maart)


Lesenswert?

hallo schrieb:
> Wo ist der Unterschied zwischen ++sek und sek++ ?

Post-/Preinkrement. So und jetzt schlag im C-Buch nach.

von hallo (Gast)


Lesenswert?

Martin Kreiner schrieb:
> hallo schrieb:
>> Wo ist der Unterschied zwischen ++sek und sek++ ?
>
> Post-/Preinkrement. So und jetzt schlag im C-Buch nach.

A...............roganter Wicht! Deine Sorte Menschen verderben ein 
Forum.

Das war natürlich ironisch gemeint.

von Thomas E. (thomase)


Lesenswert?

hallo schrieb:
> Martin Kreiner schrieb:
>> hallo schrieb:
>>> Wo ist der Unterschied zwischen ++sek und sek++ ?
>>
>> Post-/Preinkrement. So und jetzt schlag im C-Buch nach.
>
> A...............roganter Wicht! Deine Sorte Menschen verderben ein
> Forum.
>
> Das war natürlich ironisch gemeint.
War sowieso ein Eigentor.
Prä-/Postinkrement wäre richtig gewesen.


s = ++s produziert ein warning:
warning: operation on 's' may be undefined

Funktioniert aber trotzdem.

mfg.

von Ralph (Gast)


Lesenswert?

"ordentliches" Programmieren ist eine sehr formale Sache.

Das sieht beim Proggen nicht unbedingt effektiv aus weil man vieles 
Macht das so nicht notwendig erscheint. Aber bei der Fehlersuche , vor 
allem eine längere Zeit später sehr hilfreich wird.

Allerdings fordert es einiges an Disziplin sich das anzugewöhnen.

Als Anhalt hier kann zb die Regeln von MISRA dienen.

zb:
If schleife. Erstelle immer den ELSE Pfad mit , auch wenn er leer 
bleibt.

 if (stunde_a == 3)
 {
    stunde_a = 0;
 }
 else
 {
    /* bleibt leer  */
 }

Verwende keine "magic Number".
Also anstatt : (stunde_a == 3)
ist besser :   (stunde_a == MINUTEN_ZEHNER_STELLE)

und in einem *.H File kommt dann  #define MINUTEN_ZEHNER_STELLE 3
Dafür gibt es 2 gute Gründe
1. Dem Namen kannst du auch nach einem Jahr noch entnehmen warum da die 
3 Steht, der Zahl nicht.
2. Änderst du im Programm die Logik woraus sich ergibt, das du dort eine 
2 benötigst musst du jede Stelle an der dieser Wert verwendet wird 
manuell ändern. Per Define musst du nur eine Stelle ändern.



Füge großzügig erklärende Kommentare ein. Also WARUM du etwas an einer 
Stelle so gelöst hast. Es macht keinen Sinn im Kommentar die Logik zu 
beschreiben.
Die Logik kann man im Code erkennen, das WARUM jedoch nicht.
Verwende für Kommentare auch immer ein einheitliches Format. Sieht 
einfach schöner aus.

Abfragen auf "==" bergen immer die Gefahr das sich der Wert außerhalb 
der jeweiligen Funktion geändert hat und dadurch nicht passt.
Besser auf einen Bereich abfragen. Und falls der Wert außerhalb des 
erlaubten Bereiches ist eine Fehlerbehandlung durchführen.
zb eine Korrekturrechung.
Das außerhalb kann eine unsaubere Handhabng der Variable in der Logik 
sein. Aber auch eine Stackoverflpw kommt da gerne mal als Ursache in 
Frage, oder auch sehr beliebt für sowas sind fehlerhaft initalisierte 
Pointer. Nicht vergessen sollte man da auch nicht das Laufzeitverhalten 
wenn eine Varibale in Verschiedenne Scopes verwendet wird. Dabei muss 
sichergstellt werden das jeder Scope zur richtigen Zeit auf die Varibale 
zugreift.


Ebenfalls sollte man sich angewöhnen Zugriffe auf Arrays zu Prüfen.
Also zb wenn bei  array[index] = 5;  Das "index" eine Variable ist die 
berechnet wird. zb durch eine ForSchleife.   Passt hier der Index nicht 
wird schnell eine Ramadresse außerhalb des Arrays beschrieben.


Je größer und komplexer ein Programm wird, umso wichtiger ist es sich 
hier eine feste Struktur anzugewöhnen. Sonst wird das Programm schnell 
unübersichtlich, und kaum noch zu handhaben.
Extrem wichtig wird es wenn mehrere Programmierer an der gleichen 
Software arbeiten.


Es gibt da noch vieles mehr, dies waren nur ein paar Beispiele.

Was genau dein Lehrer sehen will, kann nur er dir selbst erklären.

von Mist (Gast)


Lesenswert?

If Schleife?

6, setzen!

von Yalu X. (yalu) (Moderator)


Lesenswert?

hallo schrieb:
> Wo ist der Unterschied zwischen ++sek und sek++ ?

Nicht zwischen ++sek und sek++, sondern zwischen ++sek und sek+1. Da ist 
ein sehr großer Unterschied: sek+1 ist ein ganz gewöhnlicher Ausdruck 
ohne Nebeneffekte. ++sek hat einen Nebeneffekt, nämlich den, dass der 
um 1 erhöhte Inhalt von sek wieder in sek zurückgeschrieben wird.

In der Anweisung
1
  sek = ++sek % 60;

wird der Variable sek zweimal ein Wert zugewiesen: Einmal durch ++ und 
einmal durch =. Nehmen wir an, sek hätte anfangs den Wert 59. Durch 
++sek wird sek der Wert 60 zugewiesen, durch sek=(59+1)%60 der Wert 0. 
Je nachdem, welche der beiden Zuweisungungen zuletzt erfolgt (die 
Reihenfolge darf sich der Compiler aussuchen), ist nach Ausführung der 
Anweisung entweder sek==60 oder sek==0. Solche mehrdeutigen Operationen 
werden im C-Standard als "undefined" bezeichnet.

Der GCC sagt dazu:
1
warning: operation on ‘sek’ may be undefined [-Wsequence-point]
2
   sek = ++sek % 60;
3
       ^

Das Ergebnis stimmt in diesem Fall zwar zufälligerweise, das ist aber 
nicht garantiert.

von Thomas E. (thomase)


Lesenswert?

Ralph schrieb:
> If schleife. Erstelle immer den ELSE Pfad mit , auch wenn er leer
Schwachsinn.
http://www.if-schleife.de/
1
ISR(TIMER1_COMPA_vect)
2
{
3
  sekunde++;
4
5
  if (sekunde == 60)
6
  {
7
    minute_d++;
8
    sekunde = 0;
9
    if (minute_d == 10)
10
    {
11
      minute_d = 0;
12
      minute_c++;
13
      if (minute_c == 6)
14
      {
15
        minute_c = 0;
16
        stunde_b++;
17
        if (stunde_b == 10)
18
        {
19
          stunde_b = 0;
20
          stunde_a++;
21
          if (stunde_a == 3)
22
          {
23
            stunde_a = 0;
24
          }
25
          else
26
          {
27
          }
28
        }
29
        else
30
        {
31
        }
32
      }
33
      else
34
      {
35
      }
36
    }
37
    else
38
    {
39
    }
40
  }
41
  else
42
  {
43
  }
44
}

Nicht zu fassen.
mfg.

von hallo (Gast)


Lesenswert?

Die Konsequenz ist ++ und -- nur als jeweils einzige Zuweisung in einem 
Ausdruck zu verwenden.
Das sieht nach einer Kinderkrankheit aus.

von Martin K. (maart)


Lesenswert?


von Thomas E. (thomase)


Lesenswert?


von Leerer (Gast)


Lesenswert?

> Die Konsequenz ist ++ und -- nur als jeweils einzige Zuweisung in einem
> Ausdruck zu verwenden.
Die Operatoren funktionieren auch ohne Zuweisung. Bei Zuweisungen muss 
der Programmierer nur noch mehr aufpassen und wissen was er tut.

Das Verhalten dieser Operatoren hängt auch vom Datentyp ab. Das kann man 
in der Literatur nachlesen.

von Martin K. (maart)


Lesenswert?

Thomas Eckmann schrieb:
> Und was ist jetzt dein Problem?

Tut mir leid, dein Code stand beim Verfassen meines Beitrages noch nicht 
da.
Es stand nur das einzelne "Schwachsinn" da. Hat sich erledigt.

von hallo (Gast)


Lesenswert?

Leerer schrieb:
>> Die Konsequenz ist ++ und -- nur als jeweils einzige Zuweisung
> in einem
>> Ausdruck zu verwenden.
> Die Operatoren funktionieren auch ohne Zuweisung. Bei Zuweisungen muss
> der Programmierer nur noch mehr aufpassen und wissen was er tut.
>
> Das Verhalten dieser Operatoren hängt auch vom Datentyp ab. Das kann man
> in der Literatur nachlesen.

Die Operatoren beinhalten (und sind damit) eine Zuweisung.
Sind hier nur noch Vollpfosten unterwegs?

Es gibt eine klare Rangfolge der Operatoren. Inc und dec sind höher 
angesiedelt als = und trotzdem muss man aufapssen? Also Kinderkrankheit.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ralph schrieb:
> Als Anhalt hier kann zb die Regeln von MISRA dienen.
>
> zb:
> If schleife. Erstelle immer den ELSE Pfad mit , auch wenn er leer
> bleibt.

Ich verkneife mir jetzt mal den Hinweis auf http://www.if-schleife.de/ 
;-)

Außerdem hast du die MISRA-Regeln falsch verstanden: Ein Else-Zweig ist 
nach MISRA am Ende einer If-ElseIf-Kette gefordert, aber nicht in einer 
gewöhnlichen If-Anweisung.

von Thomas E. (thomase)


Lesenswert?

hallo schrieb:
> Es gibt eine klare Rangfolge der Operatoren. Inc und dec sind höher
> angesiedelt als = und trotzdem muss man aufapssen? Also Kinderkrankheit.
Wahrscheinlich. C++ regt sich darüber jedenfalls nicht auf.

mfg.

von Leerer (Gast)


Lesenswert?

> Die Operatoren beinhalten (und sind damit) eine Zuweisung.
> Sind hier nur noch Vollpfosten unterwegs?
Niemand hat etwas anderes behauptet. Der Bezug lag auf "sek = ++sek % 
60;"

Es gibt aber immer Vollpfosten die es einfach falsch interpretieren 
wollen!

von hallo (Gast)


Lesenswert?

Leerer schrieb:
>> Die Operatoren beinhalten (und sind damit) eine Zuweisung.
>> Sind hier nur noch Vollpfosten unterwegs?
> Niemand hat etwas anderes behauptet. Der Bezug lag auf "sek = ++sek %
> 60;"
>
> Es gibt aber immer Vollpfosten die es einfach falsch interpretieren
> wollen!

Dann möge er den Sachverhalt und die Operatorreihenfolge an seiner 
gewählten Textpassage erklären. Wenn es ihm gelingt, nehme ich den 
Vollpfosten natürlich zurück. Also auf geht `s!

von Yalu X. (yalu) (Moderator)


Lesenswert?

hallo schrieb:
> Es gibt eine klare Rangfolge der Operatoren. Inc und dec sind höher
> angesiedelt als = und trotzdem muss man aufapssen?

Die Rangfolge der Operatoren regelt, in welcher Reihenfolge die 
einzelnen Operationen innerhalb eines Ausdrucks ausgewertet werden.

Sie regelt aber nicht, in welcher Reihenfolge mögliche Nebeneffekte 
ausgeführt werden. Hier lässt der C-Standard dem Compiler viele 
Freiheiten, da hier eine geschickt gewählte Reihenfolge oft deutlich 
effizienteren Programmcode ermöglicht.

Die Ausführungssequenz der Nebeneffekte wird durch so genannte 
Sequence-Points gesteuert:

  http://en.wikipedia.org/wiki/Sequence_point

Die oben diskutierte Anweisung
1
sek = ++sek % 60;

enthält keinen Sequence-Point (es gibt nur einen am Ende der Anweisung), 
somit kann der Compiler die beiden Zuweisungen an sek anordnen, wie er 
es für günstiger hält.

Ein ähnlich gearteter Fall, in dem die Problematik etwas deutlicher 
wird, ist die folgende (etwas konstruierte) Anweisung:
1
s = (s = 1) + (s = 2);

Darin sind die Zuweisungen s=1, s=2 und s=1+2 enthalten. Am Ende kann s 
also irgendeinen der Werte 1, 2 oder 3 haben.

Diese undefinierte Ausführungsreihenfolge der Seiteneffekte stellt in 
der Praxis kein Problem dar, da man Mehrfachzuweisungen an ein und 
dieselbe Variable innerhalb eines Ausdrucks allein schon aus 
Effizienzgründen sowieso vermeidet.

von Malte (Gast)


Lesenswert?

Prinzipiell versuche ich Seiteneffekte zu verhindern.
sek = ++sek % 60;

Ist imho nicht so schön. Da hab ich ja während der Berechnung immer 
einen Zwischenwert in der Variable, der nicht zu Ende ausgewertet ist. 
Es wird z.B. immer wieder mal ne 60 drin stehen, bevor der Modulo 
angewendet wird.
Spätestens wenn man es mit Nebenläufigkeit, aber auch mit Interrupts, zu 
tun hat, schiesst man sich damit sehr schnell in den Fuß.

Deshalb lieber
sek = (sek + 1) % 60;

Malte

von hallo (Gast)


Lesenswert?

Yalu X. schrieb:
> Ein ähnlich gearteter Fall, in dem die Problematik etwas deutlicher
> wird, ist die folgende (etwas konstruierte) Anweisung:
> s = (s = 1) + (s = 2);

Danke Yalu X.

von someone (Gast)


Lesenswert?

Meiner Meinung nach ist der Code in Ordnung. Ja, man kann ihn auch in 
ein paar Zeilen quetschen, aber das macht ihn nicht leichter 
verständlich und spart wahrscheinlich auch keinen Programmspeicher.
Ein sehr wichtiger Punkt wurde hier aber bisher übersehen, daher will 
ich kurz darauf eingehen: Alle Variablen, die in ISRs verwendet werden, 
sollten volatile deklariert werden. Warum? Der Compiler optimiert den 
Code. Wenn der Compiler nun sieht, dass sich eine Variable zwischen zwei 
Abfragen (oder zwischen der Deklaration mit Wert und einer Abfrage) 
nicht ändert, dann kann es vorkommen, dass der Compiler in die Abfrage 
das Ergebnis der vorherigen Abfrage oder eine Konstante einsetzt und die 
Abfrage dann wegoptimiert. Compiler sind ziemlich dumm und verstehen 
nicht, ob dein Interrupt in der Zwischenzeit den Wert deiner Variable 
geändert hat oder nicht, daher gehen sie davon aus, dass du ihnen schon 
sagst, wenn sie darauf achten sollen. Wenn du es nicht sagst, wird es 
ignoriert. Mit volatile sagst du nun dem Compiler, dass sich die 
Variable die ganze Zeit ändern kann. Der Compiler wird also nun jedes 
Mal die Variable prüfen, wenn es irgendwas damit zu tun gibt. Je nach 
Optimierungslevel kann es also durchaus auch ohne volatile klappen, aber 
darauf sollte man sich nicht verlassen.

von michael (Gast)


Lesenswert?

someone schrieb:
> Alle Variablen, die in ISRs verwendet werden,
> sollten volatile deklariert werden.

Ich denke das gilt jedoch nicht, wenn die Variable innerhalb der ISR 
deklariert wird. Dann kann es keine Seiteneffekte bezüglich dieser 
Variable geben, da weder das Hauptprogramm noch eine andere ISR auf die 
Variable zugreifen können. Außerdem wird die Variable bei jedem Eintritt 
in die ISR neu erzeugt.

Viele Grüße
Michael

von Fabian O. (xfr)


Lesenswert?

Der Hinweis auf volatile ist sehr gut. An der Stelle lässt sich nämlich 
der Code noch optimieren, leider auf Kosten der Lesbarkeit.

In der ursprünglichen Variante wird duch das volatile jedes Mal 
zwangsweise auf den Wert der Variable im Speicher zugegriffen (3-4 Mal), 
statt den Wert aus dem Prozessorregister weiterzuverwenden. Das kann man 
verhindern, indem man den Wert in der ISR in einer lokalen Variable 
(hier tmp) zwischenspeichert. Dann wird auf die Variablen im Speicher 
(sek, min0, ...) nur genau 2 Mal zugegriffen.

Allerdings macht es den C-Code (nicht den Maschinencode!) etwas länger 
und unübersichtlicher:
1
static volatile uint8_t sek;
2
static volatile uint8_t min0;
3
// ...
4
5
ISR(TIMER1_COMPA_vect)
6
{
7
  // Werte lokal verarbeiten wegen volatile
8
  uint8_t tmp;
9
  // Sekunden
10
  tmp = sek;
11
  if (tmp < 59) {
12
    sek = tmp + 1;
13
  } else {
14
    sek = 0;
15
    // Minuten
16
    tmp = min0;
17
    if (tmp < 9) {
18
      min0 = tmp + 1;
19
    } else {
20
      min0 = 0;
21
      // ...
22
    }
23
  }
24
}

von Fabian O. (xfr)


Lesenswert?

Wenn es dagegen auf Geschwindigkeit/Programmgröße nicht ankommt, sondern 
in erster Linie lesbar und verständlich sein soll, schlage ich diese 
Variante vor:
1
ISR(TIMER1_COMPA_vect)
2
{
3
  if (++sekunde == 60) {
4
    sekunde = 0;
5
    if (++minute_d == 10) {
6
      minute_d = 0;
7
      if (++minute_c == 6) {
8
        minute_c = 0;
9
        if (++stunde_b == 10) {
10
          stunde_b = 0;
11
          if (++stunde_a == 3) {
12
            stunde_a = 0;
13
          }
14
        }
15
      }
16
    }
17
  }
18
}
Die Verschachtelungstiefe dürfte zwar manchen Code-Richtlinien 
widersprechen, sie zeigt aber in diesem Fall ganz klar was passiert und 
ist durch die Kürze gut zu überblicken, daher imo vertretbar.

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

suche doch einfach mal im Netz nach einem Coding Standard. Der gibt sehr 
gut vor wie es auszusehen hat, globale Variablennamen haben immer Name_ 
, es uist fast alles geregelt und das sehr gut wie ich finde. Jeder kann 
es lesen. Ich programmiere nur so und vor allem alles in Englisch.

Gruss,
Christian

von Thomas E. (thomase)


Lesenswert?

michael schrieb:
> someone schrieb:
>> Alle Variablen, die in ISRs verwendet werden,
>> sollten volatile deklariert werden.
>
> Ich denke das gilt jedoch nicht, wenn die Variable innerhalb der ISR
> deklariert wird. Dann kann es keine Seiteneffekte bezüglich dieser
> Variable geben, da weder das Hauptprogramm noch eine andere ISR auf die
> Variable zugreifen können. Außerdem wird die Variable bei jedem Eintritt
> in die ISR neu erzeugt.
>
> Viele Grüße
> Michael

Ist aber äusserst sinnbefreit, wenn die Uhr jedesmal bei 0 wieder 
anfängt.
Das gleiche gilt, wenn andere Programmteile nicht darauf zugreifen 
können.
mfg.

von nicht "Gast" (Gast)


Lesenswert?

Moin,

gibt ja viele tolle Vorschläge hier.

Frag aber einfach mal deinen Lehrer, was er sich unter ordentlich 
vorstellt und was du an deinem Code besser machen kannst.

Dann muss er das mal beziffern und du weißt in Zukunft, was du tun must, 
um keinen Punktabzug zu erhalten.

Grüße,

von michael (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> michael schrieb:
>> someone schrieb:
>>> Alle Variablen, die in ISRs verwendet werden,
>>> sollten volatile deklariert werden.
>>
>> Ich denke das gilt jedoch nicht, wenn die Variable innerhalb der ISR
>> deklariert wird. Dann kann es keine Seiteneffekte bezüglich dieser
>> Variable geben, da weder das Hauptprogramm noch eine andere ISR auf die
>> Variable zugreifen können. Außerdem wird die Variable bei jedem Eintritt
>> in die ISR neu erzeugt.
>>
>> Viele Grüße
>> Michael
>
> Ist aber äusserst sinnbefreit, wenn die Uhr jedesmal bei 0 wieder
> anfängt.
> Das gleiche gilt, wenn andere Programmteile nicht darauf zugreifen
> können.
> mfg.

stimmt. Meine Anmerkung sollte sich auch nur auf "Alle variablen" 
beziehen, nicht speziell auf den Code des TO.

Viele Grüße
Michael

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.