8.1 Structures
---------------
8.1.1 What is a structure?
~~~~~~~~~~~~~~~~~~~~~~~~~~~
A structure is a collection of one or more variables, possibly of
different types, grouped together under a single name for convenient
handling.
8.1.2 Syntax
~~~~~~~~~~~~~
struct [<tag>] { <list-of-declarations-of-elements> }
<specifier>[,<specifier>...];
struct <tag> <specifier> [,<specifier>];
Declaration of a structure begins with the keyword 'struct'. It can
be written in two forms.
In the first form, the types and names of elements of the structure
are specified in list-of-declarations-of-elements. In this case <tag> is
optional. This is an identifier which names the structural type defined
by the list of declarations of elements. The <specifier> specifies either
a variable of structure type or an array of structures of this type.
The second form of syntax of the declaration uses a structure tag to
refer to the structure type defined somewhere else in the program.
The list of declarations of elements is a sequence of one or more
declared variables. Each variable declared in this list is called a
structure element.
Structure elements are written to memory sequentially in the order in
which they are declared. By default there is no alignment of elements
within a structure, but you can use the command-line option (switch) to
align within a structure. The structure itself is aligned to an even
address if alignment is switched on.
Examples of structure declarations:
struct test
{
int a;
char b[8];
long c;
} rr, ff[4];
This example declares a structure 'rr' and an array of four
structures 'ff'. The tag 'test' is assigned to the entire set of
variables. This tag can be used to declare other structures, for example:
struct test dd;
This declares a structure 'dd' with a set of elements declared in the
tag 'test'.
When declaring a structure with a previously declared tag, the
keyword 'struct' need not be written, so that you can write:
8.1.3 Initializing structures at compilation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After a structure is declared, its elements can have any value. To
avoid the need for this, structures must be initialized. Only global
structures can be initialized after they are declared. C-- supports
several methods of initializing a structure at declaration:
1. Single value:
struct test dd=2;
In this example, the value 2 is assigned to all elements.
2. Array of values
struct test dd={1,2,,6};
In this example the first element of structure 'dd' is assigned the
value 1, the second 2, and the fourth 6. Values skipped and not
initialized are assigned 0.
3. Command FROM:
struct test dd=FROM "file.dat";
In this example, the contents of <file.dat> are loaded during
compilation to the place where structure 'dd' was located. If the file is
larger than the structure, excess bytes will be loaded into program code
but they will not be used. If the file is smaller than the structure, the
empty bytes of the structure will be filled with zeros.
4. Command EXTRACT:
struct test dd=EXTRACT "file.dat", 24, 10;
In this example a fragment of <file.dat> is loaded during compilation
to the place where structure 'dd' was located. If the file is larger than
the structure, excess bytes will be loaded into program code but they
will not be used. If the file is smaller than the structure, the empty
bytes of the structure will be filled with zeros.
8.1.4 Initializing structures at run time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When a program is executed, in addition to assigning each member of a
structure a value, one can initialize the entire structure by assigning
it a number or a variable. For example:
void proc()
struct test aa[5],rr;
int i;
{
aa[0]=0x12345678;
aa[i]=int 0x12345678;
aa=long 0x12345678;
rr=i;
In the first example, memory occupied by the first structure of an
array of five structures is filled with byte 0x78 (by default).
In the second example, memory occupied by the (i+1)st structure of an
arrawy of five structures is filled with word 0x5678.
In the third example memory occupied by an entire array of five
structures is filled with long word 0x12345678.
In the fourth example memory occupied by structure 'rr' is filled with
the contents of variable i.
The contents of one structure can be copied to another structure. For
example:
rr=aa[2];
The contents of the third structure of an array of structures 'aa' is
copied to structure 'rr'.
8.1.5 Operations on structure members
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Operations available on a structure member are the operations
available for a variable of that type. For example (a declared
structure):
struct test
{
int a;
char b[8];
long c;
} rr[3];
Example of permissible syntax:
rr.a = rr.b[i] * rr[1].c + i ;
Note:
For operations on members of an array of structures, or structures
with indexed members in which a variable is used as the index or
structure number, the compiler can use registers SI or DI, and in some
situations (for instance rr[i].b[j] >< rr[i+1].b[j+2] ) register DX is
also used.
For individual structure members their address, size and offset in
the structure tag can be obtained. For example:
struct AA // declaration of structure tag
{
word a[3]; // first structure member
char b; // second structure member
long c; // third structure member
};
struct BB //second structure tag
{
word aa; // first element
AA bb; // second element - embedded structure
}ss; // declare structure with tag BB
void proc()
{
AX=#ss.bb.b; // get address of member 'b' of structure 'bb' in structure 'ss'
AX=#BB.bb.b; // get offset of this member in tag 'BB'
AX=sizeof(ss.bb); // get size of member 'bb' of structure 'ss'
AX=sizeof(BB.bb); // get size of member 'bb' in tag 'BB'
}
8.1.6 Embedded structures
~~~~~~~~~~~~~~~~~~~~~~~~~~
Tags of previously declared structures can be used when declaring a
structure. Here is an example of an embedded structure:
struct RGB
{
byte Red;
byte Green;
byte Blue;
byte Reserved;
};
struct BMPINFO
{
struct BMPHEADER header; //description this structure is missed
struct RGB color[256];
}info;
description of this structure is omitted
Let's assume that you need to get the contents of the variable Red
of the tenth color member, which can be written:
AL=info.color[10].Red;
There is one limit on the use of embedded structures in C--. A
variable cannot be used as an index more than once where referring to
multiple-copy structures. This can be illustrated on the following
example:
struct ABC
{
int a;
int b;
int c;
};
struct
{
struct ABC first[4]; //four copies of structure ABC
int d;
}second[4];
int i,j;
void proc()
{
AX=second[i].first[j].a; //such record will cause report on error, so
//as variable was used in two places
AX=second[2].first[j].a; //but this syntax possible.
AX=second[i].first[3].a;
}
this will produce an error message since the variable is used in two
places but this syntax is valid
8.1.7 Mapping a structure tag to a memory block
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mapping a structure tag to a memory block is an alternate pointer to
the structure.
This alternate way of using pointers to structures lets the
programmer choose the register where the structure address is to be
stored, and to check its integrity and if needed to restore its contents.
The following example illustrates how to map a structure tag to
memory:
struct AA //declare structure tag
{
word a[3]; // first structure member
char b; // second structure member
long c; // third structure member
};
byte buf[256]; //memory block to which structure tag is mapped
void proc1()
{
...
proc2 ( #buf ); // call function and transfer it as a parameter of
// memory block address
...
}
long proc2 (unsigned int pointer_to_mem)
{
int i;
BX=pointer_to_mem; // load memory block address to BX
FOR(i=0; i<3; i++){ // write -1 to array of member 'a'
BX.AA.a[i]=-1;
}
BX.AA.b=0;
ES:BX.AA.c=EAX;
return BX.AA.c; // return contents of member 'c'
}
In 16-bit mode registers BX, DI, SI and BP can be used to store the
address of a structure, but it is best to use BX. Registers DI and SI
can be used to calculate the address of multi-member objects. Register BP
is used to work with local and parametric variables. In 32-bit mode any
register can be used except ESP and EBP, but registers EDI and ESI must
be used with caution.
8.1.8 Bit fields in structures
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Bit fields of structures are used to save memory since they can be
used to pack values tightly and to organize convenient access to
registers of external devices in which different bits can have
independent functionality.
Bit fields are declared using the following syntax:
<type> [<identifikator>]:<constanta>;
for example:
int var:5; //declare bit field of size 5 bits named 'var'
A bit field consists of a certain number of bits which is assigned a
numeric expression <constant>. This value must be a positive integer and
its value cannot except the number of bytes corresponding to the <type>
of the defined bit field. In C-- bit fields can contain only unsigned
values. Arrays of bit fields or pointers to bit fields cannot be used.
The <identifier> names the bit field and is required. An unnamed bit
field is a gap corresponding to the number of bits before the position of
the next structure member. An unnamed bit field for which zero size is
specified has a special purpose: it guarantees that memory for the next
bit field will begin at the boundary of the type specified for the
unnamed bit field, i. e., the bit field will be aligned to 8/16/32 bits.
In C-- all bit fields are packed one after the other despite the
boundaries of identifier types. If the next field is not a bit field, the
bits remaining to the boundary of the byte will not be used. The maximum
size of a bit field is 32 bits for type 'dword' or 'long', 16 bits for
type 'word' or 'int', and 8 bits for type 'byte' or 'char'. Bit fields
can be combined, or used in the operater 'union'. Doing 'sizeof' a bit
field returns the size of the field in bits. When a bit field is used,
its contents will be extended to the register, along with an unsigned
integer.