9.1. Introduction
There is no doubt that the Standard Committee's decision to define a set of library routines will prove to be a huge benefit to users of C. Previously there were no standard, accepted, definitions of library routines to provide support for the language. As a result, portability suffered seriously.
The library routines do not have to be present; they will only be present in a hosted environment—typically the case for applications programmers. Writers of embedded systems and the writers of the hosted environment libraries will not have the libraries present. They are using ‘raw’ C, in a freestanding environment, and this chapter will not be of much interest to them.
The descriptions (except for this introduction) are not meant to be read as a whole chapter, but as individual pieces. The material included here is meant more for information and convenient reference than as a full tutorial introduction. It would take a full book by itself to do real justice to the libraries.
9.1.1. Headers and standard types
A number of types and macros are used widely by the library
functions. Where necessary, they are defined in the
appropriate #include
file for that function. The header
will also declare appropriate types and prototypes for the
library functions. Some important points should be noted
here:
- All external identifiers and macro names declared in any of the library headers are reserved. They must not be used, or redefined, for any other purpose. In some cases they may be ‘magic’—their names may be known to the compiler and cause it to use special methods to implement them.
- All identifiers that begin with an underscore are reserved.
- Headers may be included in any order, and more than once, but must be included outside of any external declaration or definition and before any use of the functions or macros defined inside them.
- Giving a ‘bad value’ to a function—say a null pointer, or a value outside the range of values expected by the function—results in undefined behaviour unless otherwise stated.
The Standard isn't quite as restrictive about identifiers as the list above is, but it's a brave move to make use of the loopholes. Play safe instead.
The Standard headers are:
<assert.h> <locale.h> <stddef.h> <ctype.h> <math.h> <stdio.h> <errno.h> <setjmp.h> <stdlib.h> <float.h> <signal.h> <string.h> <limits.h> <stdarg.h> <time.h>
A last general point is that many of the library routines
may be implemented as macros, provided that there will be no
problems to do with side-effects (as Chapter 7 describes).
The Standard guarantees that, if a function is normally
implemented as a macro, there will also be a true function
provided to do the same job. To use the real function,
either undefine the macro name with #undef
, or enclose its
name in parentheses, which ensures that it won't be treated
as a macro:
some function("Might be a macro\n"); (some function)("Can't be a macro\n");
9.1.2. Character set and cultural dependencies
The Committee has introduced features that attempt to cater for the use of C in environments which are not based on the character set of US ASCII and where there are cultural dependencies such as the use of comma or full stop to indicate the decimal point. Facilities have been provided (see Section 9.4) for setting a program's idea of its locale, which is used to control the behaviour of the library functions.
Providing full support for different native languages and customs is a difficult and poorly understood task; the facilities provided by the C library are only a first step on the road to a full solution.
In several places the ‘C locale’ is referred to. This is the only locale defined by the Standard and effectively provides support for the way that Old C worked. Other locale settings may provide different behaviour in implementation-defined ways.
9.1.3. The <stddef.h> Header
There are a small number of types and macros, found in
<stddef.h>
, which are widely used in other headers.
They are described in the following paragraphs.
Subtracting one pointer from another gives a result whose
type differs between different implementations. To allow
safe use of the difference, the type is defined in
<stddef.h>
to be ptrdiff_t
. Similarly, you
can use size_t
to store the result of sizeof
.
For reasons which still escape us, there is an ‘implementation
defined null pointer constant’ defined in <stddef.h>
called NULL
. Since the language explicitly
defines the integer constant 0
to be the value which can be
assigned to, and compared with, a null pointer, this would
seem to be unnecessary. However, it is very common practice
among experienced C programmers to write this sort of thing:
#include <stdio.h> #include <stddef.h> FILE *fp; if((fp = fopen("somefile", "r")) != NULL){ /* and so on */
There is also a macro called offsetof
which can be used to
find the offset, in bytes, of a structure member. The
offset is the distance between the member and the start of
the structure. It would be used like this:
#include <stdio.h> #include <stdlib.h> #include <stddef.h> main(){ size_t distance; struct x{ int a, b, c; }s_tr; distance = offsetof(s_tr, c); printf("Offset of x.c is %lu bytes\n", (unsigned long)distance); exit(EXIT_SUCCESS); }Example 9.1
The expression s_tr.c
must be capable of evaluation as an
address constant (see Chapter 6). If the member whose
offset you want is a bitfield, then you're out of luck;
offsetof has undefined behaviour in that case.
Note carefully the way that a size_t
has to be cast to the
longest possible unsigned type to ensure that not only is the argument
to printf
of the type that it expects (%lu
is
the format string for unsigned long
), but also no precision
is lost. This is all because the type of size_t
is not
known to the programmer.
The last item declared in <stddef.h>
is
wchar_t
, an integral type large enough to hold a wide
character from any supported extended character sets.
9.1.4. The <errno.h> Header
This header defines errno along with the macros EDOM
and
ERANGE
, which expand to nonzero integral constant
expressions; their form is additionally guaranteed to be
acceptable to #if
directives. The latter two are used by
the mathematical functions to report which kind of errors
they encountered and are more fully described later.
errno
is provided to tell you when library functions have
detected an error. It is not necessarily, as it used to be,
an external variable, but is now a modifiable lvalue that
has type int
. It is set to zero at program start-up, but
from then on never reset unless explicitly assigned to; in
particular, the library routines never reset it. If an
error occurs in a library routine, errno is set to a
particular value to indicate what went wrong, and the
routine returns a value (often −1) to indicate that it
failed. The usual use is like this:
#include <stdio.h> #include <stddef.h> #include <errno.h> errno = 0; if(some_library_function(arguments) < 0){ /* error processing code... */ /* may use value of errno directly */
The implementation of errno
is not known to the programmer,
so don't try to do anything other than reset it or inspect
its value. It isn't guaranteed to have an address, for
example.
What's more, you should only check errno
if the particular
library function in use documents its effect on errno
.
Other library functions are free to set it to arbitrary values after a call unless their description explicitly states what they do with it.