![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Sizes of Objects This chapter describes the sizes of both scalar and composite objects. The first section discusses concepts of size that apply to all object types but are most important for the composite types. The remaining section discusses individual types:
Concepts for Object SizesThis section describes the terminology you use to discuss object sizes and provides an overview of how storage size is determined.
Minimum, Default, Packed, and Unpacked Sizes
Use the following terms to describe the size of an object:
- Storage unit —— Smallest addressable memory unit. The size of the storage unit in bits is given by the named number System.Storage_Unit. Because all supported targets are byte-addressable, the size of the storage unit is 8.
- Minimum size for a type —— The minimum number of bits required to store the largest value of the type. For example, the minimum size of a Boolean is 1.
- Maximum size for a type —— The largest allowable size for a discrete type. The maximum size is 32 (32-bit machine) or 64 (64-bit machine).
- Packed size for a type —— The size of a component used in an array or record when a pragma Pack is in effect. This is the same as the minimum size unless modified by a 'Size clause.
- Unpacked size for a type —— The size of a component used in an array or record when no pragma Pack is in effect. This is the same as the default size unless modified by a 'Size clause. For more information see Determination of Storage Size.
- Default size for a type —— The smallest number of bits required to store the largest value of the type when stored in whole storage units. For composite types, the default sizes are multiples of 8. The possible default sizes for scalar, access, and task types appear in Table 8 . Note that the maximum size is dependent on your system (32-bit or 64-bit)
Table 6 Default Sizes for Types
Type Size in bits
Integer 8, 16, 32, 64
Enumeration 8, 16, 32
Fixed 32, 64
Float 32, 64
Access 32, 64
Task 32, 64
Determination of Storage Size
Top-level scalar and access objects are stored using their unpacked size. (A top-level object is an object that is not a component of any array or record). Components of composite objects that do not have either pragma Pack or a record representation clause are also stored using the unpacked size.
Components of composite objects having pragma Pack are stored using the packed size.
Fields of records having record representation clauses can be stored in any number of bits ranging from the minimum size to the default size of the field type. If a scalar- or composite-type component field is specified to be smaller than the default size, a filler field is introduced and the data is left-justified. Restrictions on Representation Specifications can be found in the Ada Compiler Reference.
Sizes for Specific TypesThis section describes the sizes and storage for the following types:
Discrete Types
'Size clauses for discrete types affect sizes by changing the packed and unpacked sizes. When there is no 'Size clause, the packed and unpacked sizes are the minimum and default sizes, respectively. 'Size clauses with values outside the minimum and maximum sizes cause a semantic error. Within that range, two cases depend on the value specified by the clause:
- Value <= default size —— The packed size is set equal to value; the unpacked size is not affected.
- Value > default size —— The packed size is set equal to value; the unpacked size is set to the number of bits in the smallest number of storage units that will hold the packed size.
Size examples appear in Table 7.
Note: Actual sizes vary by the compiler variant being used. See the "Declarations and Types" section in the Ada Compiler Reference for sizes specific to the compiler variant you are using.
Integer Types
The default size of a first-named integer subtype is 32.
The 'Size clause is supported for non-derived and derived integer types. The effect of a 'Size clause on minimum size is shown in the following example:
type Byte is range 0 .. 255; for Byte'Size use n;
where n is a static integer expression.
Table 8 shows the effect of n on the packed and unpacked sizes.
Table 8 Example of Effect of 'Size Clause
'Size clause Packed size Unpacked size
No 'Size clause 8 32
Use 8 8 32
Use 12 12 32
Enumeration Types
For an enumeration type with n elements, the default internal integer representation ranges from 0 to n-1.
Enumeration and length clauses are permitted on derived types. However, this might generate additional code when parent/derived types are converted to each other.
For predefined type Character, the value returned by the 'Size attribute is 8, and the minimum size is 8. User-defined character types are like ordinary enumeration types and can have a minimum size that is less than 8.
Length clauses are supported for both nonderived and derived enumeration types. The effect of a length clause on representation is shown in the following example:
type Response is (No, Maybe, Yes); for Response'Size use n;
where n is a static integer expression.
Table 9 lists the packed and unpacked sizes for different values of n.
Table 9 Example of Enumeration Type Sizes
Length clause Packed size Unpacked size
No length clause 2 8
Use 4 4 8
Use 12 12 16
Use 16 16 16
Use 20 20 32
Use 32 32 32
Floating-Point Types
The internal representations for floating-point types are the 32-bit and 64-bit floating-point representations as defined by the host architecture.
Fixed-Point Types
Fixed-point types are represented internally as integers. The integer representation is computed by scaling (dividing) the fixed-point number by the actual small implied by the fixed-point type declaration. Actual small is defined to be the nearest power of 2 that is greater than the smallest possible value of the fixed-point type. The values that are exactly representable are those that are precise multiples of the actual small; numbers between those values are represented by the closest exact multiple. For example, in the declaration:
type fix is delta 0.01 range -10.0 .. 10.0;
the integer value used to represent the lower bound of the type is .0 / (1/128), or , because the actual small, representing 0.01, is 1/128. In the example:
type fix is delta 0.01 range -10.6 .. 10.6;
the integer value used to represent the lower bound of the type is , which is the closest exact multiple of the actual small. (This is .6/(1/128) = .8; the nearest integer representation is .)
The size of the representation is 8, 16, 32, or 64.
'Size and 'Small are supported for both nonderived and derived types. The value given in a 'Small clause for a fixed type must be a positive static real number. The value need not be a power of 2. By Ada rules, it cannot be greater than the delta of the base type.
Access Types and Task Types
Access and task objects have a size of 32 bits or 64 bits, depending on your system. The 'Storage_Size length clause is allowed for access and task types. The value given in a 'Storage_Size clause can be any integer expression, and it is not required to be static. Static expressions larger than Integer'Last will generate compilation warnings; however, a Constraint_Error exception will be raised at run time.
For access types, the 'Storage_Size clause is used to specify the size of the access type's collection. If a 'Storage_Size clause has been applied to an access type, the collection is nonextensible. For task types, the clause determines the stack size.
For access types, memory allocation for the collection is controlled by pragma Collection_Policy. For more information about pragma Collection_Policy, refer to the Ada Compiler Reference.
A value (either static or not) of 0 is allowed. In this case, no collection or task stack space will be allocated, and a Storage_Error exception will be raised at run time if any attempt is made to allocate or deallocate from the collection or activate the task. Negative values are also allowed by access types; however, these generate a Storage_Error exception when the type is elaborated, even if no attempt is made to allocate or deallocate objects belonging to the collection.
Record Types
In the absence of a record representation clause, a record type has two basic representations: unpacked and packed.
In the unpacked representation, each record component starts on a storage-unit boundary, and alignment filler may be introduced between components to cause the components to be aligned properly. For example, an integer component will be aligned so that it starts on a longword boundary.
In the packed representation, a component whose size is less than 32 bits will occupy exactly the number of bits in its minimum size; such components might not start on storage-unit boundaries. At present, alignment filler is not eliminated in the packed representation.
The criteria for selecting which representation to use are the same as described in Array Types.
In either case, may lay out the record fields in a different order than that used in the type declaration for the record. This is done in an attempt to satisfy alignment requirements without introducing unnecessary alignment filler. When a convention of C is specified for a record type (pragma Convention), the layout of the record is C-compatible (i.e. the same layout as would be generated by the C compiler for a corresponding C struct). In particular, the compiler does not reorder fields in this case.
If a record representation clause is present, it may mention some or all of the fields in the record. Those fields mentioned in the clause will be laid out according to its rules; the remaining fields are then laid out according to the default algorithms, starting at the first storage unit past the last field mentioned in the clause. This is the case even if the clause leaves holes that are big enough to contain some of the fields not mentioned.
In a discriminated record, this rule has an important consequence: If one of the discriminants is not mentioned in the clause, it will be placed after all of the fields in the largest variant part (as specified by the clause). Even though some of the variant parts are smaller than others, constrained copies of the record selecting those variants will be as large as copies of the record with the largest variant.
A record representation clause cannot mention a field whose size is not known at compile time (this includes fields whose size depends on a discriminant).
Unconstrained records with default values are given the maximum sizes for objects of the values' types.
Array Types
For a given array type, Apex is capable of using one of two representations, known as the unpacked and packed representations.
In the unpacked representation, each array component starts on a storage-unit boundary, and filler (of up to three storage units) can be introduced between components to cause them to be aligned properly. This alignment filler is sometimes needed when the component is a record type or contains record types.
The packed representation for an array type differs from the unpacked representation if the type satisfies one of the following mutually exclusive requirements:
- The minimum size of the component type is less than 32 bits. In the packed representation, each component will occupy exactly the number of bits in its minimum size; this means that components might not start on storage-unit boundaries. If the last storage unit of a packed array of this type contains unused bits, will cause these bits, which are called tail filler, to be zeroed. This permits comparison and copy operations on the array to be performed efficiently using block operations.
- Alignment filler is used in the unpacked representation. In the packed representation, the alignment filler is omitted; however, each component still starts on a storage-unit boundary.
If neither of the two situations above holds, the packed representation is the same as the unpacked representation.
Rational Ada uses the unpacked representation when:
- An array type has neither pragma Pack nor a length clause.
- A length clause is applied to the array type, pragma Pack is not used, and the unpacked representation results in a size less than or equal to that specified in the length clause.
uses the packed representation when:
- Pragma Pack is applied to the array type.
- A length clause is applied to the array type, pragma Pack is not used, and the unpacked representation would result in a size greater than that specified in the length clause.
The length clause specifies only an upper bound for the size of the array; the packed representation might result in a size smaller than the size given in the length clause.
In any case, if the packed representation is too large for an explicit length clause, a compilation error results.
Change of representation is supported for arrays. Therefore, pragma Pack on a derived array type is honored, and length clauses on derived array types are permitted.
Array Storage
The elements of an array are stored in contiguous memory locations with the first array component stored at the lowest address. Components of an array can have gaps between them as described above.
For example, one-dimensional arrays of type A1 are laid out as shown in Table 10 :
type A1 is array (1 .. N) of T;
Table 10 Storage Layout for a One-Dimensional Arra
A1(1)
A1(2)
...
A1(N)
Similarly, two-dimensional arrays of type A2 have the layout shown in Table 11:
type A2 is array (1 .. N, 1 .. M) of T;
Table 11 Storage Layout for a Two-Dimensional Array
A2(1,1)
A2(1,2)
...
A2(1,M)
A2(2,1)
...
A2(N,1)
...
A2(N,M)
Dope Vectors
Associated with every array is information about its size and the bounds of each of its dimensions. This information is represented in the form of a dope vector (sometimes called an array descriptor). For constrained array types whose size is known at compile time, the dope vector is an internal construct that has no consequences for the representation of the array itself and A'Dope_Address returns zero.
However, when an array of an unconstrained type is used in the following way, the array's dope vector is included as part of the array's representation and A'Dope_Address returns a nonzero System.Address:
The array's type is the designated type of an access type. For example:
type S is access String; -- String is an unconstrained array type X : S := new String (1 .. 17); -- Allocate a string with bounds 1..17 Y : S := new String (1 .. 36); -- Allocate a string with bounds 1..36
As shown, each of the arrays allocated in the access type's collection can have different bounds. Consequently, a dope vector is needed to describe each array.
The dope vector for an array contains bounds and size information for each of its dimensions. For each dimension, this information is represented in
3* Long_Integer'Size bit triples. Thus, if an array A has N dimensions, the triple for a given dimension K (K < N) contains:
- The size (in bits) of the subarray formed by dimensions K+1 .. N, stored in the first Long_Integer'Size bits of the triple. The informal notation A'Size(K+1) is used to express this subarray size.
- The value of A'First(K), stored in the second Long_Integer'Size bits of the triple.
- The value of A'Last(K), stored in the remaining Long_Integer'Size bits of the triple.
Rational Software Corporation http://www.rational.com support@rational.com techpubs@rational.com Copyright © 1993-2002, Rational Software Corporation. All rights reserved. |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |