当前位置: 代码迷 >> 汇编语言 >> 结构体数组初始化不正确,是编译器bug还是小弟我的有关问题
  详细解决方案

结构体数组初始化不正确,是编译器bug还是小弟我的有关问题

热度:195   发布时间:2016-05-02 04:23:55.0
结构体数组初始化不正确,是编译器bug还是我的问题?
下面是我的一个测试过程,最初测试目标已达到,但又发现了相关问题,问题很小,但很严重,我会尽量写详细,所以看上去内容比较多。如若不想看那么多,请直接看结尾图片及其下内容。

我对于 IUnknown 定义中有个不明之处。

IUnknown struct DWORD
      QueryInterface    IUnknown_QueryInterface  ?
      AddRef            IUnknown_AddRef          ?
      Release           IUnknown_Release         ?
IUnknown ends

struct 后面的 DWORD 是干嘛用的?

为此,我进行了这个测试。

我猜想,它可能的作用是:
* 让结构体中的元素以此对齐
* 让结构体大小为它的公倍数

设计一个结构体数组
	struct {char a; int b; char c;} s[3];

清零后往结构体里填值,再按字节显示出来,
由此可以判断出其功效来。

结果:我发现其功能正是两个。

我尝试写了个 3,得出
>> error A2064: structure alignment must be 1, 2, 4, 8, or 16

相应可以用 byte word dword qword 等,但其缩写 db dw dd dq 却不支持。
不写,则默认为 1,即缺省值。

我还发现几个 Bug

1. 实际上它只支持 1 2 4,不支持 8 16。
2. 当对齐非 1 时,它不能正确初始化数组,值不正确。
3. 当对数组中的多个元素(结构体)初始化后,数组元素个数不正确,最终字节数为
   元素(结构体)长度 * 元素个数(dup值) * 用于初始化的元素个数。
   这里它多乘了第三个因数。

以下为全部代码。程序运行后,会弹出对话框,对两个变量和六个数组逐字节,分行显示其十六进制值。

                .386
                .model flat,stdcall
                option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include         windows.inc
include         user32.inc
includelib      user32.lib
include         kernel32.inc
includelib      kernel32.lib

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 定义测试用的结构体
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
HvDWORD struct DWORD
        aa      BYTE    ?
        bb      DWORD   ?
        cc      BYTE    ?
HvDWORD ends
; HvDWORD 与 NoDWORD 的区别在于是否在 struct 关键字后加 DWORD
NoDWORD struct
        aa      BYTE    ?
        bb      DWORD   ?
        cc      BYTE    ?
NoDWORD ends

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .data
hvdd0           HvDWORD <11h,22abcd22h,33h>
hvdd1           HvDWORD 1 dup (<11h,22abcd22h,33h>)
hvdd2           HvDWORD 2 dup (<11h,22abcd22h,33h>)
hvdd3           HvDWORD 3 dup (<11h,22abcd22h,33h>,<44h,55abcd55h,66h>,<77h,88abcd88h,99h>)
nodd0           NoDWORD <11h,22abcd22h,33h>
nodd1           NoDWORD 1 dup (<11h,22abcd22h,33h>)
nodd2           NoDWORD 2 dup (<11h,22abcd22h,33h>)
nodd3           NoDWORD 3 dup (<11h,22abcd22h,33h>,<44h,55abcd55h,66h>,<77h,88abcd88h,99h>)
; 就测试结果看,这里存在不少问题,参见说明。

szCaption       db      'HEX',0
szFormatHex     db      '%02X ',0
szFormatEnd     db      '(%dB)',0ah,0ah,0

                .data?
szText          db      4000 dup (?)

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .code
;--------------------------------------------------------------------
; @brief        将字节串打印为十六进制字符串
; @param        _dst    目标字符串首址
; @param        _src    源字节串首址
; @param        _lng    源字节串长度
; @return       目标字符串尾址
printhex        proc    _dst,_src,_lng
                local   @lng
                
                push    _lng
                pop     @lng
                .repeat
                        mov     eax,_src
                        invoke  wsprintf,_dst,offset szFormatHex,byte ptr [eax]
                        add     _dst,eax
                        ; 下一个
                        inc     _src
                        dec     @lng
                .until @lng == 0
                
                invoke  wsprintf,_dst,offset szFormatEnd,_lng
                add     eax,_dst
                
                ret
printhex        endp
;--------------------------------------------------------------------
start:
                lea     eax,szText
                
                invoke  printhex,eax,offset hvdd0,sizeof hvdd0
                invoke  printhex,eax,offset hvdd1,sizeof hvdd1
                invoke  printhex,eax,offset hvdd2,sizeof hvdd2
                invoke  printhex,eax,offset hvdd3,sizeof hvdd3
                invoke  printhex,eax,offset nodd0,sizeof nodd0
                invoke  printhex,eax,offset nodd1,sizeof nodd1
                invoke  printhex,eax,offset nodd2,sizeof nodd2
                invoke  printhex,eax,offset nodd3,sizeof nodd3
                
                invoke  MessageBox,NULL,offset szText,offset szCaption,MB_OK
                invoke  ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                end     start


对照图片,可以看出:

hvdd0 HvDWORD <11h,22abcd22h,33h>          ; 正确
hvdd1 HvDWORD 1 dup (<11h,22abcd22h,33h>)  ; 错误,值不对
hvdd2 HvDWORD 2 dup (<11h,22abcd22h,33h>)  ; 错误,值不对
hvdd3 HvDWORD 3 dup (<11h,22abcd22h,33h>,<44h,55abcd55h,66h>,<77h,88abcd88h,99h>)  ; 错误,值和个数都不对
nodd0 NoDWORD <11h,22abcd22h,33h>          ; 正确
nodd1 NoDWORD 1 dup (<11h,22abcd22h,33h>)  ; 正确
nodd2 NoDWORD 2 dup (<11h,22abcd22h,33h>)  ; 正确,但我以为后面的元素(结构体)应该全是0
nodd3 NoDWORD 3 dup (<11h,22abcd22h,33h>,<44h,55abcd55h,66h>,<77h,88abcd88h,99h>)  ; 错误,个数不对
 关于错误的归纳,我前面已提及。
结构体数组初始化不正确,是编译器bug还是我的问题?
有没有办法达成如下的初始化?

struct __attribute__((__aligned__(4))) {
    unsigned char   aa;
    unsigned long   bb;
    unsigned char   cc;
} s[3] = {
    {0x11, 0x22abcd22, 0x33},
    {0x44, 0x55abcd55, 0x66},
    {0x77, 0x88abcd88, 0x99}
};


以上。谢谢。
------解决思路----------------------
nodd3 个数不对?
ml.exe 6.14.8444 也是这样,生成的 lst 看着是对的,但obj 就是不对。而且,问题好像还不止于此,struc 语句的说明里说那个 dword 是数组元素对齐大小,和命令行里的 /Zp? 相似,所以就看了下 /Zp4 的结果,更是大出意外。
有这个问题,还是别跟它纠结了吧,确实需要就自己补了。
------解决思路----------------------
不加 /coff 即生成 OMF 格式的 obj 竟然是正确的。/coff 的问题看来是真多,上次有个定义巨量数据区(1024*1024 dd dup (0)) 耗时过多也是 /coff 时存在,OMF 时就不会。
------解决思路----------------------
我用ml 10.00.30319.01、ml 12.00.21005.1测试是没有问题的,见图:


------解决思路----------------------
#3 的也不对吧,按照通常的理解,最上面的两行应该一样的,毕竟只是直接定义和 1 dup <> 的区别。
------解决思路----------------------
我直接复制编译楼主的程序,输出就是这样。
我的理解,ml对
hvdd0 HvDWORD <11h,22abcd22h,33h>
hvdd1 HvDWORD 1 dup (<11h,22abcd22h,33h>)
的处理是不同的,二在意义上并不等价。
前者理解为hvdd0定义为HvDWORD类型<11h,22abcd22h,33h>
后者理解为hvdd1定义为HvDWORD 类型,填充数据字节11h,22abcd22h,33h,不足部分补0。

------解决思路----------------------
引用:
我直接复制编译楼主的程序,输出就是这样。
我的理解,ml对
hvdd0 HvDWORD <11h,22abcd22h,33h>
hvdd1 HvDWORD 1 dup (<11h,22abcd22h,33h>)
的处理是不同的,二在意义上并不等价。
前者理解为hvdd0定义为HvDWORD类型<11h,22abcd22h,33h>
后者理解为hvdd1定义为HvDWORD 类型,填充数据字节11h,22abcd22h,33h,不足部分补0。


也不对,1 dup (<11h,22abcd22h,33h>)确实是复制了一个HvDWORD结构,数据为<11h,22abcd22h,33h>,但是没有处理对齐。

------解决思路----------------------
hvdd2 HvDWORD 2 dup (<11h,22abcd22h,33h>)
写成
hvdd2 HvDWORD <11h,22abcd22h,33h>, <11h,22abcd22h,33h>
就对了。

看来dup()中的结构忽略对齐,算是一个BUG吧,不过一直到VC++2013中的ml都没修正(我没装2015,不知道怎样),估计已经成为“特性”了。

------解决思路----------------------
masm可以测试高版本,不要用6.1x版本了。

或者楼主可以尝试一下yasm或nasm
------解决思路----------------------
对这些古怪的表现,感觉还是用 omf 格式的算了,最多定义成 start:: 全局并 public 之(或将其 proc 了),链接时指定入口并将相应的库再加上,因为 omf 不包含这些信息或是在被 link 转换为 coff 时丢了。
用其它的编译器,语法上要求可能不同,改动或许更大。
------解决思路----------------------
高版本link已经不支持omf格式的obj。
  相关解决方案