8. Run-time Library

This section admittedly contains very little information if compared to Portable C Software [4]. We direct the reader to that reference for more information.

Time and time again, it happens that the target platform does not have all the library functions needed by a given application. This is particularly true with mathematical functions. We would like to remind the reader that the sources to 4.3BSD are publicly available, and may be obtained at several sites, e.g., funic.funet.fi [128.214.6.100] in ~ftp/pub/bsd-sources, the contents of which are cloned from uunet.uu.net. Read the copyright notices before using them.

8.1. Mathematical Functions

8.1.1. cbrt and pow

cbrt(x) evaluates the cube root of its argument, that is, x^1/3. pow(x,y) evaluates x^y. Some systems implement neither of these, or just the latter. In that case, one can define pow as a function of exp and log, and if one has pow but not cbrt, one can write the latter as a function of the former:

#define  pow(x,y)  (exp(log(x)*(y)))
#define  cbrt(x)   (pow((x),1./3.))

Thus defined, pow only admits strictly positive arguments. If the argument x is negative, then a result can be evaluated if y is an integer and one must implement such a function oneself (a predicate which determines if y is an integer is usually not available).

The definitions given above are a "poor man's" solution to the problem but acceptable in many situations. In order to obtain numerically robust and accurate results one must investigate other alternatives such as obtaining the source code for the 4.3BSD implementation via anonymous FTP as mentioned at the beginning of this Section.

It should be mentioned that if the argument y is zero then implementations differ on the result. The 4.3BSD implementation returns always 1.0; others may return undefined values, flag an error, or return not-a-number.

8.1.2. rand

rand returns a pseudo-random integer in the range 0 to RAND_MAX, which is guaranteed only to be at least 32,767. Do not rely on rand returning results over a much wider range.

8.2. Memory allocation and initialization

8.2.1. alloca

alloca(n) allocates the amount of bytes specified by n and returns a pointer to the allocated memory. This space is -- for all practical purposes -- automatically deallocated (freed) when the block scope is exited. More specifically, the storage is deallocated no sooner than the exit from the block scope; the implementation is allowed to do the freeing at function exit, upon the next call to alloca, or at any other moment deemed appropriate. The example below illustrates incorrect usage of alloca:

foo ()
{
 char *sto;
    {
      sto = alloca (10);
      use (sto);  /* Correct. */
    }
    use (sto); /* Error: storage may have been freed. */
}

Conceptually, the space is allocated on a stack, so allocation can be as fast as just adjusting the stack pointer if the machine has one, and several regions can be freed at once by simply readjusting the stack pointer. However, it is hard to implement alloca both portably and efficiently. alloca is not available on all platforms and as such is not required by the Standard. However, there are public domain implementations that work in a wide variety of cases, but which can be slow and which can delay freeing arbitrarily (10).

Thus, while it is very desirable to use alloca when it is available, because of efficiency considerations, it is highly recommended that the code be written so that malloc and free can easily replace it, if and when necessary.

8.2.2. bcopy vs. memcpy and memmove

bcopy(s1,s2,n) copies the string s1 into s2, whereas memcpy(s1,s2,n) copies s2 into s1. bcopy can be found in BSD-like systems, and some implementations handle overlapping strings, while others do not. memcpy and memmove are implemented in the other camp (System V); memcpy does not handle overlapping strings, whereas memmove does.

The normal solution is to use macros.

8.2.3. bzero vs. memset

bzero(s,n) is equivalent to memset(s,0,n). The former is implemented in BSD-like systems, whereas the latter is implemented in System V-like systems and is required by the Standard.

See also Initialization in section 11.2.4.

8.2.4. malloc and free

malloc is available in all C implementations and its behavior is very well defined except in boundary conditions. Not all implementations accept a zero-sized request. There are other minor differences such as the return type being char * in some implementations and void * in others.

In a similar vein, some implementations of free do not accept NULL as an argument. Worse, though, is that some implementations allowed the caller to use the pointer even after it had been freed so long as no other call to malloc was performed. Relying on such behavior is bad.

8.2.5. realloc

realloc(sto,n) takes a pointer to a region allocated with malloc and grows or shrinks the region so that it is of size n. The return value from realloc is a pointer to the resized storage; if the storage was grown "in place", the return value is the same as sto. If the region was moved, then the old contents are copied to the new storage (if n is smaller than the old size, then only the first n units are copied). If the region is grown, the new storage at the end is uninitialized and may contain garbage.

Under ANSI C:

For non-ANSI versions of realloc, specifying NULL as the storage or 0 as the new size causes undefined behavior. Thus, it is recommended that portable programs, even those written in ANSI C, not use these features. If it is necessary to rely on those features, use a macro or write a function that can be configured to check for those cases explicitly.

8.3. Miscellaneous

8.3.1. scanf

scanf can behave differently on different platforms because its descriptions, including the one in the Standard, allows for different interpretations under some circumstances. The most portable input parser is the one you write yourself.

Some versions of the scanf family modify and then restore arguments which are string constants. These implementations cause problems when string constants are placed in read-only memory (see {"String constants"} in section 11.2.4). If the string is actually a constant, then some workaround is needed; usually a compiler flag may be used to indicate that such constants should be placed in writable memory instead. If such a flag is not available then the code must be modified.

8.3.2. setjmp and longjmp

Quoting anonymously from comp.std.c, "pre-X3.159 implementations of setjmp and longjmp often did not meet the requirements of the Standard. Often they didn't even meet their own documented specs. And the specs varied from system to system. Thus it is wise not to depend too heavily on the exact standard semantics for this facility...".

In other words, it is not that you should not use them but be careful if you do. Furthermore, the behavior of a longjmp invoked from a nested signal handler (11) is undefined.

Finally, the symbols _setjmp and _longjmp are only defined under SunOS, BSD, and HP-UX. Some systems do not implement setjmp and friends at all.

8.3.3. Signal Handling

We would like to point out one problem when handling signals generated by hardware, such as SIGFPE and SIGSEGV. There are two possibilities on a normal exit from the signal handler: (i) the offending instruction is re-executed, or (ii) it is not.

The first possibility may cause an infinite loop, and the only portable solution is to longjmp out of the signal handler.


10. A public domain implementation of alloca can be obtained from the Free Software Foundation (GNU); try prep.ai.mit.edu in ~ftp/pub/gnu.

11. That is, a function invoked as a result of a signal raised during the handling of another signal. See section 4.6.2.1, paragraph 15 in the Standard [13].


upcontents previousHeader Files nextUsing Floating-Point Numbers