1
2 // 畢業(yè)論文做Pascal編譯器,把編譯生成的中間代碼翻譯為 nasm 匯編調(diào)用 C 語(yǔ)言庫(kù)函數(shù)時(shí),在輸出 float 部分出現(xiàn)問(wèn)題。
3
4
5 // 先說(shuō)明一下,我安排棧中數(shù)據(jù) 8字節(jié)對(duì)齊,不管數(shù)據(jù)實(shí)際大小,都分配 8字節(jié),按最低字節(jié)尋址。
6 // 當(dāng)然,調(diào)用 C 語(yǔ)言函數(shù)時(shí)的參數(shù)棧,還是按 i386 的來(lái)。
7
8
9 // C printf 格式化字符串
10 data.add( head + "c_format_float32 : db \'%f\', 0" );
11 data.add( head + "c_format_float64 : db \'%lf\', 0" );
12 data.add( head + "c_format_float64G: db \'%G\', 0" );
13
14
15 // 棧頂已經(jīng)是 8字節(jié)的 ieee754 double 數(shù)據(jù),然后
16 text.add( line + "push dword c_format_float64G" );
17 // 或 text.add( line + "push dword c_format_float64" ); 也正確
18 text.add( head + "call printf" );
19 text.add( head + "add esp, 12" );
20 // 生成可執(zhí)行文件后,運(yùn)行輸出正確
21
22
23 // 棧頂已經(jīng)是 4字節(jié)的 ieee754 float 數(shù)據(jù),且不等于0(次棧頂 4字節(jié)全零),然后
24 text.add( line + "push dword c_format_float32" );
25 text.add( head + "call printf" );
26 text.add( head + "add esp, 12" );
27 // 生成可執(zhí)行文件后,運(yùn)行輸出 0.000000
28
29
30 // 正確的是
31 // 棧頂已經(jīng)是 4字節(jié)的 ieee754 float 數(shù)據(jù),然后
32 // 先把4字節(jié)的float 轉(zhuǎn)為 8字節(jié)的double
33 text.add( line + "fld dword [esp]" );
34 text.add( head + "fstp qword [esp]" ); // 8字節(jié)對(duì)齊,未覆蓋棧中數(shù)據(jù)
35 text.add( head + "push dword c_format_float32" );
36 text.add( head + "call printf" );
37 text.add( head + "add esp, 12" );
38 // 生成可執(zhí)行文件后,運(yùn)行輸出正確
39
40
結(jié)論:
C 語(yǔ)言的 printf 使用 %f 來(lái)輸出 float 時(shí),實(shí)際上先把 4字節(jié)的float轉(zhuǎn)化為 8字節(jié)的double,然后訪問(wèn)了棧上的 8字節(jié)數(shù)據(jù)。
(環(huán)境:Ubuntu12.04 32位 intel i3 nasm gcc)