|
|
HP C
|
Previous | Contents | Index |
struct A {...}; /* Definition of external struct A */ { struct A; /* Tentative structure tag declaration. */ /* First declaration of A (in external scope) is hidden. This structure will be defined later */ struct inner { struct A *pointer; /* Declare a structure pointer by */ . /* forward referencing. */ . . }; struct A {...}; /* Tentative declaration of internal struct A is defined here. */ /* External struct A is unaffected by this definition*/ } |
Structures and unions share the following characteristics:
When passing structures as arguments, they might or might not terminate on a longword boundary. If they do not, HP C aligns the following argument on the next longword boundary. |
The difference between structures and unions lies in the way their members are stored and initialized, as follows:
One of the advantages of structures is the ability to pack data into them bit-by-bit.
A structure member often is an object with a basic type size. However, you can also declare a structure member that is composed only of a specified number of bits. Such a member is called a bit field; its length, an integral nonnegative constant expression, is set off from the field name by a colon, as shown by the following syntax:
struct-declarator:
|
Bit fields provide greater control over the structure's storage allocation and allow tighter packing of information in memory. By using bit fields, data can be densely packed into storage.
A bit field's type must be specified (except with unnamed bit fields), and a bit field can have the int, unsigned int, or signed int type. The bit field's value must be small enough to store in an object of the declared size.
In the compiler's default mode, the enum, long, short, and char types are also allowed for bit fields.
A bit field can be named or unnamed. A bit-field declaration without a declarator (for example, :10) indicates an unnamed bit field, which is useful for padding a structure to conform to a specified layout. If the bit field is assigned a width of 0, it indicates that no further bit fields should be placed in the alignment unit, and it cannot name a declarator. Use a colon (:) to separate the member's declarator (if any) from a constant expression that gives the field width in bits. No field can be longer than 32 bits (1 longword).
Since nonbit-field structure members are aligned on at least byte boundaries, the unnamed form can create unnamed gaps in the structure's storage. As a special case, an unnamed field of width 0 causes the next member (normally another field) to be aligned on at least a byte boundary; that is, a bit-field structure member with zero width indicates that no further bit field should be packed into an alignment unit.
The following restrictions apply to the use of bit fields:
Sequences of bit fields are packed as tightly as possible. In C, bit fields are assigned from right to left; that is, from low-order to high-order bit.
To create bit fields, specify an identifier, a colon, and the identifier's width (in bits) as a structure member. In the following example, three bit fields are created in the structure declaration:
struct { unsigned int a : 1; /* Named bit field (a) */ unsigned int : 0; /* Unnamed bit field = 0 */ unsigned int : 1; /* Unnamed bit field */ } class; |
The first and third bit fields are one bit wide, the second is zero bits wide, which forces the next member to be aligned on a natural or byte boundary.
Bit fields (including zero-length bit fields) not immediately declared after other bit fields have the alignment requirement imposed by their type, but never a lesser alignment requirement than that of int. In a declaration of a bit field that immediately follows another bit field, the bits are packed into adjacent space in the same alignment unit, if sufficient space remains; otherwise, padding is inserted and the second bit field is put into the next alignment unit.
See your HP C documentation for platform-specific information on bit-field alignment within a structure.
All structures can be initialized with a brace-enclosed list of component initializers. Structures with automatic storage class can also be initialized by an expression of compatible type.
Initializers are assigned to components on a one-to-one basis. If there are fewer initializers than members for a structure, the remaining members are initialized to 0. Listing too many initializers for the number of components in a structure is an error. All unnamed structure or union members are ignored during initialization.
Separate initializing values with commas and delimit them with braces { }. The following example initializes two structures, each with two members:
struct { int i; float c; } a = { 1, 3.0e10 }, b = { 2, 1.5e5 }; |
The compiler assigns structure initializers in increasing member order. Note that there is no way to initialize a member in the middle of a structure without also initializing the previous members. Example 4-1 shows the initialization rules applied to an array of structures.
Example 4-1 The Rules for Initializing Structures |
---|
#include <stdio.h> main() { int m, n; static struct { char ch; int i; float c; } ar[2][3] = (1) { (2) { (3) { 'a', 1, 3e10 }, { 'b', 2, 4e10 }, { 'c', 3, 5e10 }, } }; printf("row/col\t ch\t i\t c\n"); printf("-------------------------------------\n"); for (n = 0; n < 2; n++) for (m = 0; m < 3; m++) { printf("[%d][%d]:", n, m); printf("\t %c \t %d \t %e \n", ar[n][m].ch, ar[n][m].i, ar[n][m].c); } } |
Key to Example 4-1:
Example 4-1 writes the following output to the standard output:
row/col ch i c ------------------------------------- [0][0]: a 1 3.000000e+10 [0][1]: b 2 4.000000e+10 [0][2]: c 3 5.000000e+10 [1][0]: 0 0.000000e+00 [1][1]: 0 0.000000e+00 [1][2]: 0 0.000000e+00 |
See Section 4.9 for a description of initializers with designations for arrays and structures. |
Unions are initialized with a brace-enclosed initializer that initializes only the first member of the union. For example:
static union { char ch; int i; float c; } letter = {'A'}; |
Unions with the auto storage class may also be initialized with an expression of the same type as the union. For example:
main () { union1 { int i; char ch; float c; } number1 = { 2 }; auto union2 { int i; char ch; float c; } number2 = number1; } |
In conformance with the C99 standard, HP C supports the use of designations in the initialization of arrays and structures. (Note that designations are not supported in the common C, VAX C, and Strict ANSI89 modes of the compiler.)
C99 initializers introduce the concept of a current object and a designation.
The current object is the next thing to be initialized during the initialization of an array or structure.
A designation provides a way to set the current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the object: array elements in increasing subscript order, and structure members in declaration order.
So for an array, the first current object is a[0] when initialization begins; as each initializer is used, the current object is bumped to the next initializer, in increasing subscript order.
Similarly, for a structure, the current object is the first declaration within the structure when initialization begins; as each initializer is used, the current object is bumped to the next initializer, in declaration order.
The C99 Standard allows brace-enclosed initializer lists to contain designations, which specify a new current object. The syntax for a designation is:
designation: designator-list = designator-list: designator designator-list designator designator: [ constant-expression ] . identifier |
A designator within a designation causes the following initializer to begin initialization of the object described by the designator. Initialization then continues forward, in order, beginning with the next object after that described by the designator.
For an array, a designator looks like this:
[ integral-constant-expression ] |
If the array is of unknown size, any nonnegative value is valid.
For a structure, a designator looks like this:
.identifier |
Where identifier is a member of the structure.
The old way of initializing arrays and structures is still supported. However, the use of designators can simplify coding of initializer lists and better accommodate future changes you might want to make to arrays and structures in your application.
int a[5] = { 0, 0, 0, 5 }; // Old way int a[5] = { [3]=5 }; // New way |
typedef struct { char flag1; char flag2; char flag3; int data1; int data2; int data3; } Sx; Sx = { 0, 0, 0, 0, 6 }; // Old way Sx = { .data2 = 6 }; // New way |
int a[10] = { 1, [5] = 20, 10 }; |
a[0]=1 a[1] through a[4] = 0 a[5] = 20 a[6] = 10 a[7] through a[9] = 0 |
typedef struct { char flag1; char flag2; char flag3; int data1; int data2; int data3; } Sx; Sx = { 1, 0, 1, 65, 32, 18 }; // Old way Sx = { .flag1=1, 0, 1, .data1=65, 32, 18 }; // New way |
Sx = { .data1=65, 32, 18, .flag1=1, 0, 1 }; Sx = { .flag1=1, 0, 1, .data1=65, 32, 18 }; |
int a[MAX] = { 1, 3, 5, 7, 9, [MAX - 5] = 8, 6, 4, 2, 0 }; |
struct { int a[3], b } w[] = { [0].a = {1}, [1].a[0] = 2 }; |
w[0].a[0]=1; w[1].a[0]=2; |
struct { int a; struct { int b int c[10] }x; }y = {.x = {1, .c = {[5] = 6, 7 }}} |
y.x.b = 1; y.x.c[5] = 6; y.x.c[6] = 7; |
The following syntax declares the identifier tag as a structure, union, or enumeration tag. If this tag declaration is visible, a subsequent reference to the tag substitutes for the declared structure, union, or enumerated type. Subsequent references of the tag in the same scope (visible declarations) must omit the bracketed list. The syntax of a tag is:
struct tag { declarator-list } |
union tag { declarator-list } |
enum tag { enumerator-list } |
If the tag is declared without the complete structure or union declaration, it refers to an incomplete type. Incomplete enumerated types are illegal. An incomplete type is valid only to specify an object where the type is not required; for example, during type definitions and pointer declarations. To complete the type, another declaration of the tag in the same scope (but not within an enclosed block), defines the content.
The following construction uses the tag test to define a self-referencing structure.
struct test { float height; struct test *x, *y, *z; }; |
Once this declaration is given, the following declaration declares s to be an object of type struct test and sp to be a pointer to an object of type struct test:
struct test s, *sp; |
The keyword typedef can also be used in an alternative construction to do the same thing:
|
In a declaration whose storage-class specifier is typedef, each declarator defines a typedef name that specifies an alias for the stated type. A typedef declaration does not introduce a new type, but only introduces a synonym for the stated type. For example:
typedef int integral_type; integral_type x; |
In the previous example, integral_type is defined as a synonym for int, and so the following declaration of x declares x to be of type int. Type definitions are useful in cases where a long type name (such as some forms of structures or unions) benefits from abbreviation, and in cases where the interpretation of the type can be made easier through a type definition.
A typedef name shares the same name space as other identifiers in ordinary declarators. If an object is redeclared in an inner scope, or is declared as a member of a structure or union in the same or inner scope, the type specifiers cannot be omitted from the inner declaration. For example:
typedef signed int t; typedef int plain; struct tag { unsigned t:4; const t:5; plain r:5; }; |
It is evident that such constructions are obscure. The previous example declares a typedef name t with type signed int, a typedef name plain with type int, and a structure with three bit-field members, one named t, another unnamed member, and a third member named r. The first two bit-field declarations differ in that unsigned is a type specifier, which forces t to be the name of a structure member by the rule previously given. The second bit-field declaration includes const, a type qualifier, which only qualifies the still-visible typedef name t.
The following example shows additional uses of the typedef keyword:
typedef int miles, klicksp(void); typedef struct { double re, im; } complex; . . . miles distance; extern klicksp *metricp; complex x; complex z, *zp; |
All of the code shown in the previous example is valid. The type of distance is int, the type of metricp is a pointer to a function with no parameters returning int, and the type of x and z is the specified structure. zp is a pointer to the structure.
It is important to note that any type qualifiers used with a typedef name become part of the type definition. If the typedef name is later qualified with the same type qualifier, an illegal construction results. For example:
typedef const int x; const x y; /* Illegal -- duplicate qualifier used */ |
Previous | Next | Contents | Index |
|