United States |
Previous | Contents | Index |
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 ISO/IEC CD 9899 (SC22 N2620), otherwise known as
CD1 of C9x, the in-progress revision to the ANSI/ISO C standard,
Compaq 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.)
4.9.1 Current Object
C9x 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.
4.9.2 Designations
The C9x 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.
4.9.3 Examples
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 */ |
A C program is a collection of user-defined and system-defined functions. Functions provide a convenient way to break large computing tasks into smaller ones, which helps in designing modular programs that are easier to understand and maintain. A function contains zero or more statements to be executed when it is called, can be passed zero or more arguments, and can return a value.
This chapter discusses the following information about C functions:
A function call is a primary expression, usually a function identifier followed by parentheses, that is used to invoke a function. The parentheses contain a (possibly empty) comma-separated list of expressions that are the arguments to the function. The following is an example of a call to the function power , assuming this function is appropriately defined:
main() { . . . y = power(x,n); /* function call */ } |
See Section 6.3.2 for more information on function calls.
5.2 Function Types
A function has the derived type "function returning type". The type can be any data type except array types or function types, although pointers to arrays and functions can be returned. If the function returns no value, its type is "function returning void ", sometimes called a void function. A void function in C is equivalent to a procedure in Pascal or a subroutine in FORTRAN. A non-void function in C is equivalent to a function in these other languages.
Functions can be introduced into a program in one of two ways:
int power(int base, int exp) { int n=1; if (exp < 0) { printf ("Error: Cannot handle negative exponent\n"); return -1; } for ( ; exp; exp--) n = base * n; return n; } |
main() { int power(int base, int exp); /* function declaration */ int x, n, y; . . . y = power(x,n); /* function call */ } |
A function definition includes the code for the function. Function definitions can appear in any order, and in one source file or several, although a function cannot be split between files. Function definitions cannot be nested.
A function definition has the following syntax:
function-definition:
declaration-specifiersopt declarator declaration-listopt compound-statement |
declaration-specifiers
The declaration-specifiers (storage-class-specifier, type-qualifier, and type-specifier) can be listed in any order. All are optional.By default, the storage-class-specifier is extern . The static specifier is also allowed. See Section 2.10 for more information on storage-class specifiers.
ANSI allows the type-qualifier to be const or volatile , but either qualifier applied to a function return type is meaningless, because functions can only return rvalues and the type qualifiers apply only to lvalues.
The type-specifier is the data type of the value returned by the function. If no return type is specified, the function is declared to return a value of type int . A function can return a value of any type except "array of type" or "function returning type". Pointers to arrays and functions can be returned. The value returned, if any, is specified by an expression in a return statement. Executing a return statement terminates function execution and returns control to the calling function. For functions that return a value, any expression with a type compatible with the function's return type can follow return using the following format:
return expression;
If necessary, the expression is converted to the return type of the function. Note that the value returned by a function is not an lvalue. A function call, therefore, cannot constitute the left side of an assignment operator.
The following example defines a function returning a character:
char letter(char param1) { . . . return param1; }The calling function can ignore the returned value. If no expression is specified after return , or if a function terminates by encountering the right brace, then the return value of the function is undefined. No value is returned in the case of a void function.
If a function does not return a value, or if the function is always called from within a context that does not require a value, a return type of void should be specified:
void message() { printf("This function has no return value."); return; }Specifying a return type of void in a function definition or declaration generates an error under the following conditions:
- If the function attempts to return a value, an error occurs at the offending return statement.
- If the void function is called in a context that requires a value, an error occurs at the function call site.
declarator
The declarator specifies the name of the function being declared. A declarator can be as simple as a single identifier, such as f1 in the following example:
int f1(char p2)In the following example, f1 is a "function returning int ". A declarator can also be a more complex construct, as in the following example:
int (*(*fpapfi(int x))[5])(float)In this example, fpapfi is a "function (taking an int argument) returning a pointer to an array of five pointers to functions (taking a float argument) returning int ". See Chapter 4 for information on specific declarator syntax.
The declarator (function) need not have been previously declared. If the function was previously declared, the parameter types and return type in the function definition must be identical to the previous function declaration.
The declarator can include a list of the function's parameters. In Compaq C, up to 253 parameters can be specified in a comma-separated list enclosed in parentheses. Each parameter has the auto storage class by default, although register is also allowed. There is no semicolon after the right parenthesis of the parameter list.
There are two methods of specifying function parameters:
- The new or prototype style, which includes a parameter type list. For example:
int f1(char a, int b) { function body }- The old style, which includes an identifier list; the parameter types are defined in a separate declaration-list within the function definition, before the left brace that begins the function body. For example:
int f1(a, b) char a; int b; { function body }
Any undeclared parameters are assumed to be of type int .A function definition with no parameters is defined with an empty parameter list. An empty parameter list is specified in either of two ways:
- Using the keyword void if the prototype style is used. For example:
char msg(void) { return 'a'; }- Using empty parentheses if the old style is used. For example:
char msg() { return 'a'; }A function defined using the prototype style establishes a prototype for that function. The prototype must agree with any preceding or following declarations of the same function.
A function defined using the old style does not establish a prototype, but if a prototype exists because of a previous declaration for that function, the parameter declarations in the definition must exactly match those in the prototype after the default argument promotions are applied to the parameters in the definition.
Avoid mixing old style and prototype style declarations and definition for a given function. It is allowed but not recommended.
See Section 5.6 for more information on function parameters and arguments. See Section 5.5 for more information on function prototypes.
compound-statement
The compound-statement is the group of declarations and statements surrounded by braces in a function or loop body. This compound statement is also called the function body. It begins with a left brace ({) and ends with a right brace (}), with any valid C declarations and statements in between. One or more return statements can be included, but they are not required.
Previous | Next | Contents | Index |
|