Forum: Mikrocontroller und Digitale Elektronik 8051er Ausgabeproblem


von Marcel W. (cranke)


Lesenswert?

Hallo mir ist aufgefallen, dass zwei fast gleiche Befehle zwei völlig 
unterschiedliche Ergebnisse liefern nämlich folgendes:

printf("\n\nOhne Berechnung: %ld",100000);
printf("\nDurch Berechnung: %ld",(100*1000) );


Ohne die Berechnung wird mir dadurch 100000 angezeigt,
jedoch als Produkt aus 100 und 1000 erhalte ich -2036300128.

Weiß jemand wie das kann?


Gruß
Marcel

von Peter D. (peda)


Lesenswert?

Marcel W. schrieb:
> Weiß jemand wie das kann?

Ja, jedes C-Buch (Zahlenformate).


Peter

von Georg G. (df2au)


Lesenswert?

Peter Dannegger schrieb:
> Ja, jedes C-Buch (Zahlenformate).

Wenn es denn so einfach wäre :-)


Ohne Berechnung: 100000
Durch Berechnung: 100000
"d:\temp\lcc\test.exe"
Return code 25
Execution time 0.016 seconds
Press any key to continue...

Es hängt schon vom Compiler ab...

von Karl H. (kbuchegg)


Lesenswert?

Georg G. schrieb:
> Peter Dannegger schrieb:
>> Ja, jedes C-Buch (Zahlenformate).
>
> Wenn es denn so einfach wäre :-)

Ist es.
Auch Zahlen haben einen Datentyp.

100 ist ein int
1000 ist ein int

also hat das Ergebnis von 100*1000 welchen Datentyp?
Und wie passt der zu %ld?

Interessanter wird es bei 100000.
Denn da ist tatsächlich erst mal nicht klar, ob das ein int oder ein 
long ist. Das hängt dann tatsächlich vom Compiler (bzw. der 
darunterliegenden Hardware) ab.
Aber das sind Basisdinge, die man als C-Programmierer sowieso immer 
wissen muss: wie groß ist ein int und über welchen Zahlenbereich 
erstreckt er sich?

von Marcel W. (cranke)


Lesenswert?

Karl Heinz Buchegger schrieb:
> also hat das Ergebnis von 100*1000 welchen Datentyp?
> Und wie passt der zu %ld?

%ld habe ich gewählt da %d ja nur einen Bereich von -32768 ... +32767 
bzw %u von 0 bis +65535 hat.

Und als Compiler habe ich den vom Wickenhäuser.

von Karl H. (kbuchegg)


Lesenswert?

Marcel W. schrieb:
> Karl Heinz Buchegger schrieb:
>> also hat das Ergebnis von 100*1000 welchen Datentyp?
>> Und wie passt der zu %ld?
>
> %ld habe ich gewählt da %d ja nur einen Bereich von -32768 ... +32767
> bzw %u von 0 bis +65535 hat.

Du kannst daber nicht einfach irgendein Ausgabeformat wählen!
Das muss schon zu dem passen, was du der Funktion als Argument gibst!

nicht %d hat einen Ausgabebereich von -32768..+32767
sondern ein int kann nur Werte in diesem Bereich annehmen.

Daher nochmal die Frage:
100   ist ein int
1000  ist ein int

Welchen Datentyp hat daher das Ergebnis von 100*1000?
(und wie groß kann es daher in Zahlenform maximal werden?)

Die Frage und deren Beantwortung ist WICHTIG!
Denn erst dann hast du eines der wesentlichen fundamentalen 
Zusammenhänge in der Analyse von arithmetischen Ausdrücken entwickelt, 
wenn du diese Frage korrekt beantworten kannst!

von Georg G. (df2au)


Lesenswert?

Es gibt Gerüchte, wonach der Typ "Integer" nicht auf allen Maschinen 16 
Bit breit ist. Bei manchen Compilern ist er 32 Bit breit. Damit passt 
100000 auch in ein Integer und den kleine Fehler mit dem "l oder nicht 
l" verzeiht der Compiler, vielleicht sogar ohne Warnung (die der gute 
Programmierer ohnehin abstellt, denn er weiß alles besser).

von Karl H. (kbuchegg)


Lesenswert?

Bitte Georg.


Wir reden hier von einem 8051, einem 8-Bit µC.
Es ist nicht zielführend, wenn du den Fragesteller jetzt auch noch mit 
Spitzfindigkeiten zusätzlich verwirrst.

von Marcel W. (cranke)


Lesenswert?

Karl Heinz Buchegger schrieb:

> 100   ist ein int
> 1000  ist ein int
>
> Welchen Datentyp hat daher das Ergebnis von 100*1000?
> (und wie groß kann es daher in Zahlenform maximal werden?)


Demnach würde ich sagen ist das Ergebnis auch ein int und kann maximal 
+65535 annehmen?

von Karl H. (kbuchegg)


Lesenswert?

Marcel W. schrieb:
> Karl Heinz Buchegger schrieb:
>
>> 100   ist ein int
>> 1000  ist ein int
>>
>> Welchen Datentyp hat daher das Ergebnis von 100*1000?
>> (und wie groß kann es daher in Zahlenform maximal werden?)
>
>
> Demnach würde ich sagen ist das Ergebnis auch ein int und kann maximal

Bingo!

> +65535 annehmen?

Aber: dein Ergebnis wird nicht 32768 sein, sondern da passiert ein 
Überlauf. D.h. da kommt irgendwas raus. Aber es ist ein int!

Und daher muss die Formatieranweisung in printf auch ein %d sein. Denn 
%d ist für einen int.

  printf("\nDurch Berechnung: %d",(100*1000) );

Damit passen Berechnung und Formatieranweisung erst mal in den 
Datentypen zusammen.
Aber: wir wissen, dass 100*1000 als int-Ergebnis nicht korrekt sein 
wird, weil das Ergebnis überläuft. 100000 ist nun mal zu groß für einen 
16-Bit int.
Also muss man dafür sorgen, dass die Berechnung eben nicht als int 
Berechnung gemacht wird, sondern als long Berechnung. Denn ein (32-Bit) 
long kann dieses Ergebnis aufnehmen.

Und wie machen wir das?

von klausr (Gast)


Lesenswert?

Ordentliche Compiler meckern zum einen gleich (s.u.) und machen aus 
(100*1000) eh gleich die Kostante 100000.

klaus@LittlX:~/src$ cat berechnung.c
int main(void)
{
  printf("\n\nOhne Berechnung: %ld",100000);
  printf("\nDurch Berechnung: %ld\n",(100*1000) );
}
klaus@LittlX:~/src$ gcc berechnung.c
berechnung.c: In Funktion »main«:
berechnung.c:3:3: Warnung: Unverträgliche implizite Deklaration der 
eingebauten Funktion »printf« [standardmäßig aktiviert]
berechnung.c:3:3: Warnung: Format »%ld« erwartet Argumenttyp »long int«, 
aber Argument 2 hat Typ »int« [-Wformat]
berechnung.c:4:3: Warnung: Format »%ld« erwartet Argumenttyp »long int«, 
aber Argument 2 hat Typ »int« [-Wformat]
klaus@LittlX:~/src$ ./a.out


Ohne Berechnung: 100000
Durch Berechnung: 100000
klaus@LittlX:~/src$ gcc berechnung.c -S
[...]
klaus@LittlX:~/src$ cat berechnung.s
  .file  "berechnung.c"
  .section  .rodata
.LC0:
  .string  "\n\nOhne Berechnung: %ld"
.LC1:
  .string  "\nDurch Berechnung: %ld\n"
  .text
  .globl  main
  .type  main, @function
main:
.LFB0:
  .cfi_startproc
  pushl  %ebp
  .cfi_def_cfa_offset 8
  .cfi_offset 5, -8
  movl  %esp, %ebp
  .cfi_def_cfa_register 5
  andl  $-16, %esp
  subl  $16, %esp
  movl  $100000, 4(%esp)
  movl  $.LC0, (%esp)
  call  printf
  movl  $100000, 4(%esp)
  movl  $.LC1, (%esp)
  call  printf
  leave
  .cfi_restore 5
  .cfi_def_cfa 4, 4
  ret
  .cfi_endproc
.LFE0:
  .size  main, .-main
  .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
  .section  .note.GNU-stack,"",@progbits
klaus@LittlX:~/src$

[Interessanter Weise meckert der arm-gcc nicht!]

klaus@LittlX:~/src$ arm-none-eabi-gcc berechnung.c -S
berechnung.c: In function 'main':
berechnung.c:3:3: warning: incompatible implicit declaration of built-in 
function 'printf' [enabled by default]
klaus@LittlX:~/src$ cat berechnung.s
  .cpu arm7tdmi
  .fpu softvfp
  .eabi_attribute 20, 1
  .eabi_attribute 21, 1
  .eabi_attribute 23, 3
  .eabi_attribute 24, 1
  .eabi_attribute 25, 1
  .eabi_attribute 26, 1
  .eabi_attribute 30, 6
  .eabi_attribute 34, 0
  .eabi_attribute 18, 4
  .file  "berechnung.c"
  .section  .rodata
  .align  2
.LC0:
  .ascii  "\012\012Ohne Berechnung: %ld\000"
  .align  2
.LC1:
  .ascii  "\012Durch Berechnung: %ld\012\000"
  .text
  .align  2
  .global  main
  .type  main, %function
main:
  @ Function supports interworking.
  @ args = 0, pretend = 0, frame = 0
  @ frame_needed = 1, uses_anonymous_args = 0
  stmfd  sp!, {fp, lr}
  add  fp, sp, #4
  ldr  r0, .L2
  ldr  r1, .L2+4
  bl  printf
  ldr  r0, .L2+8
  ldr  r1, .L2+4
  bl  printf
  mov  r0, r3
  sub  sp, fp, #4
  ldmfd  sp!, {fp, lr}
  bx  lr
.L3:
  .align  2
.L2:
  .word  .LC0
  .word  100000
  .word  .LC1
  .size  main, .-main
  .ident  "GCC: (GNU Tools for ARM Embedded Processors) 4.6.2 20120613 
(release) [ARM/embedded-4_6-branch revision 188521]"
klaus@LittlX:~/src$

von Karl H. (kbuchegg)


Lesenswert?

Wisst ihr was.
Erklärt ihr ihm doch die Sache.

von Karl H. (kbuchegg)


Lesenswert?

klausr schrieb:
> Ordentliche Compiler meckern zum einen gleich (s.u.)

das ist ok.

> und machen aus
> (100*1000) eh gleich die Kostante 100000.

Wenn SEIN Compiler das macht, dann ist er fehlerhaft

von klausr (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn SEIN Compiler das macht, dann ist er fehlerhaft

Ja... ich hatte das 8051 im Titel überlesen!

von Marcel W. (cranke)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Also muss man dafür sorgen, dass die Berechnung eben nicht als int
> Berechnung gemacht wird, sondern als long Berechnung. Denn ein (32-Bit)
> long kann dieses Ergebnis aufnehmen.
>
> Und wie machen wir das?

Ich bin davon ausgegangen, dass so eine Art Autocast vorgenommen werden 
würde :D


aber da dies anscheinend nicht der Fall ist muss die Lösung lauten:

printf("\n\nOhne Berechnung: %ld",100000);
printf("\nDurch Berechnung: %ld",(long)100*(long)1000 );

Vielen dank, damit wäre mein Problem gelöst.

Gruß
Marcel

von Karl H. (kbuchegg)


Lesenswert?

Marcel W. schrieb:
> Karl Heinz Buchegger schrieb:
>> Also muss man dafür sorgen, dass die Berechnung eben nicht als int
>> Berechnung gemacht wird, sondern als long Berechnung. Denn ein (32-Bit)
>> long kann dieses Ergebnis aufnehmen.
>>
>> Und wie machen wir das?
>
> Ich bin davon ausgegangen, dass so eine Art Autocast vorgenommen werden
> würde :D

Du bist falsch ausgegangen.

Und spätestens jetzt ist der Punkt für meinen Standardspruch erreicht:
Du brauchst ein vernünftiges Buch!

Denn #das# war noch vergleichsweise einfach!

von Wilhelm F. (Gast)


Lesenswert?

Wenn du auf dem 8051 einen printf() verwendest, dann verwende ihn am 
besten nur, wenn du das "Hello WORLD Programm" machst, und sonst nichts.

Sonst nimm lieber low level Routinen zu Umwandlungen und Eingaben 
Ausgaben aller Art.

Ich hab natürlich leicht reden, habe FIFOs seriell und skalierbare 
Rechenroutinen sogar in Assembler eingebunden separat, was ich in der 
Vergangenheit alles mal erstellte. Z.B. BIN-BCD mehrstellig, und 
optimiert schnellstens.

Um eine Zahl auszugeben, schreibe ich dann den Funktionsaufruf: 
print_bin_bcd(argument). Das geht ins FIFO, wird nicht online direkt 
ausgegeben, um den µC zu blockieren.

von 32MHz/32kB/4kB (Gast)


Lesenswert?

Wilhelm Ferkes schrieb:
> Wenn du auf dem 8051 einen printf() verwendest, dann verwende ihn am
> besten nur, wenn du das "Hello WORLD Programm" machst, und sonst nichts.

Warum denn das? Ohne Speicher- und Laufzeitprobleme gehen printf() und 
Co. problemlos.

Die Zeiten der Asm-Bit-Speichergeizerei ist vorbei!

von Wilhelm F. (Gast)


Lesenswert?

32MHz/32kB/4kB schrieb:

> Wilhelm Ferkes schrieb:
>> Wenn du auf dem 8051 einen printf() verwendest, dann verwende ihn am
>> besten nur, wenn du das "Hello WORLD Programm" machst, und sonst nichts.
>
> Warum denn das? Ohne Speicher- und Laufzeitprobleme gehen printf() und
> Co. problemlos.
>
> Die Zeiten der Asm-Bit-Speichergeizerei ist vorbei!

Halb richtig, oder völlig falsch.

Die printf()-Funktion steckt so lange fest, bis sie z.B. von einem 
Interrupt unterbrochen wird. Sonst weiter nichts.

von Peter D. (peda)


Lesenswert?

Wenn man nicht gerade nen mini 8051 mit 2kB Flash nimmt, kann man 
durchaus printf einsetzen.
Und die Laufzeit spielt auch keine Rolle, wenn es für die Anzeige ist, 
so schnell kann kein Mensch ablesen.

Und auch die float Libs sind nicht so schlecht, daß man davor gleich 
Angst haben muß. Nur für superschnelle Regelkreise ist float vielleicht 
nicht die beste Wahl.

Ein 8051 mit 32kB, 64kB oder 128kB Flash ist kein Thema mehr. Und 
XTAL/12 wird oftmals auch nicht mehr geteilt.


Peter

von Wilhelm F. (Gast)


Lesenswert?

Es kommt drauf an. 9600 Baud sind 9600 Baud, auch wenn der µC mit 200MHz 
rennt. Wenn man da nicht selbst die Libraries umbaut, dann steckt ein 
printf() eben mit den 9600 Baud und Polling so lange an seiner Stelle 
fest, bis auch das letzte Zeichen ausgegeben ist.

von Peter D. (peda)


Lesenswert?

Das liegt dann aber nicht am printf, sondern am putchar. Schreib für das 
putchar nen FIFO und die Sache ist gegessen.


Peter

von Wilhelm F. (Gast)


Lesenswert?

Peter Dannegger schrieb:

> Das liegt dann aber nicht am printf, sondern am putchar. Schreib für das
> putchar nen FIFO und die Sache ist gegessen.
>
> Peter

Ja sicher. Ich weiß es ja. Man muß auch FIFOs in die serielle 
Schnittstelle implementieren, wo putchar hinein schreibt. Manche 
Compiler liefern es mit, andere nicht. SDCC beispielsweise hat es.

Aber wenn man einen Compiler nach der Installation als Ahnungsloser mit 
printf() einfach mal so benutzt, ist es eben tödlich. Da kann man Pech 
haben, daß eben putchar nicht in ein FIFO schreibt, und bei jedem Byte 
das Transmit-Flag abgewartet wird.

In meiner Studentenzeit liefen die Jungs aber reihenweise auf, und 
wunderten sich, daß ihr Programm außer printf() nicht mehr viel machte. 
Ein Professor warnte auch davor, in einem schnellen Programm printf() 
anzuwenden.

OK, für die Rechnerei innerhalb des printf() sind die µC heute reichlich 
schnell.

von 32MHz/32kB/4kB (Gast)


Lesenswert?

Wilhelm Ferkes schrieb:
> Aber wenn man einen Compiler nach der Installation als Ahnungsloser mit
> printf() einfach mal so benutzt, ist es eben tödlich. Da kann man Pech
> haben, daß eben putchar nicht in ein FIFO schreibt, und bei jedem Byte
> das Transmit-Flag abgewartet wird.
>
> In meiner Studentenzeit liefen die Jungs aber reihenweise auf, und
> wunderten sich, daß ihr Programm außer printf() nicht mehr viel machte.
> Ein Professor warnte auch davor, in einem schnellen Programm printf()
> anzuwenden.

Die gute alte Zeit ist schon lange vorbei. Und Profs nur Theoretiker. 
;-P

Bei µCs muss putchar() sowieso an die eigene Hardware angepasst werden. 
Das war gestern so und ist noch heute so.

Peter Dannegger schrieb:
> Ein 8051 mit 32kB, 64kB oder 128kB Flash ist kein Thema mehr. Und
> XTAL/12 wird oftmals auch nicht mehr geteilt.

Ja, bei 8051 denken immer alle an einen 40 poligen DIL Riesen mit 
externen Speichern und 11,... MHz Quarz. :-(

von Wilhelm F. (Gast)


Lesenswert?

32MHz/32kB/4kB schrieb:

> Bei µCs muss putchar() sowieso an die eigene Hardware angepasst werden.
> Das war gestern so und ist noch heute so.

Aber das weiß man erst, wenn man mal mit der Schnauze gegen die Wand 
lief, und stark wieder abprallte. Anfänger eher nicht. An der FH gab es 
dazu auch nicht den geringsten Ton.

von 32MHz/32kB/4kB (Gast)


Lesenswert?

Wilhelm Ferkes schrieb:
>> Bei µCs muss putchar() sowieso an die eigene Hardware angepasst werden.
>> Das war gestern so und ist noch heute so.
>
> Aber das weiß man erst, wenn man mal mit der Schnauze gegen die Wand
> lief, und stark wieder abprallte.

Nö, das gilt immer und für jeden. Woher weiss der Compiler Hersteller 
wohin dein putchar() sendet? LCD, SPI, I2C, RS232, ..., ... Da muss man 
immer selber ran.

Denn sie wissen nicht was sie tun gibt es im Kino und nicht in der 
Entwicklung. ;-P

von Wilhelm F. (Gast)


Lesenswert?

32MHz/32kB/4kB schrieb:

> Nö, das gilt immer und für jeden.

Da hast du völlig Recht.

von Proxxon (Gast)


Angehängte Dateien:

Lesenswert?

Georg G. (df2au) schrieb:

> Es gibt Gerüchte, wonach der Typ "Integer" nicht auf allen Maschinen 16
> Bit breit ist. Bei manchen Compilern ist er 32 Bit breit. Damit passt
> 100000 auch in ein Integer und den kleine Fehler mit dem "l oder nicht
> l" verzeiht der Compiler, vielleicht sogar ohne Warnung (die der gute
> Programmierer ohnehin abstellt, denn er weiß alles besser).

Karl Heinz Buchegger (kbuchegg) (Moderator) konterte

> Bitte Georg.

> Wir reden hier von einem 8051, einem 8-Bit µC.
> Es ist nicht zielführend, wenn du den Fragesteller jetzt auch noch mit
> Spitzfindigkeiten zusätzlich verwirrst.

Man könnte dazu anmerken, wenn der int-Typ 32-bit umfasst stellt sich 
das Problem mit printf auch wieder, wenn die Zahlen dort dann nicht mehr 
hineinpassen.

Siehe Anhang. Einmal mit und ohne expliziter Typumwandlung nach 64-bit 
signed int.

PC halt, aber das gleiche Problem. Also nix mit "impliziter Umwandlung".

von c90 (Gast)


Lesenswert?

Proxxon schrieb:
> Man könnte dazu anmerken, wenn der int-Typ 32-bit umfasst stellt sich
> das Problem mit printf auch wieder

auf einem 8Bit 8051???

Bitte nenne den Compiler.

von Proxxon (Gast)


Lesenswert?

c90 (Gast) schrieb:

Proxxon schrieb:
>> Man könnte dazu anmerken, wenn der int-Typ 32-bit umfasst stellt sich
>> das Problem mit printf auch wieder

> auf einem 8Bit 8051???

Nö.

> Bitte nenne den Compiler.

Pelles C und das Beispiel bezog sich nicht auf µC. Ging aber auch aus 
dem Text hevor. "PC halt, .."

von c90 (Gast)


Lesenswert?

Proxxon schrieb:
> das Beispiel bezog sich nicht auf µC

Wirf mal einen Blick auf den Titel:
"8051er Ausgabeproblem"

Für mich ist der 8051 ein µC und kein PC, aber man lernt ja nie aus :-(

von Proxxon (Gast)


Lesenswert?

> Wirf mal einen Blick auf den Titel:

Hättest du mal besser mein Posting richtig gelesen ..

Im übrigen war das Threadthema längst durch, was man unschwer an den 
nicht mehr getätigten Wortbeiträgen erkennen kann. Deshalb hast du hier 
ein sinnloses Palaver losgetreten. Aber wenn du möchtest darfst du dich 
gerne weiter daran abarbeiten. Viel Spass!

von buffer overflow (Gast)


Lesenswert?

Da der Thread sowieso durch ist, kann ich ihn auch noch schänden:

Für die korrekte Verwendung von printf, fprintf und sprintf muss man bei 
jeder Quelltext-Änderung feststellen, wie lang der Ergebnisstring 
maximal werden kann, und das mit der Länge des Puffers vergleichen, die 
in der jeweiligen stdlib benutzt wird. Das kann besonders bei Verwendung 
des Formats "%s" recht aufwendig und fehlerhaft werden, und wenn 
irgendwelche Strings von "außen" kommen, sind "if (strlen(..." 
-Anweisungen unumgänglich.

Auch wenn printf sehr kompfortabel in der Anwendung scheint, so kann 
seine Verwendung in Einzelfällen so gefährlich sein, dass man lieber 
ganz darauf verzichtet, und den Code, der die Daten in lesbare Form 
bringt, für jeden Parameter selbst aufruft. Da gewöhnt man sich schnell 
dran, und vermisst printf bald gar nicht mehr.

Ähnliches gilt für scanf, strcpy, strcat, etc..

Diese ganzen Funktionen sind wie Stützräder am Fahrrad. - Für den Anfang 
hilfreich, aber wenn man sie nicht rechtzeitig ablegt, haut man sich 
damit irgendwann in der Kurve auf die Fresse.

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.