mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik C programmieren wie die grossen Jungs


Autor: Oktoberfestbesucher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe mal meine eigene ultoa Routine programmiert siehe:
Beitrag "Zahl ausgeben mit sprintf frisst Resourcen"

Den Algorithmus habe ich mir selber ausgedacht,
ich wollte kein div(10), %10 oder /10 drin haben!
unsigned char i;
char Ziffer[10];
for(i=0;i <= 10;i++) Ziffer[i] = 0;

for(i=0;i < 32;i++)
{
  unsigned char Carry_flag;
  unsigned char n;
  if(Zahl & 0x80000000) Carry_flag = 1; else Carry_flag = 0;
  Zahl <<= 1;
  for(n=9;n <= 9;n--)
  {
    Ziffer[n] <<= 1;
    if(Carry_flag) Ziffer[n]++;
    if(Ziffer[n] >= 10)
    {
      Ziffer[n] -= 10;
      Carry_flag = 1;
    }
    else
    {
      Carry_flag = 0;
    }
  }
}
// Führende Nullen sind noch zu unterdrücken  ;-)
Ist getestet, funktioniert 1A
Was kann ich das stilistisch noch besser machen?
Wie geht das mit Pointern?
Bei mir irgendwie nicht, in dem Punkt verstehe ich mein C-Buch nicht...

Autor: Pete K. (pete77)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann lies den Rest des Buches auch noch einmal durch ...

Die erste For-Schleife ist schon fehlerhaft.

Autor: Zwie Blum (zwieblum)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mann, bist du pingelig! Ein off-by-one ist doch kein Fehler, das ist ein 
Feature, so was wie eine Kinderüberraschung für die grossen Jungs ;-)

Autor: Oktoberfestbesucher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Pete K. schrieb:
> Die erste For-Schleife ist schon fehlerhaft.

Bin mir nicht sicher, ob ich verstehe was du meinst.
Das Programm funktioniert so, ist mit einer manigfaltigkeit von Zahlen 
getestet!

Meinst du ich hätte das Array grösser machen sollen?!
char Ziffer[11];

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nö, die funktioniert nicht!
Die hinterläßt einen kaputten Stack.

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

Bewertung
0 lesenswert
nicht lesenswert
char Ziffer[10];
for(i=0;i <= 10;i++) Ziffer[i] = 0;



Dein Array umfasst 10 Elemente. Da das erste den Index 0 hat, hat das 
letzte daher welchen Index?

Die for-Schleife: Welche Werte für i generiert sie?

Auf welche Indexnummern in Ziffern wird daher zugegriffen?

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

Bewertung
0 lesenswert
nicht lesenswert
Oktoberfestbesucher schrieb:

> Das Programm funktioniert so

Aber nur wenn man nicht zu genau hinschaut :-)

Autor: Markus ---- (mrmccrash)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, es geht um die Abbruchbedingung der ersten for-Schleife. Dein 
Array ist 10 Stellen groß. was passiert, wenn du nun von 0 aufwärts nach 
10 zählst?

_.-=: MFG :=-._

Autor: Pete K. (pete77)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mich wundert, dass der Überlauf nicht zum Programmabbruch führt. Hat da 
der Compiler (welcher wird benutzt?) etwas wegoptimiert?

Die for(n) Schleife wird auch nur einmal durchlaufen -> überflüssig.

Außerdem fehlen die Kommentare. Das gehört zum Programmieren auch dazu 
:-)

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oktoberfestbesucher schrieb:
> Das Programm funktioniert so, ist mit einer manigfaltigkeit von Zahlen
> getestet!

Wichtige Programmier-Lektion:

"Programm verhält sich wie erwartet"
ist ganz und gar nicht das Gleiche wie
"Programm ist fehlerfrei"

Autor: jl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> for(i=0;i < 32;i++)
> {
>   unsigned char Carry_flag;
>   unsigned char n;


dafür hätte ich dich "verprügelt" würdest du sowas in meinem Projekt 
abgeben.
Deklarationen gehören bei mir immer an den Begin einer Funktion oder an 
den beginn eines C-Files. Auch wenns nach C&R überall elaubt ist suchst 
du dich bei Fehlern tot.

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

Bewertung
0 lesenswert
nicht lesenswert
Pete K. schrieb:
> Mich wundert, dass der Überlauf nicht zum Programmabbruch führt.

Hängt davon ab, wie die genaue Sitation am Stack bzw. im Speicher 
aussieht.

> Die for(n) Schleife wird auch nur einmal durchlaufen -> überflüssig.

Doch die wird ausgeführt.
Er benutzt einen Unterlauf als Abbruchkriterium.
Sie wird sogar ziemlich oft ausgeführt. Genau 288 mal.

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

Bewertung
0 lesenswert
nicht lesenswert
jl schrieb:
>> for(i=0;i < 32;i++)
>> {
>>   unsigned char Carry_flag;
>>   unsigned char n;
>
>
> dafür hätte ich dich "verprügelt" würdest du sowas in meinem Projekt
> abgeben.

Dann werden wir nie in einem gemeinsamen Projekt arbeiten.

> den beginn eines C-Files. Auch wenns nach C&R überall elaubt ist suchst
> du dich bei Fehlern tot.

Das genaue Gegenteil ist der Fall

Autor: Mano Wee (Firma: ---) (manow)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oktoberfestbesucher schrieb:
> ...
>
> ...
> 
> for(i=0;i < 32;i++)
> {
>   ...
>   if(Zahl & 0x80000000) Carry_flag = 1; else Carry_flag = 0;
>   Zahl <<= 1;
>  ...
> }
> ...
> 

Sehe ich das richtig, dass Zahl min. 32 Bit groß ist und das schiebst Du 
immer wieder mal nach links? - Das geht ja sicher total flott.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

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

Bewertung
0 lesenswert
nicht lesenswert
  if(Zahl & 0x80000000) Carry_flag = 1; else Carry_flag = 0;
Voll umständlich, das hier. Große machen das so:
  Carry_flag=(Zahl&0x80000000)?1:0;
Das spart Speicherplatz auf deinem Rechner... ;-)



> Deklarationen gehören bei mir ... an den beginn eines C-Files.
Ja, finde ich auch:
nur eine volatile globale Variable ist eine richtige Variable :-)
Und wozu braucht man eigentlich einen Stack?

Autor: Pink Shell (pinkshell)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar Miller schrieb:
  Carry_flag=(Zahl&0x80000000)?1:0;

Oder gleich so ?
Carry_flag=(Zahl&0x80000000);

Diese x?y:z stören imho die Lesbarkeit.

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

Bewertung
0 lesenswert
nicht lesenswert
Pink Shell schrieb:
> Lothar Miller schrieb:
>   Carry_flag=(Zahl&0x80000000)?1:0;
>
> Oder gleich so ?
> Carry_flag=(Zahl&0x80000000);

Das macht aber etwas anderes :-)

> Diese x?y:z stören imho die Lesbarkeit.

Aber es kann den Unterschied ausmachen zwischen funktioniert und 
funktioniert nicht :-)

Aber bitte, wenns dich stört:

Carry_flag = !!(Zahl&0x80000000);

Besser?

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jl schrieb:

> Deklarationen gehören bei mir immer an den Begin einer Funktion oder an
> den beginn eines C-Files. Auch wenns nach C&R überall elaubt ist suchst
> du dich bei Fehlern tot.

Nö.

Lokale Variablen schreibt man immer an den Anfang des kleinsten Blocks, 
in dem sie gültig sein müssen.

Der Compiler merkt zwar oft selber, wie lange eine Variable benötigt 
wird, aber die Lesbarkeit wird deutlich besser, wenn man das auch klar 
sieht und nicht erst 4 Seiten einer Funktion durchsuchen muß, ob die 
Variable woanders noch auftaucht.

Die Schreibweise "for( uint8_t i = ..."
benutze ich sehr gerne, damit jeder sofort weiß, "i" wird nach der 
Schleife nicht mehr benötigt.


Der Techniker sagt: Nur so genau, wie nötig.
Und der Programmierer sagt: Nur solange gültig, wie nötig.


Peter

Autor: Experte (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Diese Kombination ist auch sehr lustig:
  unsigned char n;
  ...
  for(n=9;n <= 9;n--)
  ...

Schon spannend, wie falsch man "programmieren" kann und es doch noch 
irgendwie funktioniert...

Aber solbald man die Optimierung im Compiler einschaltet und der 
Compiler "XYZ ist nicht definiert" aus dem Standard ernst nimmt, kracht 
es.

Autor: Christian H. (netzwanze) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Experte schrieb:
> Diese Kombination ist auch sehr lustig:

Wieso?
n läuft dabei doch von 9 bis 0 und die Schleife endet dann.
Nach Schleifenende ist n gleich 255 (falls "unsigned char" gleich 
"uint8_t" ist).

Oder sehe ich das jetzt etwas falsch?

Karl heinz Buchegger schrieb:
> Sie wird sogar ziemlich oft ausgeführt. Genau 288 mal.

Das verstehe ich jedenfalls nicht.

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

Bewertung
0 lesenswert
nicht lesenswert
Christian H. schrieb:

> Karl heinz Buchegger schrieb:
>> Sie wird sogar ziemlich oft ausgeführt. Genau 288 mal.
>
> Das verstehe ich jedenfalls nicht.


Die äussere Schleife 32 mal, bei jeder Iteration läuft die innere 
Schleife 9 mal. 32*9 = 288. D.h. der Schleifenrumpf der inneren Schleife 
wird 288 mal durchgehechelt.

Autor: Kenji N. (shaitan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> Nö.
>
> Lokale Variablen schreibt man immer an den Anfang des kleinsten Blocks,
> in dem sie gültig sein müssen.
>
> Der Compiler merkt zwar oft selber, wie lange eine Variable benötigt
> wird, aber die Lesbarkeit wird deutlich besser, wenn man das auch klar
> sieht und nicht erst 4 Seiten einer Funktion durchsuchen muß, ob die
> Variable woanders noch auftaucht.
>
> Die Schreibweise "for( uint8_t i = ..."
> benutze ich sehr gerne, damit jeder sofort weiß, "i" wird nach der
> Schleife nicht mehr benötigt.

Jupp sehe ich auch so. Wenn man Variablen nur in einem lokalen Scope 
braucht, dann sollten sie auch dort deklariert werden, aber dann auch am 
anfang und nicht irgendwo in der mitte. Dann weis man gleich das man die 
sich nicht die ganze Zeit merken muss, gleiches für die deklaration der 
Zählervariable der For-Schleife. Der Compiler ist vielleicht so 
intelligent und bekommt das hin, aber der Mensch ist kein Compiler...

Autor: Martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> for(i=0;i < 32;i++)
>> {
>>   unsigned char Carry_flag;
>>   unsigned char n;


>dafür hätte ich dich "verprügelt" würdest du sowas in meinem Projekt
>abgeben.
>Deklarationen gehören bei mir immer an den Begin einer Funktion oder an
>den beginn eines C-Files. Auch wenns nach C&R überall elaubt ist suchst
>du dich bei Fehlern tot.

ich könnte wetten du hast mit Fortran angefangen.
vielleicht dann dazwischen auf Basic umgestiegen :)

Man sucht sich höchstens dann tot, wenn man alle Variablen
überall sichtbar macht. Im schlimmsten Fall global. Information
hiding ist die Divise. Ich selbst verwende manchmal eine Variable
doppelt und dreifach im Körper einer Funktion um einfach die
Zwischenergebnisse ohne sinnvollen Namen festzuhalten. Dann
mache ich eher

int tmp;
if() {
tmp=...
}
...
if() {
tmp=...
}

statt

if() {
int tmp;
tmp=...
}
...
if() {
int tmp;
tmp=...
}


was man dann aber nicht unbeding machen sollte ist shadowing ala
int x;
if(bla) {
    double x;
    x=...
}

das verwirrt oft wiederum.

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian H. schrieb:
> Wieso?
> n läuft dabei doch von 9 bis 0 und die Schleife endet dann.
> Nach Schleifenende ist n gleich 255 (falls "unsigned char" gleich
> "uint8_t" ist).
>
> Oder sehe ich das jetzt etwas falsch?

Ja. Das Verhalten von
unsigned char n;
n = 0; n--;
ist undefiniert. Dass es mit einem bestimmten Compiler auf einer 
bestimmten Architektur immer 255 ergibt ist (aus Sicht von C) reiner 
Zufall.

Andreas

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

Bewertung
0 lesenswert
nicht lesenswert
Andreas Ferber schrieb:

>> Oder sehe ich das jetzt etwas falsch?
>
> Ja. Das Verhalten von
>
> unsigned char n;
> n = 0; n--;
> 
> ist undefiniert.

Ähm. Nein.
Woher hast du diese Erkentnis?

Gerade dieser Fall ist exakt definiert. Für signed Typen ist 
undefiniert, was passiert, aber für unsigned Typen ist festgelegt was 
passieren muss

> Dass es mit einem bestimmten Compiler auf einer
> bestimmten Architektur immer 255 ergibt ist (aus Sicht von C) reiner
> Zufall.

Der Zufall heißt C-Standard. Und darin ist festgelegt, dass unsigned 
Typen sich wie bei einem Rollover zu verhalten haben. Nach der 
kleinesten Zahl, geht es mit der größten weiter.

Autor: Christian H. (netzwanze) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Die äussere Schleife 32 mal,

Ok, ich hatte gedacht, dass nur die innere gemeint war.

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

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Der Zufall heißt C-Standard. Und darin ist festgelegt, dass unsigned
> Typen sich wie bei einem Rollover zu verhalten haben. Nach der
> kleinesten Zahl, geht es mit der größten weiter.

Funktioniert allerdings nur so lange, wie unsigned char identisch zu
uint8_t ist.  Wäre also ein typischer Anwendungsfall, gleich uint8_t
zu schreiben, damit es auf Maschinen, die sowas nicht haben, dann
einen Compilefehler gibt.

Allerdings würde ich mal behaupten, dass diese uint8_t-Überlauf-
Variante für eine Anzahl von Prozessoren eher eine Pessimierung
darstellt, und von guter Lesbarkeit (stand da nicht was von "wie
die großen Jungs"?) schweigen wir lieber.  Eine solche krückige
Konstruktion gehört zumindest in ihrer Wirkungsweise kommentiert,
wobei der Kommentar auch gleich noch beinhalten sollte (am besten
mit der Zahl eingesparter Taktzyklen belegt), warum man solch
eine Konstruktion gewählt hat.

Ob Implementierung einer Division durch 10 in Form einer fortlaufenden
Subtraktion wirklich irgendeinen Gewinn bringt gegenüber einer normalen
Division, wage ich auch zu bezweifeln.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Ob Implementierung einer Division durch 10 in Form einer fortlaufenden
> Subtraktion wirklich irgendeinen Gewinn bringt gegenüber einer normalen
> Division, wage ich auch zu bezweifeln.

Auf CPUs ohne Division ist die Subtraktionsmethode deutlich schneller.
Da eine Dezimalwandlung aber nur für den viel langsameren Menschen nötig 
ist, merkt man das nicht.


Diese Routine benutzt aber garnicht die Subtraktionsmethode, sondern die 
dezimale Multiplikation mit 2.
Das sind hier schon stattliche 320 Schleifendurchläufe, da könnte die 
Division mit Rest sogar schneller sein.


Peter

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

Bewertung
0 lesenswert
nicht lesenswert
>>>> Carry_flag = (Zahl&0x80000000)?1:0;
>>> Diese x?y:z stören imho die Lesbarkeit.
Karl heinz Buchegger schrieb:
>> Carry_flag = !!(Zahl&0x80000000);

Bitteschön, noch ein paar Varianten:
   uint32_t a,b,c,d,e;
    uint8_t z,y,x,w,v;

    z = (a&0x80000000)?1:0;

    y = (b&0x80000000)?!0:0;

    x = !((c&(1UL<<31))?0:!0);

    w = !!(d&0x80000000);

    v = !!((e>>24)&0x80);
Der AVR-GCC-Compiler macht aus allen Beispielen insgesamt das selbe, nur 
bei der letzten Variante wird das Ergebnis nochmal verundet:
 lds  r24, 0x0079
 lds  r25, 0x007A
 lds  r26, 0x007B
 lds  r27, 0x007C
 eor  r24, r24
 sbrc  r27, 7
 inc  r24
 eor  r25, r25
 eor  r26, r26
 eor  r27, r27
 andi  r24, 0x01  ; nur bei der letzten Variante
 sts  0x006F, r24

Leider muß ich zugeben, dass die Variante mit der if-Abfrage am 
effizientesten und schnellsten implementiert wird, weil nicht der ganze 
long-Wert manipuliert wird  :-(
    if (f&0x80000000) u=1; 
    else              u=0;
Das ergibt:
 lds  r24, 0x0065
 lds  r25, 0x0066
 lds  r26, 0x0067
 lds  r27, 0x0068
 sbrs  r27, 7
 rjmp  .+8        ; 0x162 <main+0xbe>
 ldi  r24, 0x01  ; 1
 sts  0x0081, r24
 rjmp  .+4        ; 0x166 <main+0xc2>
 sts  0x0081, r1

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

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:

> Allerdings würde ich mal behaupten, dass diese uint8_t-Überlauf-
> Variante für eine Anzahl von Prozessoren eher eine Pessimierung
> darstellt, und von guter Lesbarkeit (stand da nicht was von "wie
> die großen Jungs"?) schweigen wir lieber.

Absolut ACK

Die "großen Jungs" achten lieber auf Lesbarkeit anstatt auf den letzten 
Taktzyklus :-)

> Ob Implementierung einer Division durch 10 in Form einer fortlaufenden
> Subtraktion wirklich irgendeinen Gewinn bringt gegenüber einer normalen
> Division, wage ich auch zu bezweifeln.

Das müsste man genauer durchleuchten.
Um ehrlich zu sein, habe ich diese Idee so noch nie irgendwo gesehen. 
Wenn es tatsächlich um das Vermeiden von Divisionen geht, würde ich das 
so machen, wie es im Ass Tutorial überall gemacht wird: Subtraktion von 
10000, 1000, 100, 10 und mitzählen. 32 Bit Divisionen sind schon nicht 
ohne, wenn man sie komplett in Software machen muss.

Die andere Seite der Medaille sieht so aus:
Wo wird denn so eine Umwandlung typischerweise gemacht?
Ich glaube ich lehne mich hier nicht zuweit aus dem Fenster, wenn ich 
sage: In einem Prozentsatz von ungefähr 98% (sag niemals 100%, man kann 
dich darauf festnageln), fällt diese Operation vor einer I/O Operation 
an. Und damit ist sie in den wenigsten Fällen wirklich zeitkritisch.

Aber ich wollte den TO nicht frustrieren. Denn immerhin: Er hat sich 
etwas überlegt, er hat es implementiert (von dem Schnitzer abgesehen) 
und das finde ich gut. Weiter so!

Autor: Thomas Burkhart (escamoteur)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du wolltest das doch mal mit Pointern haben, oder nicht unda dazu noch 
optimiert.
unsigned char i;
char Ziffer[10];
for(i=0;i <= 10;i++) Ziffer[i] = 0;
unsigned char i;
char Ziffer[10];
char* p = &Ziffer[0];
for(i=10;i != 0;i--)
{
   *p++= 0;
}

Überprüfung mit 0 ist schneller als <=10. Bei *p++  muss nicht jedesmal 
die Adresse Ziffer[i] neu berechnet werden, da das konkret bedeutet:

*(&Ziffer[0] + i * sizeof(i))

je nach Prozessor kann *p++ in einem einzigen Befehl abgearbeitet 
werden.

Gruß
Tom
P.S.: Hoffe ich hab da jetzt nicht daneben gehaunen

Autor: jl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Martin schrieb:
> ich könnte wetten du hast mit Fortran angefangen.
>
> vielleicht dann dazwischen auf Basic umgestiegen :)

Ne Fortran nicht, aber 87 mit Basic und Assembler auf einem Z80.
Dann C für uC etwas Pascal/Delphi für die PC Seite.

Von Pascal hab ich etwas für mich mitgenommen wie ich etwas 
strukturierter in C organisieren kann und C-Files als Objekte betrachte. 
Zur lesbarkeit gibt es bei mir am Fileanfang die Deklarationen der 
globalen Variablen, und alle variablen die nur in einer Funktion lokal 
benutzt werden am Funktionsanfang. Da eine Funktion sowieso nicht über 
1000Zeilen gehen soll braucht es nur einen kurzen Blick an den Anfang 
der Funktion.

Gerade bei verteilter Entwicklung und extremer Fluktuation der 
Entwickeler ist eine absolute simple Struktur von Nöten. Da dann 
manchmal auch noch unerfahrene ins Projekt reinkommen aber eine 
Anpassung erfolgen soll werden damit kleine Denkfehler vermindert.

Ebenso verfluche ich den Konstrukt a ? b : c, Selektive in einezelnen 
Funktionen wenns klar ist und eindeutig ist ist es OK, aber was da 
manchmal in geschachtelten Makros zusammengebastelt wird (das schlimmste 
waren bei mir 7 ? Konstrukte über verschiedenste Makros ineinander 
gesetzt) ist schon extrem Fehleranfällig.

Aber das ist hier keine Schönschreibdiskussion.



JL

Autor: Stefan Wimmer (wswbln)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"bei verteilter Entwicklung und extremer Fluktuation der
Entwickeler" würde ich mir eh' überlegen, ob C die richtige 
Programmiersprache ist oder nicht eine mit strengeren (Interface-)Regeln 
wie Modula oder Ada vielleicht besser wäre. Aber die Tools dürfen ja 
immer nichts kosten (so gesehen die Crux von GCC). Dann reicht es auch 
oft nicht mal für statische Analysetools, so deren Existenz im 
"Management" überhaupt bekannt ist...

Autor: Rene K. (draconix)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oktoberfestbesucher schrieb:
>
for(n=9;n <= 9;n--)...

Verstehe ich nicht?! Das dürfte doch genau einmal durchlaufen...
Direkt nach dem ersten Aufruf wird N kleiner gleich 9.. somit dürfte die 
Schleife beendet sein?!

Autor: Thomas Burkhart (escamoteur)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ähm, die Bedingung sagt "solange n<= als9 ist soll die Schleife 
ausgeführt werden.!

Autor: Rene K. (draconix)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thomas Burkhart schrieb:
> Ähm, die Bedingung sagt "solange n<= als9 ist soll die Schleife
> ausgeführt werden.!

Achso... ja... meine Güte so verdreht hab ichs noch nie gesehen o.O

Autor: Hannes Lux (hannes)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rene K. schrieb:
> Oktoberfestbesucher schrieb:
>>
for(n=9;n <= 9;n--)...
>
> Verstehe ich nicht?! Das dürfte doch genau einmal durchlaufen...
> Direkt nach dem ersten Aufruf wird N kleiner gleich 9.. somit dürfte die
> Schleife beendet sein?!

Schon vor dem ersten Durchlauf ist n kleinergleich 9, nämlich 9, also 
gleich 9. Demnach ist die Logik wohl andersherum (while <-> until).

;-)

...

Autor: Markus Müller (mmvisual)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die GROSSEN Jungs machen GROSSE Programme, die passen nur in GROSSE CPUs 
mit ARM-32-Bit Architektur oder ähnlich leistungsfähigen Typen mit 
GROSSEM Flash Speicher.
Damit erübrigt sich das Problem mit Division automatisch, weil die CPUs 
das alle samt gut können.

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
>> Ja. Das Verhalten von
>>
>> unsigned char n;
>> n = 0; n--;
>> 
>> ist undefiniert.
> Ähm. Nein.
> Woher hast du diese Erkentnis?

Yepp, my fault. Das im Hinterkopf verankerten "Vorsicht bei Overflow" zu 
stark verschärft. IIRC hatte der GCC da in letzter Zeit auch ein paar 
Optimizer-Bugs rund um das Overflow-Verhalten, so dass er 
sicherheitsrelevante Range-Checks wegoptimiert hat.

Andreas

Autor: Oktoberfestbesucher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bin nach den Osterferien noch mal drübergegangen:
// unsigned long int Zahl an einer bestimmten Position auf LCD ausgeben
void LCD_Anzeige_Zahl(unsigned char pos,unsigned long int Zahl)
{
  char Ziffer[10]; // Array mit Ziffer[0] bis Ziffer[9]
  char* P = &Ziffer[0]; // Pointer auf Ziffer[0]

  LCD_Instruction(pos); // Zeichen-position an LCD

  for(unsigned char i = 10;i;i--)  *P++ = 0; // Array initialisieren

  for(unsigned char i = 32;i;i--) // unsigned long int hat 32 Bit
  {
    P = &Ziffer[9];
    unsigned char Carry_flag = (Zahl & 0x80000000) ? 1 : 0;// Test MSB
    Zahl <<= 1;

    for(unsigned char n = 10;n;n--)
    {
      *P <<= 1;
      if(Carry_flag)  (*P)++;
      if(*P >= 10)
      {
        *P -= 10;
        Carry_flag = 1;
      }
      else Carry_flag = 0;
      P--; // Zeiger auf nächste Ziffer
    }
  }

  // führende Nullen als Leerzeichen ausgeben
  unsigned char leading_zero_flag = 0;
  P = &Ziffer[0];
  for (unsigned char i = 10;i;i--) 
  {
    if(leading_zero_flag || *P || i == 1) 
      {
        LCD_Zeichen(*P + '0'); // Ausgabe Ziffer + ASCII '0'
        leading_zero_flag = 1;
      }
      else LCD_Zeichen(' ');
    P++;
  }
}
So besser?

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

Bewertung
0 lesenswert
nicht lesenswert
Du entwickelst für meinen Geschmack da eine Vorliebe für SChleifen, die 
von einem Endwert runterzählen, obwohl sie das gar nicht müssten.

Was spricht gegen
  for(unsigned char i = 0; i < 10; ++i )
    *P++ = 0; // Array initialisieren

Jeder C-Programmierer weltweit erkennt so ein Konstrukt sofort als 
Schleife, die 10-mal durchlaufen wird. Bei jeglicher Array-Arbeit kommt 
so etwas vor, es ist etwas woran sich alle gewöhnt haben und das sie aus 
20 Meter Entfernung, direkt nach dem Aufstehen mit dem linken Fuss mit 
verschlafenen Augen noch vor dem ersten Kaffee sofort wiedererkennen.

Bei
  for(unsigned char i = 10;i;i--)  *P++ = 0; // Array initialisieren

müssen die meisten (inklusive mir) wahrscheinlich erst mal eine 
Gedenkminute einlegen, um sich davon zu überzeugen, dass das auch nur 
eine Zählschleife ist, die 10 mal ausgeführt wird.

Autor: Oktoberfestbesucher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> eine Vorliebe für SChleifen, die
> von einem Endwert runterzählen, obwohl sie das gar nicht müssten.

Mache ich schon lange so, kommt von einer Vorliebe für 8051-Assembler.
Dort sieht das z.B. so aus
    PUSH   Loopcounter
    MOV    Loopcounter,#10 'Diese Schleife wird 10 mal durchlaufen
my_weird_loop:




    DJNZ   Loopcounter,my_weird_loop
    POP    Loopcounter
der Test ob die Schleife zuende ist ist hier sehr einfach.

Ich bin nicht beratungsresistent => also werde ich es in Zukunft so 
machen wie du es schreibst.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Bei  for(unsigned char i = 10;i;i--)  *P++ = 0; // Array initialisieren
>
> müssen die meisten (inklusive mir) wahrscheinlich erst mal eine
> Gedenkminute einlegen,

Dann gehöre ich nicht zu Deinen "meisten".
Ich finde nämlich diese Schreibweise deutlich besser zu lesen.
Bei Deiner Schreibweise kann man leicht reinfallen, wenn da i <= 10; 
steht, läuft sie 11 mal. Oder wenn sie bei 1 startet, nur 9-mal.
Ich hab daher so schon mehrmals falsche Schleifenzahlen gehabt, bis ich 
die schönere Schleife entdeckte.

Außerdem erzeugt das i-- den kürzeren Code, da der Vergleich gespart 
wird.
Beim 8051 gibts dafür sogar den speziellen 3-fach Befehl DJNZ 
(runterzählen, testen, springen), der auch keine Flags zerstört.


Peter

Autor: Walter Tarpan (nicolas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Unter Matlab kann ich mir durch das runterzählen in Schleifen oft das 
reservieren von Arrays sparen :-).

Viele Grüße
Nicolas

Autor: moo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
// for(unsigned char i = 10;i;i--)  *P++ = 0;

ich schreib's sehr oft so:

i=10;
while(i--)
{
   *p++ = 0;
}

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manchmal wird die Schleifenzahl auch als Paramter übergeben, dann kann 
man diesen gleich runterzählen und muß keine extra Hochzählvariable 
verschwenden.


Peter

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

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger schrieb:
> Karl heinz Buchegger schrieb:
>> Bei  for(unsigned char i = 10;i;i--)  *P++ = 0; // Array initialisieren
>>
>> müssen die meisten (inklusive mir) wahrscheinlich erst mal eine
>> Gedenkminute einlegen,
>
> Dann gehöre ich nicht zu Deinen "meisten".

Mag sein.
Ich hab in all den Jahren schon viele Fremdlibraries studiert. Derartige 
aufwärts for-Schleifen gibt es zu Millionen. Aber anders rum, hmm, da 
brauch ich noch nicht mal alle 10 Finger um diejenigen der letzten 20 
Jahre zu zählen :-)

> Ich finde nämlich diese Schreibweise deutlich besser zu lesen.
> Bei Deiner Schreibweise kann man leicht reinfallen, wenn da i <= 10;
> steht, läuft sie 11 mal.

Die Kriterien sind einfach
  Startwert 0
  Vergleich: kleiner
  einfaches Inkrement
  und natürlich überall dieselbe Variable.

dann ist es eine Zählschleife die genau so oft zählt, wie es in der 
Abbruchbedingung steht.

> Außerdem erzeugt das i-- den kürzeren Code, da der Vergleich gespart
> wird.
> Beim 8051 gibts dafür sogar den speziellen 3-fach Befehl DJNZ
> (runterzählen, testen, springen), der auch keine Flags zerstört.

Das überlass ich dem Compiler :-)
Im Ernst: Einige Optimizer, bei denen ich mir das angesehen habe, 
implementieren derartige Schleifen auch durch runterzählen, wenn die 
Schleifenvariable im Rumpf nicht benutzt wird. Genauso wie ein 
ordentlicher Compiler

   for( i = 0; i < 10; ++i )
     Ziffer[i] = '\0'

das selbsttätig auf Pointerzugriff umstellt :-)

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

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Ich hab in all den Jahren schon viele Fremdlibraries studiert. Derartige
> aufwärts for-Schleifen gibt es zu Millionen. Aber anders rum, hmm, da
> brauch ich noch nicht mal alle 10 Finger um diejenigen der letzten 20
> Jahre zu zählen :-)

Kommt sicher daher, dass sie halt im K&R auch immer in dieser Form
geschrieben standen.  Mir geht's hier so wie dir, und wenn man eine
Weile C programmiert, kommt man eigentlich nie wieder auf die Idee,
die Schleife bei 1 anfangen zu lassen oder <= als Endbegrenzer zu
benutzen...

>> Beim 8051 gibts dafür sogar den speziellen 3-fach Befehl DJNZ
>> (runterzählen, testen, springen), der auch keine Flags zerstört.

> Das überlass ich dem Compiler :-)

Du vergisst, dass Peter einen schlechten Compiler gewöhnt ist,
bei dem man als Programmierer noch voroptimieren musste. ;-)

> Im Ernst: Einige Optimizer, bei denen ich mir das angesehen habe,
> implementieren derartige Schleifen auch durch runterzählen, wenn die
> Schleifenvariable im Rumpf nicht benutzt wird.
#include <stdint.h>

uint16_t
arraysum(uint8_t i, const uint16_t * restrict ary)
{
        uint16_t sum = 0;
#ifdef DOWN
        while (i-- != 0)
#else
        for (uint8_t j = 0; j < i; j++)
#endif
                sum += *ary++;

        return sum;
}

(mit -std=c99 compilieren, mal mit, mal ohne -DDOWN)

wird vom AVR-GCC zu zwar nicht identischem aber praktisch gleichem
Code compiliert, der sich nur leicht in der Anordnung der Assembler-
anweisungen unterscheidet.

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

Bewertung
0 lesenswert
nicht lesenswert
gcc entschliesst sich bei
int main()
{
  for( unsigned char i = 0; i < 10; ++i )
    Ziffer[i] = '\0';

  foo( Ziffer );
}

das i zu eliminieren und das ganze mittels Z-Pointer und Abfrage auf das 
Ende des Speicherbereichs zu implementieren
int main()
{
  46:  e0 e6         ldi  r30, 0x60  ; 96
  48:  f0 e0         ldi  r31, 0x00  ; 0
  for( unsigned char i = 0; i < 10; ++i )
    Ziffer[i] = '\0';
  4a:  11 92         st  Z+, r1
{
}

int main()
{
  for( unsigned char i = 0; i < 10; ++i )
  4c:  80 e0         ldi  r24, 0x00  ; 0
  4e:  ea 36         cpi  r30, 0x6A  ; 106
  50:  f8 07         cpc  r31, r24
  52:  d9 f7         brne  .-10       ; 0x4a <main+0x4>
    Ziffer[i] = '\0';



Interessant, was er aus
int main()
{
  for( unsigned char i = 10; i; --i )
    Ziffer[i] = '\0';

macht:
int main()
{
  46:  ea e6         ldi  r30, 0x6A  ; 106
  48:  f0 e0         ldi  r31, 0x00  ; 0
  for( unsigned char i = 10; i; --i )
    Ziffer[i] = '\0';
  4a:  10 82         st  Z, r1
  4c:  31 97         sbiw  r30, 0x01  ; 1
{
}

int main()
{
  for( unsigned char i = 10; i; --i )
  4e:  80 e0         ldi  r24, 0x00  ; 0
  50:  e0 36         cpi  r30, 0x60  ; 96
  52:  f8 07         cpc  r31, r24
  54:  d1 f7         brne  .-12       ; 0x4a <main+0x4>
    Ziffer[i] = '\0';

Die Abfrage auf das Schleifenede durch Adressvergleich ist geblieben, 
aber den  st -Z, r1 dürfte er nicht kennen.

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

Bewertung
0 lesenswert
nicht lesenswert
Wohingegen er in
int main()
{
  char* P = &Ziffer[0]; 

  for(unsigned char i = 10;i;i--)  *P++ = 0;
die Absicht offenbar erkannt hat
int main()
{
  46:  e0 e6         ldi  r30, 0x60  ; 96
  48:  f0 e0         ldi  r31, 0x00  ; 0
  char* P = &Ziffer[0]; 

  for(unsigned char i = 10;i;i--)  *P++ = 0;
  4a:  11 92         st  Z+, r1
  4c:  80 e0         ldi  r24, 0x00  ; 0
  4e:  ea 36         cpi  r30, 0x6A  ; 106
  50:  f8 07         cpc  r31, r24
  52:  d9 f7         brne  .-10       ; 0x4a <main+0x4>

identischer Code zum Fall
int main()
{
  for( unsigned char i = 0; i < 10; ++i )
    Ziffer[i] = '\0';

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

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Interessant, was er aus
>
> int main()
> {
>   for( unsigned char i = 10; i; --i )
>     Ziffer[i] = '\0';
> 
>
> macht:

Ganz abgesehen davon, dass ich da einen schönen Bock geschossen habe. 
Array access out of bounds :-)

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

Bewertung
0 lesenswert
nicht lesenswert
Man sollte als geübter C-Programmierer halt keine Schleifen schreiben,
die rückwärts zählen. :-)

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich meine auch irgendwo mal gelesen zu haben das man mit unsigned 
int/char sonstwas komisches dem Compiler keinen Gefallen tut und 
statdessen einfach int für den Schleifenzähler verwenden sollte (der 
Optimizer kann sich dann schon das beste raus suchen für den 
Wertbereich).

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.