Inspiriert von der Frage „Unterschied beim Initialisieren und Nullsetzen eines Arrays in c/c++?“ habe ich mich entschlossen, die Assemblierung eines optimierten Release-Builds für Windows Mobile Professional (ARM-Prozessor, aus dem Microsoft Optimizing Compiler) in meinem Fall tatsächlich zu untersuchen. Was ich fand, war etwas überraschend, und ich frage mich, ob jemand etwas Licht in meine diesbezüglichen Fragen bringen kann.
Diese beiden Beispiele werden untersucht:
byte a[10] = { 0 };
byte b[10];
memset(b, 0, sizeof(b));
Sie werden in derselben Funktion verwendet, daher sieht der Stack folgendermaßen aus:
[ ] // padding byte to reach DWORD boundary
[ ] // padding byte to reach DWORD boundary
[ ] // b[9] (last element of b)
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ] // b[0] = sp + 12 (stack pointer + 12 bytes)
[ ] // padding byte to reach DWORD boundary
[ ] // padding byte to reach DWORD boundary
[ ] // a[9] (last element of a)
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ]
[ ] // a[0] = sp (stack pointer, at bottom)
Die generierte Assembly mit meinen Kommentaren:
; byte a[10] = { 0 };
01: mov r3, #0 // r3 = 0
02: mov r2, #9 // 3rd arg to memset: 9 bytes, note that sizeof(a) = 10
03: mov r1, #0 // 2nd arg to memset: 0-initializer
04: add r0, sp, #1 // 1st arg to memset: &a[1] = a + 1, since only 9 bytes will be set
05: strb r3, [sp] // a[0] = r3 = 0, sets the first element of a
06: bl memset // continue in memset
; byte b[10];
; memset(b, 0, sizeof(b));
07: mov r2, #0xA // 3rd arg to memset: 10 bytes, sizeof(b)
08: mov r1, #0 // 2nd arg to memset: 0-initializer
09: add r0, sp, #0xC // 1st arg to memset: sp + 12 bytes (the 10 elements
// of a + 2 padding bytes for alignment) = &b[0]
10: bl memset // continue in memset
Jetzt verwirren mich zwei Dinge:
- Was ist der Sinn der Zeilen 02 und 05? Warum nicht einfach &a geben[0] und 10 Bytes zu memset?
- Warum werden die Füllbytes einer 0 nicht initialisiert? Ist das nur zum Auffüllen in Strukturen?
Bearbeiten: Ich war zu neugierig, um den Struct-Fall nicht zu testen:
struct Padded
{
DWORD x;
byte y;
};
Der Assembler zum 0-Initialisieren:
; Padded p1 = { 0 };
01: mov r3, #0
02: str r3, [sp]
03: mov r3, #0
04: str r3, [sp, #4]
; Padded p2;
; memset(&p2, 0, sizeof(p2));
05: mov r3, #0
06: str r3, [sp]
07: andcs r4, r0, #0xFF
08: str r3, [sp, #4]
Hier sehen wir in Zeile 04, dass tatsächlich eine Auffüllung erfolgt, da str
(im Gegensatz zu strb
) wird genutzt. Recht?
Nun, nach dem Lesen der Kommentare unten scheint es, dass msvc beim Nullen des Speichers nicht sehr konsequent ist.
– Matt Tischler
26. Oktober 2009 um 23:49 Uhr