54 Subjects Screened
RECOMMENDATIONS FOR FUTURE STUDY:
This macro when invoked will print the expression and its result, as the first instance of the expression is converted to a string constant by the preceding#. For example,
PRINT_DEBUG(x/y);
is expanded to
printf("x/y" " = %g\n", x/y);
Another rather obscure preprocessor operator is ##, which provides a way to concatenate two tokens. For example,
#define TEMP(i) temp ## i
might be used to create different temporary variable names TEMP(1) = TEMP(2);
which, after preprocessing, becomes temp1 = temp2;
The preprocessor defines a number of predefined macros. These are __LINE__, __FILE__, __DATE__,__TIME__,__STDC__, and__STDC_VERSION__. Notice that each of these names is prefixed and suffixed by double underscore characters. Determining the meaning of each of these macros is left as an exercise to the reader.
To put the various aspects of this section together, consider the following macro,
#define PRINT_DEBUG(expr, type) \ printf("File: " __FILE__ \
"\nLine: %d\nExpr: " #expr \
" = %" type##TYPE "\n", __LINE__, (expr))
which, given a set of definitions for formatting different types, e.g.,
#define intTYPE "d"
#define doubleTYPE "f"
can be used as
PRINT_DEBUG(x/y, double);
which will print the source-file name, the statement line number, the expression and its value. Indeed a clever and useful debugging macro.
#ifdef DEBUG
printf("Pointer %#x points to value %f", pd, *pd);
#endif
When writing a program that contains portable code, it is good practice to isolate the non-portable parts in a separate source file. The different code sections for different machines are then enclosed in preprocessor conditions so that only the code for the specified machine is compiled. For example,
#ifdef __WIN32__ /* Code specific to Windows. */
return WaitForSingleObject(handle, 0) == WAIT_OBJECT_0;
#elif defined(__QNX__) || defined(__linux__) /* Code specific to QNX or Linux. */
if(flock(fd, LOCK_EX | LOCK_NB) == -1) return 0;
else return 1;
#endif
A header file should only be included once in any given source file (although they may appear in any number ofdifferent source files in the program). Otherwise certain symbols might obtain multiple definitions, which would result in a compilation error. This problem can occur if some header files include other header files, such that several headers are dependent on a common header file. To prevent the problem of multiple inclusion, the following preprocessor idiom is applied.
Consider a header fileaheader.h, which begins and ends with the preprocessor commands below.
#ifndef A_HEADER_H_
#define A_HEADER_H_
/* Contents of header file is contained here. */
#endif
By prefixing the file with#ifndef A_HEADER_H_, the header is included the first time (presuming A_HEADER_H_is not defined previously), but A_HEADER_H_is subsequently defined in the next line.
This prevents any subsequent inclusion of the header for a given source-file. This idiom is known as a “header guard”.
Chapter 11
Structures and Unions
A structure is a collection of one or more variables, possibly of different types, grouped together under a single name for convenient handling. ... Structures help to organise complicated data, particularly in large programs, because they permit a group of related variables to be treated as a unit instead of as separate entities [KR88, page 127].
The C language provides means to create user-defined types called structures. These types are aggregates of basic types and other user-defined types, and permit a logical grouping of related data. This chapter discusses structures, their operations and implications, and the associated topics of type-definitions and unions.
11.1 Structures
A structure is declared using the keywordstruct, and the internal organisation of the structure is defined by a set of variables enclosed in braces. Consider the following example, which declares a structure for representing two-dimensional points.
struct Point { int x;
int y;
};
Style Note. By convention, structures should always be named with an uppercase first letter.
This distinguishes them from variables and functions (lowercase first letter), and symbolic constants (all uppercase).
The variablesxandyare calledmembers of the structure namedPoint. Variables of typePoint may be defined as a list of identifiers at the end of thestructdefinition,
struct Point { int x;
int y;
} p1, p2, p3; /* Define three variables of type Point. */
or as subsequent definitions using the tag “struct Point”.
struct Point p1, p2, p3; /* Define three variables of type Point. */
When a structure is defined, its members may be initialised using brace notation as follows.
struct Point topleft = { 320, 0 };
Here the memberx is initialised to 320 and the membery to 0. In general, the values within the initialiser list correspond to the structure member variables in order of their declaration.
Individual members of astructmay be accessed via themember operator “.”. For example, struct Point topleft;
topleft.x = 320;
topleft.y = 0;
is an alternative way to initialise the variable topleft. Thus, we might compute the distance between two pointspt1andpt2, using the standard library functionsqrt(), as shown below.
struct Point delta = { pt1.x - pt2.x, pt1.y - pt2.y };
double distance = sqrt((double)delta.x * delta.x + (double)delta.y * delta.y);
Structures can be nested, such that the definition of one structure type may be composed, in part, of another structure type. For example, a rectangle might be described by its top-left and bottom-right corners as follows.
struct Rectangle {
struct Point topleft;
struct Point bottomright;
};
To access the lowest-level members of a variable of typeRectangle, therefore, requires two instances of the member operator.
struct Rectangle rect;
rect.topleft.x = 50;