18.2 让我们用malloc()为结构体分配空间

但是,有时候把结构体放在堆中而不是栈上却更简单:

  1. #include <windows.h>
  2. #include <stdio.h>
  3. void main()
  4. {
  5. SYSTEMTIME *t;
  6. t=(SYSTEMTIME *)malloc (sizeof (SYSTEMTIME));
  7. GetSystemTime (t);
  8. printf ("%04d-%02d-%02d %02d:%02d:%02d",
  9. t->wYear, t->wMonth, t->wDay,
  10. t->wHour, t->wMinute, t->wSecond);
  11. free (t);
  12. return;
  13. };

让我们用优化/Ox编译一下它,看看我们得到什么东西

清单18.4: 优化的MSVC

  1. _main PROC
  2. push esi
  3. push 16 ; 00000010H
  4. call _malloc
  5. add esp, 4
  6. mov esi, eax
  7. push esi
  8. call DWORD PTR __imp__GetSystemTime@4
  9. movzx eax, WORD PTR [esi+12] ; wSecond
  10. movzx ecx, WORD PTR [esi+10] ; wMinute
  11. movzx edx, WORD PTR [esi+8] ; wHour
  12. push eax
  13. movzx eax, WORD PTR [esi+6] ; wDay
  14. push ecx
  15. movzx ecx, WORD PTR [esi+2] ; wMonth
  16. push edx
  17. movzx edx, WORD PTR [esi] ; wYear
  18. push eax
  19. push ecx
  20. push edx
  21. push OFFSET $SG78833
  22. call _printf
  23. push esi
  24. call _free
  25. add esp, 32 ; 00000020H
  26. xor eax, eax
  27. pop esi
  28. ret 0
  29. _main ENDP

所以,sizeof(SYSTEMTIME) = 16, 这正是malloc所分配的字节数。它返回了刚刚分配的地址空间,这个指针存在EAX寄存器里。然后,这个指针会被移动到ESI结存器中, GetSystemTime()会用它来存储返回值,这也就是为什么这里分配完之后并没有把EAX放到某个地方保存起来,而是直接使用它的原因。

新指令:MOVZX(Move with Zero eXtent, 0扩展移动)。它可以说是和MOVSX基本一样(13.1.1节),但是,它把其他位都设置为0。这是因为printf()需要一个32位的整数,但是我们的结构体里面是WORD,这只有16位厂。这也就是为什么从WORD复制到INT时第16~31位必须清零的原因了。因为,如果不清除的话,剩余位可能有之前操作留下来的干扰数据。

在下面这个例子里面,我可以用WORD数组来重现这个结构:

  1. #include <windows.h>
  2. #include <stdio.h>
  3. void main()
  4. {
  5. WORD *t;
  6. t=(WORD *)malloc (16);
  7. GetSystemTime (t);
  8. printf ("%04d-%02d-%02d %02d:%02d:%02d",
  9. t[0] /* wYear */, t[1] /* wMonth */, t[3] /* wDay */,
  10. t[4] /* wHour */, t[5] /* wMinute */, t[6] /* wSecond */);
  11. free (t);
  12. return;
  13. };

我们得到:

  1. $SG78594 DB ’%04d-%02d-%02d %02d:%02d:%02d’, 0aH, 00H
  2. _main PROC
  3. push esi
  4. push 16 ; 00000010H
  5. call _malloc
  6. add esp, 4
  7. mov esi, eax
  8. push esi
  9. call DWORD PTR __imp__GetSystemTime@4
  10. movzx eax, WORD PTR [esi+12]
  11. movzx ecx, WORD PTR [esi+10]
  12. movzx edx, WORD PTR [esi+8]
  13. push eax
  14. movzx eax, WORD PTR [esi+6]
  15. push ecx
  16. movzx ecx, WORD PTR [esi+2]
  17. push edx
  18. movzx edx, WORD PTR [esi]
  19. push eax
  20. push ecx
  21. push edx
  22. push OFFSET $SG78594
  23. call _printf
  24. push esi
  25. call _free
  26. add esp, 32 ; 00000020H
  27. xor eax, eax
  28. pop esi
  29. ret 0
  30. _main ENDP

同样,我们可以看到编译结果和之前一样。个人重申一次,你不应该在写代码的时候用这么晦涩的方法来表达它。