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.
|