[prev] [up] [overview] [next]

Section 12. Library Subroutines

12.1: Why does strncpy not always place a '\0' termination in the destination string?

strncpy was first designed to handle a now-obsolete data structure, the fixed-length, not-necessarily-\0-terminated "string."  strncpy is admittedly a bit cumbersome to use in other contexts, since you must often append a '\0' to the destination string by hand.

12.2: I'm trying to sort an array of strings with qsort, using strcmp as the comparison function, but it's not working.

By "array of strings" you probably mean "array of pointers to char."  The arguments to qsort's comparison function are pointers to the objects being sorted, in this case, pointers to pointers to char (strcmp, of course, accepts simple pointers to char.)

The comparison routine's arguments are expressed as "generic pointers," const void * or char * They must be converted back to what they "really are" (char **) and dereferenced, yielding char *'s which can be usefully compared.  Write a comparison function like this:

	int pstrcmp(p1, p2)	/* compare strings through pointers */
	char *p1, *p2;		/* const void * for ANSI C */
		return strcmp(*(char **)p1, *(char **)p2);

Beware of the discussion in K&R II Sec. 5.11 pp. 119-20, which is not discussing Standard library qsort.

12.3: Now I'm trying to sort an array of structures with qsort My comparison routine takes pointers to structures, but the compiler complains that the function is of the wrong type for qsort How can I cast the function pointer to shut off the warning?

The conversions must be in the comparison function, which must be declared as accepting "generic pointers" (const void * or char *) as discussed in question 12.2 above. The code might look like

        int mystructcmp(p1, p2)
        char *p1, *p2;          /* const void * for ANSI C */
                struct mystruct *sp1 = (struct mystruct *)p1;
                struct mystruct *sp2 = (struct mystruct *)p2;
                /* now compare sp1->whatever and sp2-> ... */
(If, on the other hand, you're sorting pointers to structures, you'll need indirection, as in question 12.2:
        sp1 = *(struct mystruct **)p1 .)

12.4: How can I convert numbers to strings (the opposite of atoi)?  Is there an itoa function?

Just use sprintf (You'll have to allocate space for the result somewhere anyway; see questions 3.1 and 3.2 Don't worry that sprintf may be overkill, potentially wasting run time or code space; it works well in practice.)

References: K&R I Sec. 3.6 p. 60; K&R II Sec. 3.6 p. 64.

12.5: How can I get the current date or time of day in a C program?

Just use the time, ctime, and/or localtime functions.  (These routines have been around for years, and are in the ANSI standard.)  Here is a simple example:

	#include <stdio.h>
	#include <time.h>

		time_t now = time((time_t *)NULL);
		printf("It's %.24s.\n", ctime(&now));
		return 0;

References: ANSI Sec. 4.12 .

12.6: I know that the library routine localtime will convert a time_t into a broken-down struct tm, and that ctime will convert a time_t to a printable string.  How can I perform the inverse operations of converting a struct tm or a string into a time_t?

ANSI C specifies a library routine, mktime, which converts a struct tm to a time_t Several public-domain versions of this routine are available in case your compiler does not support it yet.

Converting a string to a time_t is harder, because of the wide variety of date and time formats which should be parsed.  Some systems provide a strptime function; another popular routine is partime (widely distributed with the RCS package), but these are less likely to become standardized.

References: K&R II Sec. B10 p. 256; H&S Sec. 20.4 p. 361; ANSI Sec. .

12.7: How can I add n days to a date?  How can I find the difference between two dates?

The ANSI/ISO Standard C mktime and difftime functions provide some support for both problems.  mktime() accepts non-normalized dates, so it is straightforward to take a filled-in struct tm, add or subtract from the tm_mday field, and call mktime() to normalize the year, month, and day fields (and convert to a time_t value).  difftime() computes the difference, in seconds, between two time_t values; mktime() can be used to compute time_t values for two dates to be subtracted.  (Note, however, that these solutions only work for dates in the range which can be represented as time_t's, and that not all days are 86400 seconds long.)  See also questions 12.6 and 17.28.

References: K&R II Sec. B10 p. 256; H&S Secs. 20.4, 20.5 pp. 361-362; ANSI Secs., .

12.8: I need a random number generator.

The standard C library has one: rand() The implementation on your system may not be perfect, but writing a better one isn't necessarily easy, either.

References: ANSI Sec. p. 154; Knuth Vol. 2 Chap. 3 pp. 1-177.

12.9: How can I get random integers in a certain range?

The obvious way,

        rand() % N
(where N is of course the range) is poor, because the low-order bits of many random number generators are distressingly non-random.  (See question 12.11.)  A better method is something like
        (int)((double)rand() / ((double)RAND_MAX + 1) * N)
If you're worried about using floating point, you could try
        rand() / (RAND_MAX / N + 1)
Both methods obviously require knowing RAND_MAX (which ANSI defines in <stdlib.h>), and assume that N is much less than RAND_MAX.

12.10: Each time I run my program, I get the same sequence of numbers back from rand().

You can call srand() to seed the pseudo-random number generator with a more random initial value.  Popular seed values are the time of day, or the elapsed time before the user presses a key (although keypress times are hard to determine portably; see question 16.10).

References: ANSI Sec. p. 154.

12.11: I need a random true/false value, so I'm taking rand() % 2 but it's just alternating 0, 1, 0, 1, 0...

Poor pseudorandom number generators (such as the ones unfortunately supplied with some systems) are not very random in the low-order bits.  Try using the higher-order bits.  See question 12.9.

12.12 I'm trying to port this old program.  Why do I get "undefined external" errors for ..:

Those routines are variously obsolete; you should instead...:
use strchr.
use strrchr.
use memmove, after interchanging the first and second arguments (see also question 5.15).
use memcmp.
use memset, with a second argument of 0.

12.13: I keep getting errors due to library routines being undefined, but I'm #including all the right header files.

In some cases (especially if the routines are nonstandard) you may have to explicitly ask for the correct libraries to be searched when you link the program.  See also question 15.2.

12.14: I'm still getting errors due to library routines being undefined, even though I'm using -l to request the libraries while linking.

Many linkers make one pass over the list of object files and libraries you specify, and extract from libraries only those modules which satisfy references which have so far come up as undefined.  Therefore, the order in which libraries are listed with respect to object files (and each other) is significant; usually, you want to search the libraries last.  (For example, under Unix, put any -l switches towards the end of the command line.)

12.15: I need some code to do regular expression matching.

Look for the regexp library (supplied with many Unix systems), or get Henry Spencer's regexp package from ftp.cs.toronto.edu in pub/regexp.shar.Z (see also question 17.12).

12.16: How can I split up a command line into whitespace-separated arguments, like main's argc and argv?

Most systems have a routine called strtok, although it can be tricky to use and it may not do everything you want it to (e.g., quoting).

References: ANSI Sec.; K&R II Sec. B3 p. 250; H&S Sec. 15.7; PCS p. 178.

[prev] [up] [overview] [next]