Previous | Table of Contents | Next |
Explicit type conversions are performed by means of casts. A cast is a parenthesized type name that is used as a unary operator. For example, the simple expression
(float)i
converts is value to type float. The cast might be used to force floating-point division:
f = i / (float)j;
(Because floating-point division is performed if either operand is floating point, only one cast is required.)
The type name that appears in a cast is actually a stripped-down declaration, minus the identifier name. An identifierless declarator may appear in the type name in a cast in order to perform pointer conversions, as described in section 3.6.3.
Although the type void has, by definition, no values, it is permissible to cast an expression to void, although the only purpose of doing so is the documentary one of indicating explicitly that a value is being ignored. For example, in the statement
(void)printf(Hello, world!\n);
the return value of printf is explicitly discarded; the (void) cast may additionally disable a zealous compilers warning about the discarded return value. (Of course, a call without the cast would not be incorrect, either.)
In modern C programming, the use of explicit casts is generally discouraged. The existence of function prototypes and void pointers (see sections 3.5.3 and 3.6.6) has removed most of the needs for explicit conversions that arose in traditional C programming. When a cast is used just to shut the compiler up (that is, in an attempt to silence some warning that the programmer believes is extraneous or unimportant), it is usually the case that the resulting code is less portable than it could be, and that another alternative, without an explicit cast, exists and is to be preferred. About the only good remaining uses for explicit casts are in forcing floating-point division (as in the example just above), forcing long arithmetic (as in li = i * (long)j;), performing the occasional explicit conversion to unsigned to prevent sign extension, and performing the occasional explicit pointer conversion when generic pointers are being used (see section 3.10.4.4.1 for an example).
In several circumstances, C requires a constant expression, one that can be evaluated at compile time. Constant expressions are required for array dimensions, case labels (see section 3.4.3), and the initializers for static-duration variables, arrays, and other aggregate variables (i.e., structures and unions). Constant expressions must obviously involve only compile-time constants; they cannot contain any function calls or variable references (even to variables declared const). It follows that the assignment operators and ++ and -- may also not appear. (Section 3.6.3 mentions a few constraints on constant expressions involving pointers.)
Statements are the discrete steps of a C program. By default, simple statements are executed one after the other, but the usual collection of conditional and looping constructs is available and can be used to arrange for more interesting and useful control flow patterns.
Anywhere that a simple statement may appear, it is also possible to put a block of several statements. A block consists of a pair of braces ({}) enclosing zero or more statements. Declarations may appear at the beginning of the block; these declare identifiers are local to that block (and thus have even narrower scope than the local variables in the surrounding function or block).
The simplest and most common statements in C are expression statements. As the name implies, an expression statement consists of an expression; in fact, the only syntactic difference between an expression and an expression statement is that an expression statement is terminated by a semicolon. (The semicolon is a statement terminator in C; it is not a separator as it is in Pascal, for example.)
By the definition of an expression statement, any expression can be made into a statement by putting a semicolon after it. For example, an assignment expression becomes a useful statement:
a = b + c;
A single function call becomes a useful statement, even if nothing is done with its return value, as long as the function does something interesting:
printf(Hello, world!\n);
More generally, an expression statement is useful if it performs (either directly, or indirectly through a function call) at least one side effect. An expression statement that computes a value but does nothing with it, as in
a + b;
is syntactically valid but useless because the computed value is conceptually discarded. (In this case, some compilers would emit code to evaluate the expression and some would not, and some would issue a warning.)
Closely related to the expression statement is the empty or null statement. (In fact, it can be said that an empty statement is an expression statement, if the expression is considered optional.) An empty statement is a single semicolon, used where a statement is syntactically required but there is no work to be done, as in a loop with no body or a label at the end of a block. For example, this loop finds the end of a string:
for(i = 0; string[i] != \0; i++) ;
Previous | Table of Contents | Next |