Forum: Compiler & IDEs Was macht der Compiler mit meinen paar Zeilen C-Code?


von Sam .. (sam1994)


Lesenswert?

Hi

Ich hab mich mal vor kurzem ein bisschen in Asm eingelesen. Als ich aber 
dann mal ein Programm kompiliert habe und auf die verdammte Größe von 72 
Bytes starrte, fragte ich mich was für einen Mist der Compiler da macht? 
Schreibt der da Standardmäßig irgendweche Routinen rein?

Das war der Code:
1
#include <avr/io.h>
2
3
int main()
4
{
5
    DDRD = 1 << 6; //PD6 als Ausgang
6
    DDRB = 0xFF;   //Alle PB Ports als Ausgänge
7
    
8
    PORTD = 1 << 6;
9
    PORTB = 1 << 3;
10
    
11
    while(1);
12
}

Aus dem wurde dann:
1
; Disassembly of main.hex (avr-gcc style)
2
3
.text
4
main:
5
cpi     r19, 0x1a       ; 26
6
cpi     r19, 0x00       ; 0
7
cpi     r19, 0x00       ; 0
8
cpi     r19, 0x00       ; 0
9
cpi     r19, 0x10       ; 16
10
sbci    r19, 0x32       ; 50
11
cpi     r19, 0x10       ; 16
12
sbci    r19, 0x37       ; 55
13
cpi     r19, 0x10       ; 16
14
sbci    r19, 0x36       ; 54
15
cpi     r19, 0x10       ; 16
16
sbci    r19, 0x35       ; 53
17
cpi     r19, 0x10       ; 16
18
sbci    r19, 0x34       ; 52
19
cpi     r19, 0x10       ; 16
20
sbci    r19, 0x33       ; 51
21
cpi     r19, 0x10       ; 16
22
sbci    r19, 0x32       ; 50
23
cpi     r19, 0x10       ; 16
24
sbci    r19, 0x31       ; 49
25
cpi     r19, 0x50       ; 80
26
sbc     r3, r18
27
cpi     r19, 0x1a       ; 26
28
cpi     r19, 0x00       ; 0
29
cpi     r19, 0x10       ; 16
30
cpi     r19, 0x00       ; 0
31
cpi     r19, 0x10       ; 16
32
sbci    r19, 0x30       ; 48
33
cpi     r19, 0x00       ; 0
34
sbci    r20, 0x36       ; 54
35
cpi     r19, 0x00       ; 0
36
sbci    r20, 0x35       ; 53
37
cpi     r19, 0x00       ; 0
38
sbci    r20, 0x34       ; 52
39
cpi     r19, 0x00       ; 0
40
sbci    r20, 0x33       ; 51
41
cpi     r19, 0x00       ; 0
42
sbci    r20, 0x32       ; 50
43
cpi     r19, 0x00       ; 0
44
sbci    r20, 0x31       ; 49
45
cpi     r19, 0x00       ; 0
46
sbci    r19, 0x39       ; 57
47
cpi     r19, 0x70       ; 112
48
sbc     r4, r19
49
cpi     r19, 0x1a       ; 26
50
cpi     r19, 0x00       ; 0
51
cpi     r19, 0x20       ; 32
52
cpi     r19, 0x00       ; 0
53
cpi     r19, 0x00       ; 0
54
sbci    r19, 0x38       ; 56
55
cpi     r19, 0x00       ; 0
56
sbci    r19, 0x37       ; 55
57
cpi     r19, 0x00       ; 0
58
sbci    r19, 0x36       ; 54
59
cpi     r19, 0x10       ; 16
60
cpi     r19, 0x21       ; 33
61
cpi     r19, 0x14       ; 20
62
sbci    r20, 0x26       ; 38
63
sbci    r20, 0x35       ; 53
64
sbci    r20, 0x56       ; 86
65
sbci    r20, 0x34       ; 52
66
sbci    r20, 0x24       ; 36
67
cpi     r20, 0x06       ; 6
68
sbci    r19, 0x42       ; 66
69
cpi     r19, 0x40       ; 64
70
sbc     r4, r22
71
cpi     r19, 0x1a       ; 26
72
cpi     r19, 0x00       ; 0
73
cpi     r19, 0x30       ; 48
74
cpi     r19, 0x00       ; 0
75
cpi     r19, 0x00       ; 0
76
sbci    r19, 0x39       ; 57
77
sbci    r19, 0x50       ; 80
78
sbci    r19, 0x36       ; 54
79
cpi     r20, 0x86       ; 134
80
sbci    r19, 0x50       ; 80
81
cpi     r19, 0x84       ; 132
82
sbci    r19, 0x21       ; 33
83
cpi     r20, 0x92       ; 146
84
sbci    r20, 0x56       ; 86
85
cpi     r20, 0x96       ; 150
86
sbci    r19, 0x27       ; 39
87
cpi     r20, 0x82       ; 130
88
sbci    r19, 0x22       ; 34
89
cpi     r20, 0x82       ; 130
90
sbci    r19, 0x58       ; 88
91
cpi     r19, 0x10       ; 16
92
sbc     r4, r20
93
cpi     r19, 0x0a       ; 10
94
cpi     r19, 0x08       ; 8
95
cpi     r19, 0x40       ; 64
96
cpi     r19, 0x00       ; 0
97
cpi     r19, 0x80       ; 128
98
sbci    r19, 0x28       ; 40
99
sbci    r20, 0x62       ; 98
100
sbci    r20, 0x36       ; 54
101
sbci    r20, 0x66       ; 102
102
cpi     r19, 0x98       ; 152
103
sbci    r19, 0x64       ; 100
104
sbci    r20, 0x36       ; 54
105
cpi     r20, 0x46       ; 70
106
sbc     r4, r20
107
cpi     r19, 0x0a       ; 10
108
cpi     r19, 0x00       ; 0
109
cpi     r19, 0x00       ; 0
110
cpi     r19, 0x00       ; 0
111
sbci    r19, 0x61       ; 97
112
sbc     r4, r22

Kann mir jemand mal erklären was das soll? Der macht ja dauernd 
irgendwelche vergleiche, und subtrahiert irgendwas.

von Ich (Gast)


Lesenswert?

Wenn ich mich jetz nicht täusche, fehlt hier die while-Schleife.

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Wie hast du denn das asm-file erzeugt (gcc Aufruf mit Parametern)? 
Normalerweise macht der keinen solchen Unsinn.

Ich sehe garnicht, wo das passieren soll, was im Quelltext steht.

Grüße,

Peter

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Peter Diener schrieb:
> Wie hast du denn das asm-file erzeugt (gcc Aufruf mit Parametern)?
> Normalerweise macht der keinen solchen Unsinn.

Das ist kein gcc-Output, sondern der eines Disassemblers.

von holger (Gast)


Lesenswert?

>Als ich aber
>dann mal ein Programm kompiliert habe und auf die verdammte Größe von 72
>Bytes starrte,

Ach mein Gottchen, das ist aber echt viel!
Es gibt noch ein Leben vor main() ;)

von Ansgar K. (paulderbademeister)


Lesenswert?

1. C braucht immer etwas mehr Platz als man aufgrund des Codes denken 
würde, da z.B. vor dem Start der main noch Variablen initialisiert 
werden, alle Interrupts mit Dummysprüngen belegt werden und ähnliches...

2. Das Listing passt überhaupt nicht zu deinem C-Code - falls das 
Programm läuft würde ich sagen, dass der Disassembler Murks produziert. 
Z.B. sehe ich da keinen einzigen Speicherbefehl, der etwas auf die Ports 
gibt.

von Peter D. (peda)


Lesenswert?

Also bei mir erzeugt er dieses Listing (ohne Prolog):
1
int main()
2
{
3
    DDRD = 1 << 6; //PD6 als Ausgang
4
  34:  80 e4         ldi  r24, 0x40  ; 64
5
  36:  81 bb         out  0x11, r24  ; 17
6
    DDRB = 0xFF;   //Alle PB Ports als Ausgänge
7
  38:  9f ef         ldi  r25, 0xFF  ; 255
8
  3a:  97 bb         out  0x17, r25  ; 23
9
    
10
    PORTD = 1 << 6;
11
  3c:  82 bb         out  0x12, r24  ; 18
12
    PORTB = 1 << 3;
13
  3e:  88 e0         ldi  r24, 0x08  ; 8
14
  40:  88 bb         out  0x18, r24  ; 24
15
  42:  ff cf         rjmp  .-2        ; 0x42 <__SREG__+0x3>


Peter

von Sam .. (sam1994)


Lesenswert?

achso, jetzt weiß ich meinen Fehler. Ich hab das HexFile genommen und 
das nimmt das Programm nicht an.

Entschuldigt bitte meine Unwissenheit.

Hier ist das ganze mit der bin-Datei:
1
; Disassembly of main.bin (avr-gcc style)
2
3
.text
4
main:
5
6
; Referenced from offset 0x32 by rjmp
7
Label1:
8
rjmp    Label2
9
rjmp    Label3
10
rjmp    Label3
11
rjmp    Label3
12
rjmp    Label3
13
rjmp    Label3
14
rjmp    Label3
15
rjmp    Label3
16
rjmp    Label3
17
rjmp    Label3
18
rjmp    Label3
19
rjmp    Label3
20
rjmp    Label3
21
rjmp    Label3
22
rjmp    Label3
23
rjmp    Label3
24
rjmp    Label3
25
rjmp    Label3
26
rjmp    Label3
27
28
; Referenced from offset 0x00 by rjmp
29
Label2:
30
clr     r1
31
out     0x3f, r1        ; 63
32
ldi     r28, 0xdf       ; 223
33
out     0x3d, r28       ; 61
34
rcall   Function1
35
rjmp    Label5
36
37
; Referenced from offset 0x04 by rjmp
38
; Referenced from offset 0x06 by rjmp
39
; Referenced from offset 0x08 by rjmp
40
; Referenced from offset 0x0a by rjmp
41
; Referenced from offset 0x0c by rjmp
42
; Referenced from offset 0x0e by rjmp
43
; Referenced from offset 0x10 by rjmp
44
; Referenced from offset 0x12 by rjmp
45
; Referenced from offset 0x02 by rjmp
46
; Referenced from offset 0x16 by rjmp
47
; Referenced from offset 0x18 by rjmp
48
; Referenced from offset 0x1a by rjmp
49
; Referenced from offset 0x1c by rjmp
50
; Referenced from offset 0x1e by rjmp
51
; Referenced from offset 0x20 by rjmp
52
; Referenced from offset 0x22 by rjmp
53
; Referenced from offset 0x24 by rjmp
54
; Referenced from offset 0x14 by rjmp
55
Label3:
56
rjmp    Label1
57
58
; Referenced from offset 0x2e by rcall
59
Function1:
60
ldi     r24, 0x40       ; 64
61
out     0x11, r24       ; 17
62
ser     r25
63
out     0x17, r25       ; 23
64
out     0x12, r24       ; 18
65
ldi     r24, 0x08       ; 8
66
out     0x18, r24       ; 24
67
68
; Referenced from offset 0x42 by rjmp
69
Label4:
70
rjmp    Label4
71
72
; Referenced from offset 0x30 by rjmp
73
Label5:
74
cli
75
76
; Referenced from offset 0x46 by rjmp
77
Label6:
78
rjmp    Label6

Die vielen rjmp macht c Standardmäßig rein, oder wie? ISt das nicht 
unnötig?
Und warum deaktiviert der globale Interrupts wenn ich es nichtmal im 
C-Code geschrieben habe. Erfindet der das einfach dazu, damit die 
schleife wirklich unendlich ist?

von Michael H. (michael_h45)


Lesenswert?

Samuel K. schrieb:
> achso, jetzt weiß ich meinen Fehler. Ich hab das HexFile genommen und
> das nimmt das Programm nicht an.
Der hatte wohl eine Binärdatei erwartet und kein Intel-Hex.
http://hex2bin.sourceforge.net/

von Markus Engel (Gast)


Lesenswert?

Die Jumps gehören zur Interrupt-Vektor-Tabelle. Die liegt am Anfang vom 
Flash und darin steht, bei welchem Interrupt er wohin springen soll. Die 
sollte auch im Assembler-Code drinstehen, den du selber schreibst ;) .
Der erste Sprung zum Label2 ist der Reset-Vektor. Der wird nach dem 
Strom-An ausgeführt und springt zu deinem Code. Alle anderen springen 
über den Umweg Label3 zum Anfang zurück, machen also einen Reset, wenn 
versehentlich ein solcher, von dir nicht verwendeter Interrupt ausgelöst 
wird.
Das mit dem cli bastelt der Compiler dazu, richtig. Störts dich?

von Ansgar K. (paulderbademeister)


Lesenswert?

Samuel K. schrieb:
> Und warum deaktiviert der globale Interrupts wenn ich es nichtmal im
> C-Code geschrieben habe. Erfindet der das einfach dazu, damit die
> schleife wirklich unendlich ist?

Das ist eine zusätzliche Schleife, die der Compiler hinzufügt, um den µC 
davon abzuhalten weiterzumachen, falls aus irgendeinem Grund aus der 
main zurückgesprungen wird. Ansonsten könnte er beispielsweise 
probieren, die Anfangswerte von Variablen (die weiter hinten im Flash 
liegen) als ausführbaren Code zu interpretieren und Mist produzieren. Da 
wenn diese Stelle erreicht wird schon irgendetwas schiefgelaufen ist, 
werden auch Interrupts abgeschaltet und der Controller macht erst nach 
einem Reset weiter.

von Simon H. (simi)


Lesenswert?

Der C-Compiler bastelt überhaupt nichts, was Du nicht explizit 
schreibst. Wäre ja auch merkwürdig und würde mich auch irgendwie jucken.

ABER:

Wie oben schon ironisch erwähnt: Es gibt ein Leben vor main(). Dieses 
Leben heisst z.B. crt0. oder startup. Das ist ein Object-File, welches 
vom Linker mit in Dein Binary reingelinkt wird. Der Sinn ist wie folgt:

Der C-Compiler erzeugt Dir aus Deinem C-File Befehl für Befehl 
Maschinencode. Nicht mehr und nicht weniger. Und den schmeisst er in 
Dein main.o

Nehmen wir mal an, Du würdest NUR dieses main.o in Dein bin-File 
schreiben. Dann wäre also der erste Befehl von main() auf Adresse 0 
(nehme ich mal an, kommt auf das Link-Script an). Ok, der Prozi wird da 
anfangen zu robotten.

Aber was ist z.B. mit einer globalen Variable, die Du auf einen Wert 
initialisiert hast?

z.B. int x = 5; (im global scope)?

Jetzt müsste der C-compiler eigentlich Code "erfinden", der irgendwoher 
eine Fünf nimmt und sie in eine RAM-Zelle schreibt. Wohin soll der Code? 
in main.o? Nö, sicher nicht, ist ja nicht im Scope von main(). Wohin 
also?

Anwort: Nirgendwohin. Der Compiler teilt dem Linker bloss mit:
Ich möchte, dass Du im ROM eine Speicherzelle mit einer 5 beschreibst. 
Und ich möchte, dass Du mir eine Speicherzelle im RAM reserviertst.

Und jetzt geht der Compiler einfach frech davon aus, dass die 5 ins Ram 
gespeichert wird, bevor main() aufgerufen wird.
WIE das geschieht, ist dem C-Compiler scheissegal. Und genau dafür 
braucht es z.B. so ein crt0 oder startup Object, welches dieses 
Initialisieren erledigt.

Und dann gibt's halt noch viele andere Dinge, die erledigt werden 
müssen, und die den C-Compiler überhaupt nichts interessieren. Wie eben 
z.B. Interrupts deaktivieren. Interrupt-Vektoren abspitzen. Oszillatoren 
konfigurieren etcetc....

Warum interessiert all das den C-Compiler nicht? Weil er keine Ahnung 
haben darf, wie das geschieht. Er soll ja prozessorunabhängig sein.

crt0 ist also nichts anderes als eine Routine - resp. ein Object-File, 
geschrieben in Assembler oder wiederum in (sehr limitiertem) C. Und DICH 
braucht dieses crt0 überhaupt nicht zu kümmern. Denn wie der C-Compiler 
willst Du einfach davon ausgehen dürfen, dass alles so eingerichtet ist, 
wie C es verlangt - vor Aufruf von main().
Und wer nun schreibt dieses crt0? Üblicherweise die Typen, die den Prozi 
machen und/oder die Typen, die den Compiler rausbringen.


Gruäss
Simon

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.