html article 1 1 ' n' 0 C 1 ' 1' int long char int int typedef struct if switch while 1 1 The Standard Library March 1995 Barry McMullin Introduction It is well known that one should not waste time "re-inventing the wheel" . In Engineering, this means not redesigning something one has already got a perfectly satisfactory design for. In Software Engineering, it means not rewriting software to perform operations that one has previously written (and tested!) software for. In the case of the language there are a range of things that programmers very frequently wish to do, which are so common that standard software for the purpose is actually distributed along with every compiler. This software is called the Standard Library . You are more or less guaranteed that every compiler will come with an implementation of the Standard Library. It is important to be aware at least of the existence of the Standard Library, and to have some outline idea of the software contained in the Library. In this way you can avoid unecessarily writing and testing software which is already effectively available, in a well tested form, for free. Chapter 10 of Illustrating C provides a detailed discussion of the Standard Library. You should consult this, or some equivalent documentation (e.g. the on-line help in the Turbo-C++ IDE), whenever you need detailed information on the full range of functionality of the Standard Library. This essay is intended only to provide an introduction to a very small subset of the Standard Library, and is not a substitute for those more detailed sources of information. Mechanics In order to use the facilities of the Standard Library, one must have some understanding of the mechanics of how the compiler will access it. Some very simple programs can be completely "self-contained" : all the code for all the functions was contained in a single program file. In general, however, this need not be the case; in particular, it will not be the case if you wish to make use of the Standard Library. In principle, of course, the Standard Library could be distributed simply as a set of one or more source code files. You would then use a library function simply by copying the source code out of the relevant library file and pasting it into your own program file. However, this is not the mechanism which is used in practise for a variety of reasons: Since the code for the Standard Library is rarely if ever modified, it is very inefficient to have to recompile or retranslate it every time a programmer makes a change to his own code. The library functions are, in some cases, not actually written in the language at all. The compiler suppliers normally prefer not to distribute source code for the Standard Library as this would facilitate unscrupulous pirating of their work. What is actually done is that the Standard Library software is distributed in a sort of "canned" or precompiled form, as a set of "object files" (or, more commonly, a single "object library" file). Your program file(s) are then compiled completely separately from the source code for the Standard Library, yielding one or more further object files. Finally, all the required "object" files, both your own, and those making up the Standard Library, are combined or linked together to yield the "executable" file, which can actually be run. To a large extent this process can be made automatic and invisible. The compiler compiles your source file(s) to the "object" code form, and then automatically links these together with the any required library object file(s). However, it cannot be made completely automatic. In practise there is certain information, associated with the Standard Library, which the compiler must be made aware of at the time it is compiling your source files, in order to make sure that the object files produced will correctly link up with the object files for the Standard Library. Since, for the most part, the Standard Library simply consists of a set of functions which you can call in your program, the most important such information is the way data is to be exchanged with each such function; i.e. how many parameters does the function take, of what types, in what order, and what type of return value does the function yield (if any)? The compiler could, of course, try to infer this information from the way the function is actually called; but this is not always technically possible, and, in any case, it is much better if the compiler has some independent way of knowing how the function should be called, because then it can automatically check whether you have, in each case, called it correctly. It turns out that this is extremely useful, and is an effective way of automatically detecting a range of very common programming mistakes. The compiler is given this information about how a function should be called by the use of a so-called function prototype . This is simply the "header" of the function definition, which states the function name, the return type, and the formal parameter list. But whereas, in a function definition this is then followed by a compound statement which actually defines the function, in a function prototype it is simply followed by a terminating semicolon. Thus, a prototype might look like this for example: int multiply(unsigned *multiplicand, unsigned multiplier, unsigned *product); This tells the compiler that the function called multiply should take three parameters, repectively of types (unsigned *) , unsigned and (unsigned *) , and will yield a return value of type Function prototypes should appear at the outermost level of a source file -~i.e. not within the definition of any function. So far, so good. It seems that if you wish to call or invoke any of the Standard Library functions in your program,, you must simply insert, somewhere before the function(s) which make such call(s), a suitable function prototype, so that the compiler will then be able to decide whether the calls are correct or not. But how are you going to know what is the correct prototype for each function in the first place? Well, one possibility is to look up the function in the detailed technical documentation for the Standard Libarary: this will normally include the function prototype. You can then copy that into your own file. However, this is clearly unsatisfactory. Apart from being laborious, it is error prone -~and the possibility of an error in the function prototype undermines a primary point of using prototypes in the first place, namely that it allows the compiler to crosscheck for valid function calls! A better idea is if the prototypes are provided to you in a machine readable form -~i.e. in one or more files on the computer. Then you can simply copy the required ones, and paste them into your own source files. Well, yes, this is a major improvement, but still leaves something to be desired. For one thing, duplicating the prototypes in every source program wastes disk space. More seriously, there is always a danger that you might (accidentally) modify a prototype, again confounding the idea of allowing the compiler to automatically crosscheck the prototype against the invocation(s) of the function. A final possibility is to leave the prototypes in one or more separate files; but have the compiler automatically access or scan the relevant files immediately before, or as part of the process of, compiling your files. In this way, there is no laborious, manual, copying of the prototypes, but no duplication and no risk of accidental modification either. Files which are used for this kind of purpose -~which contain no actual executable code, but which contain only function prototypes (and possibly other things such as symbolic constants etc.) which allow the compiler to correctly mesh the file it is compiling with some other software which has been "pre-compiled" , are called header files. Header files are normally given the extension .h to distinguish them from files which actually contain executable code -~function definitions etc. -~which will normally have the extension .c . It would be possible, in principle, to put all the prototypes for all the functions in the Standard Library, plus any other required information (sybolic constants etc.), in a single .h file, and have the compiler automatically include it in compiling any file. However, in practise this is not done for various reasons. For example, most .c files only involve calling a small subset of the functions in the Standard Library; it is then wasteful and time consuming for the compiler to process prototypes for all functions in the Standard Library. The mechanism that is actually used then is as follows. A series of separate .h files are supplied with the compiler. Each one provides prototypes (plus other required information) relating to only some coherent or related subset of the functions in the Standard Library. The programmer must then explicitly instruct the compiler to process just those .h files which are required in order to properly compile any particular .c file. And this is done by inserting, into the .c file one or more so-called include directives. A include directive is something much the same as a define in the sense that it is not handled by the compiler "proper" but by the "pre-processor" which runs (automatically) immediately before the compiler. In this case, the pre-processor processes a include by concatenating together the .h file and the original .c file, to produce a big temporary file which is what is actually then processed in the compilation phase proper. Thus, if you want to use a function from the Standard Library, you must first look up, in the relevant technical documentation, which header file contains the prototype etc. for that function; and then insert a line something like the following in your .c file: include The angle brackets around the name of the header file tell the pre-processor to search for the file in the "standard" directories for such things; normally these will be set up when the compiler is installed, and you, as a programmer, need not worry about what these standard directories actually are. However, in case you wish to actually examine any of the header files, the relevant directory for Turbo-C++, as configured on the machines in the CAE Laboratory is: F: You may, of course, need to include several header files, depending on the particular selection of Standard Library functions you wish to use. All required include directives are normally placed close to the top of your .c file -~typically either at the very top, or immediately after an initial introductory comment which documents the overall contents of the source file. Each include must , in any case, preceed any calls or invocations of the relevant functions. The rest of this essay is concerned with introducing just a very small selection of the several hundred functions normally available in the Standard Library. It is organised into sections according to the distinct .h files required. Implementation-defined Limits: limits.h The header file limits.h essentially just provides define directives defining symbolic constants for the maximum and minimum values allowed with the standard integral data types -~i.e. it does not provide any function prototypoes as such. It is important to be able to refer to these values in your programs in order to prevent, or at least detect, overflow situations. The values potentially differ from one compiler to another (hence "implementation-defined" ), but the symbolic constants defined in limits.h always have the same names. Thus by using the symbolic constants to refer to these limits it should be possible to write your programs in such a way that they will automatically adjust to whatever the limits actually are with any particular compiler. Some constants which are commonly used are: INT : maximum value of INT : minimum value of LONG : maximum value of LONG : minimum value of UINT : maximum value of unsigned int ULONG : maximum value of unsigned long This is not an exhaustive list: examine a copy of limits.h for yourself to see others. However, note that there is, of course, no need for constants called, say, UINT or ULONG since these minima are always guaranteed to be simply zero. Error Conditions: errno.h Many functions in the standard library will detect "exception" or "error" conditions in certain circumstances -~essentially if the function has been requested to do something which, for some reason, it can't. The exact action of the function in such situations depends on the details of the particular function and the particular exception condition. However, the most usual strategy is for the return value from the function to signal, with some special value, that something has gone wrong. It is then up to the calling site to react to this in some "appropriate" way. Minimally this will mean giving some kind of overt external signal of the problem. In any case, while the standard library functions typically provide an initial or gross indication that something has gone wrong via the return value, it is often also useful for the calling site to have access to a more detailed signal which clarifies exactly the nature of the problem. This is usually achieved by having the standard library function record a detailed "error number" in a global variable called errno . This variable is defined within the standard library itself: but your programs can gain access to it by a so-called extern declaration. This declaration is already provided in the header file errno.h ; so if you include this, you will then be able to access errno just as any other variable is accessed. By examining its value immediately after a call to a standard function, your program can generally establish what (if anything) went wrong. As well as the declaration of errno the file errno.h also provides a series of define directives which define symbolic names for the standard error numbers or codes which may be recorded in errno . This would allow your program to test for specific error codes by comparing errno to these symbolic values. We shall also see later how errno can be automatically translated into a corresponding textual error "message" , and, say, displayed, on the computer screen (see the discussion of the function perror() , prototyped in stdio.h ). Utility Functions: stdlib.h The header file stdlib provides prototypes for a selection of miscellaneous "utility" functions, as well as a few more symbolic constants. A small selection of the more commonly used functions is as follows: int atoi(char *s) This takes a string representation of an integer (i.e. a string something like "145" or "-9999" or "000000" etc.) and converts it into the corresponding internal representation of type which is the return value from the function. With the Turbo-C++ implementation of the Standard Library, the behaviour where the answer would overflow (e.g. atoi(100000) ) is indeterminate -~so it is your responsibility to make sure this never arises; if the conversion simply cannot be done (e.g. atoi("xyz") the return value will be 0 . long atol(char *s) This takes a string representation of an integer (i.e. a string something like "145" or "-999999" or "25763178" etc.) and converts it into the corresponding internal representation of type which is the return value from the function. Leading white space in the string is ignored. Again, with the Turbo-C++ implementation of the Standard Library, the behaviour where the answer would overflow (e.g. atol(-500000000) ) is indeterminate; and if the conversion simply cannot be done (e.g. atol("") the return value will be 0L . void exit(int status) This function may be called to forcibly terminate the program at any point. The parameter status is simply a number which is, in some sense, made available to the "external" environment of the program. In the case of programs run under DOS , the exit() status can be accessed via the errorlevel parameter in the DOS if command -~though this is normally only used in batch files. In any case, if you are using exit() to terminate your program, you should normally just give it one of two pre-defined exit values which have been given symbolic names in stdlib.h : use exit(EXIT ) if the program is terminating "normally" and exit(EXIT ) if it is terminating because of some unexpected or intolerable exception or error being encountered. Input and Output: stdio.h stdio.h ( "standard input/output" ) provides basic functions for accessing data "external" to a program. This naturally includes data in files on disk, but also covers data coming from the keyboard, or displayed on the screen, or data routed via any of the other input/output "ports" of the computer. All of these sources or destinations for data may be generically referred to as streams , or, more simply, files . Files are classified into two kinds: text and binary . A text file is divided into "lines" , where each line has zero or more characters, and is terminated by a newline character A binary file is simply a sequence of unprocessed or uninterpreted bytes, with no line organisation superimposed upon it. In general, the terms character and byte can be regarded as almost synonymous here. Files are accessed through data structures called file pointers . Technically, a file pointer is the address of an object of a special type, denoted FILE . This type is defined by the stdio.h header file, and will not be recognised by the compiler unless the header file has been include 'd. A file pointer must be created and associated with each particular external disk file, or input/output port, before any data in that file can be accessed ( "read" or "written" ). This process of creating a file pointer and associating it with a disk file or input/output port is called opening a file, and is performed by the fopen() function described in more detail below. The return value from fopen() is the value of the file pointer, and must be stored in a suitable variable to allow the file to be accessed subsequently. Files are opened for access in a particular mode : reading, writing, or (occasionally) both. The file is treated as a sequence of characters (or, more generally, bytes). When the file is first opened it is positioned at the very start; the contents can then be read or written in sequence until the end of the file. With certain kinds of file (namely those stored on disk) it may be possible to "reposition" the file in an arbitrary way -~go back to the start, or directly to the end, etc. If a file if opened for writing then its previous contents, if any, will normally be lost. A program may have many files open at any given time, each one associated with its own distinct file pointer. File contents can be read, one character at a time, in sequence, with the fgetc() function (provided the file is in read mode); or written with the fputc() function (in write mode). There are also a variety of more sophisticated reading and writing functions such as fprintf() and fscanf() , which typically read or write many characters in one go, and automatically translate between external "text" representations, and internal "binary" representations, for the various native data types ( etc.). When reading a file it is necessary to be able to recognise when the end of the file is encountered. This is signalled by fgetc() yielding a special, reserved, return value, with the symbolic name EOF (this value is also -~somewhat misleadingly -~the return value if fgetc() encounters any kind of error or exception condition). Now this raises a problem: if a special value is reserved to denote the end of file condition, does this mean that this special value can never actually be stored within a file (since, once it is read, it would be mistakenly taken as signalling that the file is ended)? If so, this would be a serious restriction. In practice, this problem is solved in a rather ingenious, but also subtle and confusing manner. A file is treated as a sequence of values of type but the return value from fgetc() is actually made of type Normally this is just an "encoded" or "numerical equivalent" of the value read from the file. But since supports more distinct values than does, it is possible to encode EOF as a value than has no "equivalent" in the data type. In this way, a file can contain any arbitrary values, without restriction. But, in turn, this means that it is very important that before the return value from fgetc() is transformed back into a value (e.g. by assigning it to a variable of type ) it must be tested to see is it EOF . For consistency with this behaviour of fgetc() many other functions in the Standard Library (such as fputc() for example) also use the data type to effectively deal with values, but allowing also for the special EOF value. As I said, this mechanism is quite subtle; in my opinion it may be just a little bit too subtle! It involves the programmer in relying, willy nilly, on automatic conversions between types -~a practice of which I am generally severely critical. Unfortunately, this is now a de facto standardised way of doing things with the Standard Library, and cannot be helped at this stage. Nonetheless, it certainly helps if you at least understand what is going on. Once the program is finished processing a particular file, the file should be closed with the fclose() function. This essentially involves discarding the data structure of type FILE which was associated with the file: it is therefore very important that, once a file is closed, the file pointer which was associated with it is not used again (since it no longer points at anything meaningful!). Files are automatically closed when a program terminates in a "controlled" fashion (either by reaching the end of the main() function, or by an explicit invocation of the exit() function). There are three files which are opened by default, and automatically, for every program. File pointers associated with them are defined in stdio.h , as follows: stdin : The standard input file . By default this is a read mode file, associated with the computer keyboard. However, if the program is invoked from the DOS command line, then this file may be "redirected" to be associated with, say, a disk file, using the DOS input redirection operator < . stdout : The standard output file . By default this is a write mode file, associated with the computer display screen. When running a program under the Turbo-C++ IDE, this corresponds to the so-called User screen , which can be accessed via the Window pull-down menu. Again, however, if the program is invoked from the DOS command line, then this file may be "redirected" to be associated with, say, a disk file, using the DOS output redirection operator > . stderr : The standard error file . This is a write mode file, associated with the computer display screen. It differs from stdout in that it cannot be redirected in any simple way under DOS . A program can do input and output on these three files immediately, without having to call fopen() first. The three files have certain conventional usages, as the names imply. Thus many programs take one input file, or stream of data, and transform it in some way into one output stream of data. If this is the case, the input data would normally be read from stdin , and the output data would be written on stdout . stderr is reserved for signalling "errors" or exception conditions, separate from the "normal" output. Programs written in this way can then be conveniently connected together in "pipelines" , with the output from one being automatically routed as the input to another; in DOS pipelines are set up with the pipe operator | . Thus, for example, suppose we have two programs part1 and part2 which perform two separate transformations on a data stream. The simple ( DOS ) command: D: part1 would cause part1 to be started up, reading its input from the keyboard, and writing its output to the screen. Incidentally, when input is being taken from the keyboard, under DOS , the special key combination CTRL-Z , followed by Enter , is normally used to signal end of file. By contrast, the command: D: part1 out.dat would cause part1 to read its input from the disk file in.dat and write its output to the disk file out.dat . Note that, despite the redirection of stdout , any error or exception messages, written to stderr , would still appear on the screen. Finally, the command: D: part1 out.dat would cause the output of part1 to be automatically routed as the input to part2 , whose output would then finally be routed to out.dat . Thus, writing programs to take input from stdin and write output to stdout (and errors etc. to stderr ) means that they can be subsequently mixed and matched in a very flexible way to achieve a variety of different effects, and is a very powerful idea. But even if you have no intention of linking programs in this way, it will still often be convenient to use stdin as a source of keyboard input, and stdout (and/or stderr ) as a route for output to the screen. There are specialised versions of several stdio.h functions which access one or the other of these files by default (e.g. printf() is a variant of fprintf() which automatically writes to stdout ). A small subset of the functions declared in stdio.h will now be described in more detail. FILE *fopen(char *filename, char *mode) fopen() is used to create a file pointer or stream, for subsequent input or output to a file. Thus, the return value from fopen() must normally be stored in a variable, so that it can then be used as an argument to other functions such as fgetc() etc. filename is a string giving the name of the desired file. This should conform to the conventions of the "environment" or "operating system" under which the program will be running. Under DOS , file names consist, in general, of a device specifier (e.g., D: ), followed by a "path" identifying the desired subdirectory, followed by an individual file name proper. Directory and file names proper consist of a name part of up to 8 characters, followed by a period, followed by an "extension" part of up to three characters. DOS does not distinguish between upper and lower case anywhere in a file name. The directory separator character in DOS is the backslash ; this is a rather unfortunate historical accident, as this character also has a special meaning in strings, as an "escape" character. The nett effect is that, in representing a DOS file name in a string constant, you must write the directory separator as a double backslash: ; note that this actually only results in the internal storage of a single character within the string, when the program is compiled. Examples of well formed filename arguments under DOS might be: "myfile" "yourfile.h" "d:something.xyz" "f: "COM1:" "lpt1:" Of course the filename argument, as with any of the other string arguments to be discussed, might equally be a string "variable" -~the name of a array which had previously been loaded with the desired file name (including the usual nul character as the string terminator: ). mode is a string specifying the desired file "access mode" -~i.e. what kind(s) of operations are going to be carried out on it. Two examples of legal values for this would be: "r" : Open the file for reading. "w" : Open the file for writing. In general, fopen() will open a file for access either as a binary or a text file -~but the default is implementation specific. Under Turbo-C++, this default can be configured by the user. In any case, regardless of the default, the type of access can be explicitly specified in the mode string by adding either a t or b character for text or binary respectively, e.g.: "rt" : Open for reading as a text file. "wb" : Open for writing as a binary file. If the call to fopen() is successful, then the return value is simply the value of the created file pointer. However, if the call fails for any reason (e.g. trying to open a file on device "X:" when no such device is present on the machine) then the return value will be the special zero or null pointer value defined in stdio.h (and, indeed, several of the other header files) define the symbolic name NULL for this value. Thus, after a call to fopen() your program should always check the return value to see whether it is NULL -~and take some appropriate action if so (e.g. issue a suitable message and call exit() ). In any case, if the return value from fopen() is set to NULL then errno will also be set to some more specific error code. int fgetc(FILE *stream) fgetc() reads the next sequential character (byte) from the open file identified by the file pointer stream . This character is converted into an value, and is the return value from the function. However, if the end of file is encountered (the last character has already been read), or if any other error is encountered in reading, then the return value will be EOF , and errno will be set appropriately. In the case of a text file, the newline character should be interpreted as terminating a line. int getchar(void) getchar() is equivalent to fgetc(stdin) . int fputc(int c, FILE *stream) fputc() converts the value c into the corresponding value, and writes that to the next sequential position in the open file identified by the file pointer stream . The return value is normally set to be equal to the input argument c ; however, if any error is encountered then the return value will be EOF and errno will be set appropriately. int putchar(int c) putchar(c) is equivalent to fputc(c, stdout) . int fprintf(FILE *stream, char *format, ...) fprintf() is a "print-and-format" function. It provides for transformation of values of any of the native data types into a corresponding textual or string form; this whole string is written to the open file identified by the file pointer stream . fprintf() is actually just one of a whole family of functions which provide minor variations on the basic idea of formatted output. fprintf() takes a variable number of arguments. The arguments stream and format are required -~they must always be present and must be of the types specified in the prototype; but these may then be followed by an arbitrary number (including zero!) of further arguments of arbitrary types. format is a string which indirectly specifies how many further arguments are being passed in (if any) and their types. More precisely, format contains "ordinary" characters interspersed with "conversion specifications" . The ordinary characters are simply written to stream . But each time fprintf() encounters a conversion specification, another input argument is converted into a textual or string form, and output on stream instead. Thus, to simply print a string on the screen (and assuming that stdout has not been redirected) one might use the function call: fprintf(stdout, "Helllooo VietNAM!!!!!!") Conversion specifications are introduced by the character . Examples of some simple conversion specifications are as follows: " representation. " " " " be technically of type (char *) and point at a normal nul-terminated array of characters). In the last two cases, the argument is already essentially textual, so there is strictly no "conversion" to be done as such. There are many other more complex conversion specifications, but they will not be detailed here. Examples of the use of fprintf() might be as follows: fprintf(stdout, "The answer is: It is important to understand that although the value 42 appears in texual form in the statement above, when the program is compiled this is converted into an internal, non-textual, representation. This is why, in order to display this value on the screen again, fprintf() must be used to convert back to a textual representation. In this case the nett effect of this call to fprintf() will be that the string: The answer is: 42. will appear on the screen (assuming stdout has not been redirected). Precisely the same display would, of course, result from the statement: fprintf(stdout, "The answer is: Again, the same display would result from the sequence of statments: x = 7; y = x; x ; y++; fprintf(stdout, "The answer is: where x and y are variables. Similarly, if a , b and c are variables of type and respectively, we might encounter the sequence of statements: a = 32000; b = 32000L * 32000L; c = '!'; fprintf(stdout, " resulting in the following message on the screen: 32000 times 32000 is 1024000000! Note carefully, how the extra arguments in the call to fprintf() must pair off exactly with the conversion specifications in the format argument: i.e. for each conversion specification, in sequence, which appears in the format string, there must be one extra argument, of just the right type. For technical reasons, which will not be explored here, it is not possible for the computer to automatically check that conversion specifications do, in fact, match up with arguments correctly . A mismatch between conversion specifications and arguments in a call to fprintf() , or some related function, is one of the most common kinds of errors encountered in programs -~precisely because the computer cannot automatically detect it. The effects of such errors are completely unpredictable, and may not be immediately apparent at all, so that they can be extremely difficult to track down. You have been warned! Some trivial examples of such mismatching would be as follows: fprintf(stream, "The answer is: fprintf(stream, "Or perhaps it is: fprintf(stream, "Or there again, maybe its fprintf(stream, "My last offer is: Note that if you want to move onto a new line on the display screen, you must explicitly write the newline character to the relevant stream. Try to predict the precise effect of this sequence of statements for example: fprintf(stdout, "Hello. "); fprintf(stdout, "Hello?"); fprintf(stdout, " that you?"); fprintf(stdout, "Yes? ); fprintf(stdout, "My number is: Note that because the character in the format string is interpreted in a special way by fprintf() (namely as introducing a conversion specification), there is a problem if one actually wants to just output this character. To get around this you just put in two successive characters, thus: . The second one will cause fprintf() to recognise that this is not a conversion specification after all, and it will simply output one character without further ado (and without attempting to convert any argument). The return value from fprintf() is the number of characters which have been written out, or EOF if any error has been detected. programmers commonly neglect to check this return value from fprintf() , presumably because error conditions which it can detect and signal are rather rare. Nonetheless, as a general practice I would recommend that you include code in your programs to check this return value (at least to see if it is EOF ), unless the your usage is extremely trivial (e.g. just printing the format string, without having any extra arguments to be converted). int printf(char *format, ...) printf(format, ...) is equivalent to fprintf(stdout, format, ...) . int fscanf(FILE *stream, char *format, ...) fscanf() is more or less the converse to fprintf() : it reads characters from stream and attempts to convert them into "internal" representations of appropriate types, as determined by conversion specifications in the string format . The extra arguments must all be pointer types in this case . Typicllly, these arguments will simply be the addresses of variables (generated with the & operator) into which the converted values are to be stored. fprintf() thus simply stores each converted value at the location pointed at by each extra argument in turn. Again, it is up to you, the programmer, to ensure that the arguments match the conversion specifications correctly; and again, if they do not, then this will not be automatically detected, and will have entirely unpredictable effects. The conversion specifications handled by fscanf() are quite similar in format to those handled by fprintf() . They will not be described further here. The return value from fscanf() is the number of input fields or values successfully scanned, converted, and stored, or EOF if end of file or any error is encountered. In general, it is a very good idea for your programs to check this return value to see that it has the expected value. int scanf(char *format, ...) scanf(format, ...) is equivalent to fscanf(stdin, format, ...) . int fclose(FILE *stream) fclose() simply closes the open file associated with stream . The return value is zero if this is completed successfully; otherwise it is EOF and errno is set. While open files should be closed automatically if or when your program exits anyway, it is still a good practice to explicitly close files with fclose() when your program is finished with them. This makes the behaviour of the program more intelligible to someone reading it, and also provides a little extra "robustness" against the possibility that your program might terminate abnormally (i.e. CRASH!). void perror(char *s) perror(s) outputs the string s on stderr , followed by an error message which describes or elaborates the error code currently stored in errno . It is equivalent to: fprintf(stderr, " message ") where error message is a string describing whatever error condition is represented by the current value of errno . perror() might usefully be called on any occasion when your program detects that a standard library function has failed to operate as expected. Diagnostics: assert.h assert.h provides the declaration of one "function-like" object with the following prototype: void assert(int status) For technical reasons, this object is not implemented as a function in the normal sense, but is what is called a macro instead. However, for most practical purposes, it can be regarded just as any "normal" function. If status is non-zero then assert() will have no effect; but if status is zero then a message of the following form will be output to stderr : Assertion failed: status , file filename , line nnn where status is the value of status (i.e. zero), filename is the name of the source file containing the call to assert() , and nnn is the line number in that file at which the call appears. assert() then causes the program to terminate. assert() is potentially a very useful way of dealing with error or exception conditions detected within your programs, where the condition is such that you are unable (or unwilling) to try to make the program deal with it in any more "intelligent" way. The beauty of assert() is that, if it is activiated, the resulting message immediately pinpoints exactly the position in your source code where the problem is detected. However, note that this benefit will be almost totally lost if you place the invocation of assert() within a function of your own, which you then call from wherever the error is detected: for then the file name and line number output by assert() will always be the same (namely within your error handler) and will not give any indication of the "real" position at which the problem was detected. Character Class Tests: ctype.h ctype.h provides prototypes for a selection of functions which test a value for membership of a given class. In each case the function takes a single argument, which is technically of type int , but which represents a value or, possibly, EOF ; this is for compatibility with fgetc() as discussed earlier. The return value is always of type being zero (false) if the value is not a member of the relevant calss, or non-zero (true) if it is. Some examples are as follows: int isalpha(int c) : Letter (i.e. "alphabetic" )? int isdigit(int c) : Digit? int isalnum(int c) : Letter or digit ( "alphanumeric)? int islower(int c) : Lower case letter? int isupper(int c) : Upper case letter? int isspace(int c) : "Whitespace" (i.e. space, newline, tab, etc.)? int isprint(int c) : Printable or displayable (including space)? ctype.h also provides two functions which convert the case of letters: int tolower(int c) int toupper(int c) String Functions: string.h string.h provides prototypes for a wide range of functions which operate upon strings in various ways. A brief description of a small sample of these follows. Note that where one of these functions potentially modifies a string, it might , in general, make it longer ; therefore in passing in a pointer to the string which will be modified, you must make sure that the array holding this string is big enough to hold the longest possible string (including the nul terminator) which might conceivably result from the string operation(s). If you fail to watch out for this the results will generally be unpredictable -~and definitely not very pleasant. char *strcpy(char *s, char *t) Copy the string pointed to by t to that pointed to by s . The return value is simply s (i.e. a pointer to the destination string); this is sometimes convenient in forming more complex expressions, but, more usually, it can be ignored (discarded). char *strcat(char *s, char *t) Concatenate the strings pointed at by s and t ; the result is pointed to by s . That is, in effect, the characters from string t are added on at the end of string s . The return value is again simply s . int strcmp(char *s, char *t) Compare the strings pointed to by s and t . The return value is zero if the two strings are identical; it is negative if s would come before t in an alphabetical ordering; and positive if t would come before s . Conclusion This essay serves only to skim the surface of the facilities offered by the Standard Library. It is well worth your while to become reasonably familiar with the full range of services which are available in the library: it can speed up programming of many applications very considerably.