United States |
Previous | Contents | Index |
An object of a given data type is stored in a section of memory having a discreet size. Objects of different data types require different amounts of memory. Table 3-2 shows the size and range of the basic data types.
Type | Size | Range |
---|---|---|
Integral Types | ||
short int , or signed short int | 16 bits | --32768 to 32767 |
unsigned short int | 16 bits | 0 to 65535 |
int or signed int | 32 bits | --2147483648 to 2147483647 |
unsigned int | 32 bits | 0 to 4294967295 |
long int , or signed long int (OPENVMS) | 32 bits | --2147483648 to 2147483647 |
long int , or signed long int (TRU64 UNIX) | 64 bits | --9223372036854775808 to 9223372036854775807 |
unsigned long int (OPENVMS) | 32 bits | 0 to 4294967295 |
unsigned long int (TRU64 UNIX) | 64 bits | 0 to 18446744073709551615 |
signed __int64 (ALPHA) | 64 bits | --9223372036854775808 to 9223372036854775807 |
unsigned __int64 (ALPHA) | 64 bits | 0 to 18446744073709551615 |
Integral Character Types | ||
char and signed char | 8 bits | --128 to 127 |
unsigned char | 8 bits | 0 to 255 |
wchar_t | 32 bits | 0 to 4294967295 |
Floating-Point Types (range is for absolute value) | ||
float | 32 bits | 1.1 x 10 -38 to 3.4 x 10 38 |
double | 64 bits | 2.2 x 10 -308 to 1.7 x 10 308 |
long double (OPENVMS ALPHA) | 128 bits | 3.4 x 10 -49321 to 1.2 x 10 1049321 |
long double (OPENVMS VAX, TRU64 UNIX) | Same as double | Same as double |
Derived types can require more memory space.
See your platform-specific Compaq C documentation for the sizes of
implementation-defined data types.
3.2 Integral Types
In C, an integral type can declare:
The integral types are:
For Compaq C on OpenVMS systems, storage for int and long is identical. Similarly, storage of signed int and signed long is identical, and storage for unsigned int and unsigned long is identical.
For Compaq C on Tru64 UNIX systems, storage for the int data types is 32 bits, while storage for the long int data types is 64 bits.
The 64-bit integral types signed __int64 and unsigned __int64 are provided on Alpha processors.
For each of the signed integral types, there is a corresponding unsigned integral type that uses the same amount of storage. The unsigned keyword with the integral type modifies the way the integer value is interpreted, which allows the storage of a larger range of positive values. When using the unsigned keyword, the bits are interpreted differently to allow for the increased positive range with the unsigned type (at the expense of the negative range of values). For example:
signed short int x = 45000; /* ERROR -- value too large for short int */ unsigned short int y = 45000;/* This value is OK */ |
The range of values for the signed short int type is --32,768 to 32,767. The range of values for the unsigned short int type is 0 to 65,535.
A computation involving unsigned operands can never overflow, because any result outside the range of the unsigned type is reduced to fit the type by the rules of modulus arithmetic. If the result cannot be represented by the resulting integer type, the result is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type. This means that the low-order bits are kept, and the high-order bits of the mathematical result that do not fit in the type of the result are discarded. For example:
unsigned short int z = (99 * 99999); /* Value of y after evaluation is 3965 */ |
Compaq C treats the plain char type as signed by default for compatibility with VAX C and many other C compilers. However, a command-line option can control this, and a predefined macro can be tested to determine the setting of the option in a given compilation. On Alpha systems, unsigned char might offer some performance advantage for character-intensive processing.
An unsigned integer of n bits is always interpreted in straight unsigned binary notation, with possible values ranging from 0 to 2 n-1 .
The interpretation of signed integers depends on the size of machine representation and the encoding technique used on the machine. With two's-complement representation, signed integers of n bits have a range of --2 n-1 to 2 n-1-1 . |
Character types are declared with the keyword char and are integral types. Using char objects for nonintegral operations is not recommended, as the results are likely to be nonportable. An object declared as a char type can always store the largest member of the source character set.
Valid character types are:
The wide character type wchar_t is provided to represent characters not included in the ASCII character set. The wchar_t type is defined using the typedef keyword in the <stddef.h> header file. Wide characters used in constants or strings must be preceded with an L . For example:
#include <stddef.h> wchar_t a[6] = L"Hello"; |
All char objects are stored in 8 bits. All wchar_t objects are stored as unsigned int objects in 32 bits. The value of a given character is determined by the character set being used. In this text, the ASCII character set is used in all examples. See Appendix C for a complete list of ASCII equivalents, in decimal, octal, and hexadecimal radixes.
To aid portability, declare char objects that will be used in arithmetic as signed char or unsigned char . For example:
signed char letter; unsigned char symbol_1, symbol_2; signed char alpha = 'A'; /* alpha is declared and initialized as 'A' */ |
Strings are arrays of characters terminated by the null
character (\0). Section 1.8.3 has more information on the syntactic
rules of using strings; Chapter 4 has information on declaring
string literals.
3.3 Floating-Point Types
The three floating-point types are:
Use the floating-point types for variables, constants, and function return values with fractional parts, or where the value exceeds the storage range available with the integral types. The following examples show sample floating-point type declarations (and initializations):
float x = 35.69; double y = .0001; double z = 77.0e+10; float Q = 99.9e+99; /* Exceeds allowable range */ |
There are five derived types in C:
The following sections describe these derived types.
A derived type is formed by using one or more basic types in
combination. Using derived types, an infinite variety of new types can
be formed. The array and structure types are collectively called the
aggregate types.
Note that the aggregate types do not include union types, but a union
may contain an aggregate member.
3.4.1 Function Type
A function type describes a function that returns a value of a specified type. If the function returns no value, it should be declared as "function returning void " as follows:
void function1 (); |
In the following example, the data type for the function is "function returning int ":
int uppercase(int lc) { int uc = lc + 0X20; return uc; } |
Chapter 4 discusses declarations in general. Chapter 5 covers
functions specifically, including their declarations, parameters, and
argument passing.
3.4.2 Pointer Type
A pointer type describes a value that represents the address of an object of a stated type. A pointer is stored as an integral value that references the address of the target object. Pointer types are derived from other types, called the referenced type of the pointer. For example:
int *p; /* p is a pointer to an int type */ double *q(); /* q is a function returning a pointer to an object of type double */ int (*r)[5]; /* r is a pointer to an array of five elements */ /* (r holds the address to the first element of the array) */ const char s[6]; /* s is a const-qualified array of 6 elements */ |
The pointer itself can have any storage class, but the object addressed by the pointer cannot have the register storage class or be a bit field. Pointers to qualified or unqualified versions of compatible types have the same representation and alignment requirements as the target type. Pointers to other types need not have the same representation or alignment requirements.
The construction void * designates a generic "pointer to void " type. The void * construction can be used to point to an object of any type, and it is most useful when a pointer is needed to point to the address of objects with different or unknown types (such as in a function prototype). A pointer to void can also be converted to or from a pointer of any other type, and has the same representation and alignment requirements as a pointer to a character type.
A pointer to the address 0 (zero) is called a null pointer. Null pointers are often used to indicate that no more members of a list exist (for example, when using pointers to show the next member of the list). Dereferencing a null pointer with the * or subscripting operators leads to unpredictable and usually very unfavorable results.
See Chapter 4 for details on the syntax of pointer declarations.
3.4.3 Array Type
An array type can be formed from any valid completed type. Completion of an array type requires that the number and type of array members be explicitly or implicitly specified. The member types can be completed in the same or a different compilation unit. Arrays cannot be of void or function type, since the void type cannot be completed and function types are not object types requiring storage.
Typically, arrays are used to perform operations on some homogeneous set of values. The size of the array type is determined by the data type of the array and the number of elements in the array. Each element in an array has the same type. For example, the following definition creates an array of four characters:
char x[] = "Hi!" /* Declaring an array x */; |
Each of the elements has the size of a char object, 8 bits. The size of the array is determined by its initialization; in the previous example, the array has three explicit elements plus one null character. Four elements of 8 bits each results in an array with a size of 32 bits.
An array is allocated contiguously in memory, and cannot be empty (that is, have no members). An array can have only one dimension. To create an array of "two dimensions," declare an array of arrays, and so on.
It is possible to declare an array of unknown size; this sort of declaration is called an incomplete array declaration, because the size is not specified. The following example shows an incomplete declaration:
int x[]; |
The size of an array declared in this manner must be specified elsewhere in the program. (See Section 4.7 for more information on declaring incomplete arrays and initializing arrays.)
Character strings (string literals) are stored in the form of an array of char or wchar_t type, and are terminated by the null character (\0).
An array in C has only one dimension. An array of arrays can be declared, however, to create a multidimensional array. The elements of these arrays are stored in increasing addresses so that the rightmost subscript varies most rapidly. This is called row-major order, and is analogous to a car's odometer. For example, in an array of two arrays declared as int a[2][3]; the elements are stored in this order:
a[0][0], a[0][1], a[0][2], a[1][0], a[1][1], a[1][2] |
A structure type is a sequentially allocated nonempty set of objects, called members. Structures let you group heterogeneous data. They are much like records in Pascal. Unlike arrays, the elements of a structure need not be of the same data type. Also, elements of a structure are accessed by name, not by subscript. The following example declares a structure employee , with two structure variables ( ed and mary ) of the structure type employee :
struct employee { char name[30]; int age; int empnumber; }; struct employee ed, mary; |
Structure members can have any type except an incomplete type, such as the void type or a function type. Structures can contain pointers to objects of their own type, but they cannot contain an object of their own type as a member; such an object would have an incomplete type. For example:
struct employee { char name[30]; struct employee div1; /* This is invalid. */ int *f(); }; |
The following example, however, is valid:
struct employee { char name[30]; struct employee *div1;/* Member can contain pointer to employee structure. */ int (*f)(); /* Pointer to a function returning int */ }; |
The name of a declared structure member must be unique within the structure, but it can be used in another nested or unnested structure or name spaces to refer to a different object. For example:
struct { int a; struct { int a; /* This 'a' refers to a different object than the previous 'a' */ }; }; |
Chapter 4 contains more examples on structures and their declarations.
The compiler assigns storage for structure members in the order of member declaration, with increasing memory addresses for subsequent members. The first member always begins at the starting address of the structure itself. Subsequent members are aligned per the alignment unit, which may differ depending on the member sizes in the structure. A structure may contain padding (unused bits) so that members of an array of such structures are properly aligned, and the size of the structure is the amount of storage necessary for all members plus any padded space needed to meet alignment requirements. See your system's Compaq C documentation for platform-specific information about structure alignment and representation.
A pragma is available to change the alignment of a structure on one platform to match that of structures on other platforms. See Section B.29 for more information on this pragma.
Previous | Next | Contents | Index |
|