5.5.1 Portability of C Functions

Most usual functions can either be missing, or be buggy, or be limited on some architectures. This section tries to make an inventory of these portability issues. By definition, this list always requires additions. A much more complete list is maintained by the Gnulib project (see Gnulib), covering Current Posix Functions in Gnulib, Legacy Functions in Gnulib, and Glibc Functions in Gnulib. Please help us keep the Gnulib list as complete as possible.

exit

On ancient hosts, exit returned int. This is because exit predates void, and there was a long tradition of it returning int.

On current hosts, the problem more likely is that exit is not declared, due to C++ problems of some sort or another. For this reason we suggest that test programs not invoke exit, but return from main instead.

isinf
isnan

In C99 and later, isinf and isnan are macros. On some systems just macros are available (e.g., HP-UX and Solaris 10), on some systems both macros and functions (e.g., glibc 2.3.2), and on some systems only functions (e.g., IRIX 6). In some cases these functions are declared in nonstandard headers like <sunmath.h> and defined in non-default libraries like -lm or -lsunmath.

In C99 and later, isinf and isnan macros work correctly with long double arguments, but pre-C99 systems that use functions typically assume double arguments. On such a system, isinf incorrectly returns true for a finite long double argument that is outside the range of double.

The best workaround for these issues is to use Gnulib modules isinf and isnan (see Gnulib). But a lighter weight solution involves code like the following.

#include <math.h>

#ifndef isnan
# define isnan(x) \
    (sizeof (x) == sizeof (long double) ? isnan_ld (x) \
     : sizeof (x) == sizeof (double) ? isnan_d (x) \
     : isnan_f (x))
static int isnan_f  (float       x) { return x != x; }
static int isnan_d  (double      x) { return x != x; }
static int isnan_ld (long double x) { return x != x; }
#endif

#ifndef isinf
# define isinf(x) \
    (sizeof (x) == sizeof (long double) ? isinf_ld (x) \
     : sizeof (x) == sizeof (double) ? isinf_d (x) \
     : isinf_f (x))
static int isinf_f  (float       x)
{ return !isnan (x) && isnan (x - x); }
static int isinf_d  (double      x)
{ return !isnan (x) && isnan (x - x); }
static int isinf_ld (long double x)
{ return !isnan (x) && isnan (x - x); }
#endif

Some optimizing compilers mishandle these definitions, but systems with that bug typically have many other floating point corner-case compliance problems anyway, so it’s probably not worth worrying about.

malloc

The C standard says a successful call malloc (0) is implementation dependent. It can return either NULL or a new non-null pointer. The latter is more common (e.g., the GNU C Library) but is by no means universal. AC_FUNC_MALLOC can be used to insist on non-NULL (see Particular Function Checks).

putenv

Posix prefers setenv to putenv; among other things, putenv is not required of all Posix implementations, but setenv is.

Posix specifies that putenv puts the given string directly in environ, but some systems make a copy of it instead (e.g., glibc 2.0, or BSD). And when a copy is made, unsetenv might not free it, causing a memory leak (e.g., FreeBSD 4).

On some systems putenv ("FOO") removes ‘FOO’ from the environment, but this is not standard usage and it dumps core on some systems (e.g., AIX).

On MinGW, a call putenv ("FOO=") removes ‘FOO’ from the environment, rather than inserting it with an empty value.

realloc

It is problematic to call realloc with a zero size. The C standard says realloc (NULL, 0) is equivalent to malloc (0), which means one cannot portably tell whether the call has succeeded if it returns a null pointer. If ptr is non-null, the C standard says realloc (ptr, 0) has undefined behavior.

The AC_FUNC_REALLOC macro avoids some of these portability issues, and the Gnulib module realloc-gnu avoids more of them. See Particular Function Checks.

signal handler

In most cases, it is more robust to use sigaction when it is available, rather than signal.

snprintf

In C99 and later, if the output array isn’t big enough and if no other errors occur, snprintf and vsnprintf truncate the output and return the number of bytes that ought to have been produced. Some ancient systems returned the truncated length (e.g., GNU C Library 2.0.x or IRIX 6.5), and some a negative value (e.g., earlier GNU C Library versions).

strerror_r

Posix specifies that strerror_r returns an int, but many systems (e.g., GNU C Library version 2.36) provide a different version returning a char *. AC_FUNC_STRERROR_R can detect which is in use (see Particular Function Checks).

strnlen

AIX 4.3 provided a broken version which produces the following results:

strnlen ("foobar", 0) = 0
strnlen ("foobar", 1) = 3
strnlen ("foobar", 2) = 2
strnlen ("foobar", 3) = 1
strnlen ("foobar", 4) = 0
strnlen ("foobar", 5) = 6
strnlen ("foobar", 6) = 6
strnlen ("foobar", 7) = 6
strnlen ("foobar", 8) = 6
strnlen ("foobar", 9) = 6
sysconf

_SC_PAGESIZE is standard, but some older systems (e.g., HP-UX 9) have _SC_PAGE_SIZE instead. This can be tested with #ifdef.

unlink

The Posix spec says that unlink causes the given file to be removed only after there are no more open file handles for it. Some non-Posix hosts have trouble with this requirement, though, and some DOS variants even corrupt the file system.

unsetenv

On MinGW, unsetenv is not available, but a variable ‘FOO’ can be removed with a call putenv ("FOO="), as described under putenv above.

va_copy

C99 and later provide va_copy for copying va_list variables. It may be available in older environments too, though possibly as __va_copy (e.g., gcc in strict pre-C99 mode). These can be tested with #ifdef. A fallback to memcpy (&dst, &src, sizeof (va_list)) gives maximum portability.

va_list

va_list is not necessarily just a pointer. It can be a struct (e.g., gcc on Alpha), which means NULL is not portable. Or it can be an array (e.g., gcc in some PowerPC configurations), which means as a function parameter it can be effectively call-by-reference and library routines might modify the value back in the caller (e.g., vsnprintf in the GNU C Library 2.1).

Signed >>

Normally the C >> right shift of a signed type replicates the high bit, giving a so-called “arithmetic” shift. But care should be taken since Standard C doesn’t require that behavior. On a few platforms (e.g., Cray C by default) zero bits are shifted in, the same as a shift of an unsigned type.

Integer /

C divides signed integers by truncating their quotient toward zero, yielding the same result as Fortran. However, before C99 the standard allowed C implementations to take the floor or ceiling of the quotient in some cases. Hardly any implementations took advantage of this freedom, though, and it’s probably not worth worrying about this issue nowadays.