|
This section summarizes the basic data types, derived data types, enumerated data types, and typedef.Also summarized in this section is the format for declaring variables. DeclarationsWhen defining a particular structure, union, enumerated data type, or typedef, the compiler does not automatically reserve any storage.The definition merely tells the compiler about the particular data type and (optionally) associates a name with it. Such a definition can be made either inside or outside a function or method. In the former case, only the function or method knows of its existence; in the latter case, it is known throughout the remainder of the file. After the definition has been made, variables can be declared to be of that particular data type.A variable that is declared to be of any data type will have storage reserved for it, unless it is an extern declaration, in which case it might or might not have storage allocated (see the section “Storage Classes and Scope”). The language also enables storage to be allocated at the same time that a particular structure, union, or enumerated data type is defined.This is done by simply listing the variables before the terminating semicolon of the definition. Basic Data TypesThe basic Objective-C data types are summarized in Table B.3.A variable can be declared to be of a particular basic data type using the following format: type name = initial_value; The assignment of an initial value to the variable is optional and is subject to the rules summarized in the section “Variables.” More than one variable can be declared simultaneously using the following general format: type name = initial_value, name = initial_value, .. ; Before the type declaration, an optional storage class can also be specified, as summarized in the section “Variables.” If a storage class is specified and the type of the variable is int, then int can be omitted. For example static counter; declares counter to be a static int variable. Table B.3 Summary of Basic Data Types
| | Type | Meaning
| | int | Integer value; that is, a value that contains no decimal point; guaranteed to contain at least 32 bits of accuracy
| | short int | Integer value of reduced accuracy; takes half as much memory as an int on some machines; guaranteed to contain at least 16 bits of accuracy | | long int | Integer value of extended accuracy; guaranteed to contain at least 32 bits of accuracy | | long long int | Integer value of extra-extended accuracy; guaranteed to contain at least 64 bits of accuracy | | unsigned int | Positive integer value; can store positive values up to twice as large as an int; guaranteed to contain at least 32 bits of accuracy | | float | Floating-point value; that is, a value that can contain decimal places; guaranteed to contain at least six digits of precision | | double | Extended accuracy floating-point value; guaranteed to contain at least 10 digits of precision | | long double | Extra-extended accuracy floating-point value; guaranteed to contain at least 10 digits of precision | | char | Single character value; on some systems, sign extension can occur when used in an expression | | unsigned char | Same as char, except it ensures that sign extension will not occur as a result of integral promotion | | signed char | Same as char, except it ensures that sign extension will occur as a result of integral promotion | | _Bool | Boolean type; large enough to store the value 0 or 1 | | float _Complex | Complex number | | double _Complex | Extended accuracy complex number | | long double_Complex | Extra-extended accuracy complex number | | void | No type; used to ensure that a function or method that does not return a value is not used as if it does return one, or to explicitly discard the results of an expression; also used as a generic pointer type (void *) |
|
Note that the signed modifier can also be placed in front of the short int, int, long int, and long long int types. Because these types are signed by default anyway, this has no effect. _Complex and _Imaginary data types enable complex and imaginary numbers to be declared and manipulated, with functions in the library for supporting arithmetic on these types. Normally, you should include the file <complex.h> in your program, which defines macros and declares functions for working with complex and imaginary numbers. For example, a double_Complex variable c1 can be declared and initialized to the value 5 + 10.5i with a statement such as follows: double _Complex c1 = 5 + 10.5 " I; Library routines such as creal and cimag can then be used to extract the real and imaginary parts of c1, respectively. An implementation is not required to support types _Complex and _Imaginary, and it can optionally support one but not the other. Derived Data TypesA derived data type is one that is built up from one or more of the basic data types. Derived data types are arrays, structures, unions, and pointers (which include objects). A function or method that returns a value of a specified type is also considered a derived data type. Each of these, with the exception of functions and methods, is summarized in the following paragraphs. Functions and methods are separately covered in the sections “Functions” and “Classes,” respectively. ArraysSingle-Dimensional Arrays Arrays can be defined to contain any basic data type or any derived data type.Arrays of functions are not permitted (although arrays of function pointers are). The declaration of an array has the following basic format: type name[n] = { initExpression, initExpression, .. }; The expression n determines the number of elements in the array name and can be omitted, provided a list of initial values is specified. In such a case, the size of the array is determined based on the number of initial values listed or on the largest index element referenced if designated initializers are used. Each initial value must be a constant expression if a global array is defined. Fewer values can exist in the initialization list than there are elements in the array, but more cannot exist. If fewer values are specified, only that many elements of the array are initialized— the remaining elements are set to 0. A special case of array initialization occurs in the case of character arrays, which can be initialized by a constant character string. For example char today[] = “Monday”; declares today as an array of characters.This array is initialized to the characters ‘M’,‘o’, 'n', 'd', 'a', 'y', and '\0', respectively. If you explicitly dimension the character array and don’t leave room for the terminating null, the compiler doesn’t place a null at the end of the array: char today[6] = “Monday”; This declares today as an array of six characters and sets its elements to the characters 'M', 'o', 'n', 'd', 'a', and 'y', respectively. By enclosing an element number in a pair of brackets, specific array elements can be initialized in any order. For example int x = 1233; int a[] = { [9] = x + 1, [2] = 3, [1] = 2, [0] = 1 }; defines a 10-element array called a (based on the highest index into the array) and initializes the last element to the value of x + 1 (1234) and the first three elements to 1, 2, and 3, respectively. Variable-Length Arrays Inside a function, method, or block, you can dimension an array using an expression containing variables. In that case, the size is calculated at runtime. For example, the function
int makeVals (int n) { int valArray[n]; ... } defines an automatic array called valArray with a size of n elements, where n is evaluated at runtime and can vary between function calls.Variable-length arrays cannot be initialized. Multidimensional Arrays The general format for declaring a multidimensional array follows: type name[d1][d2]...[dn] = initializationList; The array name is defined to contain d1 x d2 x...x dn elements of the specified type. For example int three_d [5][2][20]; defines a three-dimensional array, three_d, containing 200 integers. A particular element is referenced from a multidimensional array by enclosing the desired subscript for each dimension in its own set of brackets. For example, the statement three_d [4][0][15] = 100; stores 100 into the indicated element of the array three_d. Multidimensional arrays can be initialized in the same manner as one-dimensional arrays. Nested pairs of braces can be used to control the assignment of values to the elements in the array. The following declares matrix to be a two-dimensional array containing four rows and three columns: int matrix[4][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; Elements in the first row of matrix are set to the values 1, 2, and 3, respectively; in the second row they are set to 4, 5, and 6, respectively; and in the third row they are set to 7, 8, and 9, respectively. The elements in the fourth row are set to 0 because no values are specified for that row. The declaration int matrix[4][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; initializes matrix to the same values because the elements of a multidimensional array are initialized in dimension order—that is, from leftmost to rightmost dimension. The declaration int matrix[4][3] = { { 1 }, { 4 }, { 7 } }; sets the first element of the first row of matrix to 1, the first element of the second row to 4, and the first element of the third row to 7.All remaining elements are set to 0 by default. Finally, the declaration int matrix[4][3] = { [0][0] = 1, [1][1] = 5, [2][2] = 9 }; initializes the indicated elements of the matrix to the specified values. StructuresGeneral Format: struct name { memberDeclaration memberDeclaration ...
} variableList; The structure name is defined to contain the members as specified by each memberDeclaration. Each such declaration consists of a type specification followed by a list of one or more member names. Variables can be declared at the time that the structure is defined simply by listing them before the terminating semicolon, or they can subsequently be declared using the following format: struct name variableList; This format cannot be used if name is omitted when the structure is defined. In that case, all variables of that structure type must be declared with the definition. The format for initializing a structure variable is similar to that for arrays. Its members can be initialized by enclosing the list of initial values in a pair of curly braces. Each value in the list must be a constant expression if a global structure is initialized. The declaration struct point { float x; float y; } start = {100.0, 200.0}; defines a structure called point and a struct point variable called start with initial values as specified. Specific members can be designated for initialization in any order with the notation .member = value in the initialization list, as in struct point end = { .y = 500, .x = 200 }; The declaration struct entry { char *word; char *def; } dictionary[1000] = { { “a”, “first letter of the alphabet” }, { “aardvark”, “a burrowing African mammal” }, { “aback”, “to startle” } }; declares dictionary to contain 1,000 entry structures, with the first 3 elements initialized to the specified character string pointers. Using designated initializers, you could have also written it like this: struct entry { char *word; char *def; } dictionary[1000] = { [0].word = “a”, [0].def = “first letter of the alphabet”, [1].word = “aardvark”, [1].def = “a burrowing African mammal”, [2].word = “aback”, [2].def = “to startle” }; or equivalently like this: struct entry { char *word; char *def; } dictionary[1000] = { { {.word = “a”, .def = “first letter of the alphabet” }, {.word = “aardvark”, .def = “a burrowing African mammal”} , {.word = “aback”, .def = “to startle”} }; An automatic structure variable can be initialized to another structure of the same type like this: struct date tomorrow = today; This declares the date structure variable tomorrow and assigns to it the contents of the (previously declared) date structure variable today. A memberDeclaration that has the format type fieldName : n defines a field that is n bits wide inside the structure, where n is an integer value. Fields can be packed from left to right on some machines and right to left on others. If fieldName is omitted, the specified number of bits is reserved but cannot be referenced. If fieldName is omitted and n is 0, the field that follows is aligned on the next storage unit boundary, where a unit is implementation-defined.The type of a field can be int, signed int, or unsigned int. It is implementation-defined whether an int field is treated as signed or unsigned.The address operator (&) cannot be applied to a field, and arrays of fields cannot be defined. UnionsGeneral Format: union name {
memberDeclaration memberDeclaration ...
} variableList; This defines a union called name with members as specified by each memberDeclaration. Each member of the union shares overlapping storage space, and the compiler ensures that enough space is reserved to contain the largest member of the union. Variables can be declared at the time that the union is defined, or they can be subsequently declared using the notation union name variableList; provided the union was given a name when it was defined. It is the programmer’s responsibility to ensure that the value retrieved from a union is consistent with the last value stored inside the union.The first member of a union can be initialized by enclosing the initial value, which, in the case of a global union variable, must be a constant expression, inside a pair of curly braces: union shared { long long int l; long int w[2]; } swap = { 0xffffffff }; A different member can be initialized instead by specifying the member name, as in union shared swap2 = {.w[0] = 0x0, .w[1] = 0xffffffff}; This declares the union variable swap and sets the l member to hexadecimal ffffffff. An automatic union variable can also be initialized to a union of the same type, as in union shared swap2 = swap; PointersThe basic format for declaring a pointer variable is as follows: type *name; The identifier name is declared to be of type “pointer to type,” which can be a basic data type or a derived data type. For example int *ip; declares ip to be a pointer to an int, and the declaration struct entry *ep; declares ep to be a pointer to an entry structure. If Fraction is defined as a class, the declaration Fraction *myFract; declares myFract to be an object of type Fraction—or more explicitly, myFract is used to hold a pointer to the object’s data structure after an instance of the object is created and assigned to the variable. Pointers that point to elements in an array are declared to point to the type of element contained in the array. For example, the previous declaration of ip would also be used to declare a pointer into an array of integers. More advanced forms of pointer declarations are also permitted. For example, the declaration char *tp[100]; declares tp to be an array of 100 character pointers, and the declaration struct entry (*fnPtr) (int); declares fnPtr to be a pointer to a function that returns an entry structure and takes a single int argument. A pointer can be tested to see whether it’s null by comparing it against a constant expression whose value is 0. The implementation can choose to internally represent a null pointer with a value other than 0. However, a comparison between such an internally represented null pointer and a constant value of 0 must prove equal. The manner in which pointers are converted to integers and integers are converted to pointers is machine-dependent, as is the size of the integer required to hold a pointer. The type “pointer to void” is the generic pointer type.The language guarantees that a pointer of any type can be assigned to a void pointer and back again without changing its value. The type id is a generic object pointer.Any object from any class can be assigned to an id variable, and vice versa. Other than these two special cases, assignment of different pointer types is not permitted and typically results in a warning message from the compiler if attempted. Enumerated Data TypesGeneral Format: enum name { enum_1, enum_2, .. } variableList; The enumerated type name is defined with enumeration values enum_1,enum_2,..., each of which is an identifier or an identifier followed by an equals sign and a constant expression.variableList is an optional list of variables (with optional initial values) declared to be of type enum name. The compiler assigns sequential integers to the enumeration identifiers starting at 0. If an identifier is followed by = and a constant expression, the value of that expression is assigned to the identifier. Subsequent identifiers are assigned values beginning with that constant expression plus one. Enumeration identifiers are treated as constant integer values by the compiler. If you want to declare variables to be of a previously defined (and named) enumeration type, you can use the following construct: enum name variableList; A variable declared to be of a particular enumerated type can be assigned only a value of the same data type, although the compiler might not flag this as an error. typedefThe typedef statement is used to assign a new name to a basic or derived data type. The typedef does not define a new type but simply a new name for an existing type. Therefore, variables declared to be of the newly named type are treated by the compiler exactly as if they were declared to be of the type associated with the new name. In forming a typedef definition, proceed as though a normal variable declaration were being made.Then, place the new type name where the variable name would normally appear. Finally, in front of everything, place the keyword typedef. As an example, typedef struct { float x; float y; } POINT; associates the name POINT with a structure containing two floating-point members called x and y. Variables can subsequently be declared to be of type POINT, like so: POINT origin = { 0.0, 0.0 }; Type Modifiers: const, volatile, and restrictThe keyword const can be placed before a type declaration to tell the compiler the value cannot be modified. So, the declaration const int x5 = 100; declares x5 to be a constant integer. (That is, it won’t be set to anything else during the program’s execution.) The compiler is not required to flag attempts to change the value of a const variable. The volatile modifier explicitly tells the compiler that the value changes (usually dynamically). When a volatile variable is used in an expression, its value is accessed each place it appears. To declare port17 to be of type “volatile pointer to char,” you would write this line: char *volatile port17; The restrict keyword can be used with pointers. It is a hint to the compiler for optimization (similar to the register keyword for variables). The restrict keyword specifies to the compiler that the pointer will be the only reference to a particular object—that is, it will not be referenced by any other pointer within the same scope. The lines int * restrict intPtrA; int * restrict intPtrB; tell the compiler that, for the duration of the scope in which intPtrA and intPtrB are defined, they will never access the same value.Their use for pointing to integers (in an array, for example) is mutually exclusive. |