News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Struct questions

Started by Jimg, April 20, 2007, 02:26:09 AM

Previous topic - Next topic

Jimg

What is the purpose of an unnamed struct?

For example, in Window.inc is the following:
CY STRUCT
    struct
        Lo dd ?
        Hi dd ?
    ENDS
    int64 dq ?
CY ENDS



and

What is the effect of putting DWORD after the struct .e.q. also in Windows.inc
IID   STRUCT DWORD
    Data1        dd ?
    Data2        dw ?
    Data3        dw ?
    Data4        db 8 dup(?)
IID ENDS




GregL

Quote from: JimgWhat is the effect of putting DWORD after the struct .e.q. also in Windows.inc

Alignment. BYTE = 1, WORD = 2, DWORD = 4.

MASM Programmer's Guide, Chapter 5, Structures and Unions, Alignment Value and Offsets for Structures.

MASM Programmer's Guide


MichaelW

There is a brief description of the structure and union alignments that the compiler implements, which must generally be duplicated in structures and unions used with the API, here (see Structure and Union Layout):

http://msdn2.microsoft.com/en-us/library/aa290049(VS.71).aspx
eschew obfuscation

japheth

 Both examples are not very good, because CY indeed *is* a union, not a struct, which has a size of 8 bytes, and the DWORD alignment parameter for IID has no effect at all.


Jimg

Thanks guys, I must have been really tired last night when I was trying to figure this out :red


Jimg

japheth-

When I print out the SIZEOF CY, it prints out as 16.  So it's not really a union, right?


Jimg

After reading the alignment description at least 10 times, wouldn't an IID structure-

IID   STRUCT DWORD
    Data1        dd ?
    Data2        dw ?
    Data3        dw ?
    Data4        db 8 dup(?)
IID ENDS

come out like:

offset
IID+0    Data1
    +1       .
    +2       .
    +3       .
    +4    Data2
    +5       .
    +6    (unused fill to align Data3)
    +7    (unused fill)
    +8   Data3
    +9      .
   +10    (unused fill to align Data4
   +11    (unused fill)
   +12   Data4
   +13      .
   +14      .
   +15      .
   +16      .
   +17      .
   +18      .
   +19      .

but when I print SIZEOF IID, I get 16, which means no padding was applied  ??

Jimg

And back to my original question---

What is the purpose of an unnamed struct?

For example, in Window.inc is the following:
Code:
CY STRUCT
    struct
        Lo dd ?
        Hi dd ?
    ENDS
    int64 dq ?
CY ENDS

wouldn't this be exactly equal to:
CY STRUCT
    Lo dd ?
    Hi dd ?
    int64 dq ?
CY ENDS

???

MichaelW

The only difference I can see between the version with the unnamed structure and the version without is that the unnamed structure must be initialized as a single item. At the moment I can't think of any good reason to use such an unnamed structure.

If IID were defined as a union (in which case it would need to be renamed to avoid a conflict) SIZEOF would return the size of the largest field.

Because the fields are aligned to the minimum of the alignment and the field size, alignment has no effect on structures where the fields are ordered from largest to smallest.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
SCY STRUCT
  STRUCT
    Lo  dd ?
    Hi  dd ?
  ENDS
  int64 dq ?
SCY ENDS

_SCY STRUCT
  Lo    dd ?
  Hi    dd ?
  int64 dq ?
_SCY ENDS

UIID UNION
  Data1 dd ?
  Data2 dw ?
  Data3 dw ?
  Data4 db 8 dup(?)
UIID ENDS

SIID STRUCT 4
    Data1 dd ?
    Data2 dw ?
    Data3 dw ?
    Data4 db 8 dup(?)
SIID ENDS

S1 STRUCT
  f1 BYTE ?
  f2 WORD ?
  f3 BYTE ?
  f4 DWORD ?
S1 ENDS

S2 STRUCT 2
  f1 BYTE ?
  f2 WORD ?
  f3 BYTE ?
  f4 DWORD ?
S2 ENDS

S4 STRUCT 4
  f1 BYTE ?
  f2 WORD ?
  f3 BYTE ?
  f4 DWORD ?
S4 ENDS

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      scy SCY   <{11111111h,22222222h},3333333333333333h>
      _scy _SCY <11111111h,22222222h,3333333333333333h>

      uiid UIID <>
      siid SIID <11111111h,2222h,3333h,{4,4,4,4,4,4,4,4}>

      s1 S1 <11h,2222h,33h,44444444h>
      s2 S2 <11h,2222h,33h,44444444h>
      s4 S4 <11h,2222h,33h,44444444h>

      buffer db 120 dup(0)
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    print ustr$(SIZEOF scy),13,10
    print ustr$(SIZEOF _scy),13,10,13,10

    invoke HexDump,ADDR scy,SIZEOF scy,ADDR buffer
    print ADDR buffer
    invoke HexDump,ADDR _scy,SIZEOF _scy,ADDR buffer
    print ADDR buffer,13,10

    print ustr$(SIZEOF uiid),13,10
    print ustr$(SIZEOF siid),13,10,13,10

    invoke HexDump,ADDR siid,SIZEOF siid,ADDR buffer
    print ADDR buffer,13,10

    print ustr$(SIZEOF s1),13,10
    print ustr$(SIZEOF s2),13,10
    print ustr$(SIZEOF s4),13,10,13,10

    invoke HexDump,ADDR s1,SIZEOF s1,ADDR buffer
    print ADDR buffer,13,10
    invoke HexDump,ADDR s2,SIZEOF s2,ADDR buffer
    print ADDR buffer,13,10
    invoke HexDump,ADDR s4,SIZEOF s4,ADDR buffer
    print ADDR buffer,13,10,13,10

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

Ignore the trailing zero on the partial dumps, I think this problem has already been reported and fixed in the next version of the HexDump procedure.

16
16

11 11 11 11 22 22 22 22 - 33 33 33 33 33 33 33 33
11 11 11 11 22 22 22 22 - 33 33 33 33 33 33 33 33

8
16

11 11 11 11 22 22 33 33 - 04 04 04 04 04 04 04 04

8
10
12

11 22 22 33 44 44 44 44 - 0
11 00 22 22 33 00 44 44 - 44 44 0
11 00 22 22 33 00 00 00 - 44 44 44 44 0
eschew obfuscation

Jimg

According to the document Greg pointed me to:
QuoteIf the field size in bytes is greater than the alignment value, the field is padded so that its offset is evenly divisible by the alignment value. Otherwise, the field is padded so that its offset is evenly divisible by the field size.

Any padding required to reach the correct offset for the field is added prior to allocating the field. The padding consists of zeros and always precedes the aligned field. The size of the structure must also be evenly divisible by the structure alignment value, so zeros may be added at the end of the structure.

I can't find anything that says:
QuoteBecause the fields are aligned to the minimum of the alignment and the field size, alignment has no effect on structures where the fields are ordered from largest to smallest.
Where did you find this?

drizz

the correct translation (from the PSDK .h file) is:
CY union
struct
Lo dd ?
Hi dd ?
ends
int64 dq ?
CY ends


and the reason is that CY is actually an union

mov eax,cy1.Lo
mov eax,cy1.Hi
fild cy1.int64

QuoteWhat is the purpose of an unnamed struct?
in this example CY represents both struct and a single value
The truth cannot be learned ... it can only be recognized.

Jimg

Sure, it makes perfect sense if CY is a union.

So Windows.inc is in error and needs to be fixed, right?

drizz

The truth cannot be learned ... it can only be recognized.

Jimg

#13
Ok, so far we have:

I screwed up and pick an example (CY) that had other problems thus obscuring my original question about unnamed structs.


An unnamed struct has no reason to exist and to avoid superfluous confusion we shouldn't use it


Masm does not assign storage for structs with the alignment parameter correctly, or at least not how it says it does.

As drizz says, an unnamed struc is useful in a union.
so, otherwise there is no reason to use it.  Or perhaps, you could insert several unnamed structures and reference them using array notation?


Alright, after reading it for at least the 30th time, I finally SEE what it says.  My sincerest apologies for my ignorance.  I even underlined the important part,  and still didn't get it.  Otherwise, the field is padded so that its offset is evenly divisible by the field size.
The last words are FIELD SIZE, not alignment size, i.e. the size of the field being defined.  Ugh.  Some days it's not worth getting out of bed.

so, clarifying the description somewhat for myself-

If the size in bytes of the field being defined is greater than the alignment value, then enough zero bytes are inserted before the field so that its offset is evenly divisible by the alignment value.
Likewise, if the size in bytes of the field being defined is less than or equal to the alignment value, then enough zero bytes are inserted before the field so that its offset is evenly divisible by the size of the field being defined.

Just like Michael said: "... the fields are aligned to the minimum of the alignment and the field size ..."

MichaelW

FWIW at this point, here is the original definition of CY from WinNT.h, complete with comments:

/* the following isn't the real definition of CY, but it is */
/* what RPC knows how to remote */
typedef struct tagCY
    {
    LONGLONG int64;
    } CY;

#else /* 0 */
/* real definition that makes the C++ compiler happy */
typedef union tagCY {
    struct {
#ifdef _MAC
        long      Hi;
        long Lo;
#else
        unsigned long Lo;
        long      Hi;
#endif
    };
    LONGLONG int64;
} CY;

So it looks to me like the MASM definition should be a structure within a union, just as drizz defined it, and using an unnamed structure seems entirely reasonable, given that Microsoft did the same in at least one of the examples in the MASM Programmer's Guide:

uptr  UNION
  dwptr FPWORD  0
  STRUCT
    offs  WORD  0
    segm  WORD  0
  ENDS 
uptr  ENDS

eschew obfuscation