Next: , Previous: System Portability, Up: Writing C


5.6 Portability between cpus

Even GNU systems will differ because of differences among cpu types—for example, difference in byte ordering and alignment requirements. It is absolutely essential to handle these differences. However, don't make any effort to cater to the possibility that an int will be less than 32 bits. We don't support 16-bit machines in GNU.

Similarly, don't make any effort to cater to the possibility that long will be smaller than predefined types like size_t. For example, the following code is ok:

     printf ("size = %lu\n", (unsigned long) sizeof array);
     printf ("diff = %ld\n", (long) (pointer2 - pointer1));

1989 Standard C requires this to work, and we know of only one counterexample: 64-bit programs on Microsoft Windows. We will leave it to those who want to port GNU programs to that environment to figure out how to do it.

Predefined file-size types like off_t are an exception: they are longer than long on many platforms, so code like the above won't work with them. One way to print an off_t value portably is to print its digits yourself, one by one.

Don't assume that the address of an int object is also the address of its least-significant byte. This is false on big-endian machines. Thus, don't make the following mistake:

     int c;
     ...
     while ((c = getchar()) != EOF)
       write(file_descriptor, &c, 1);

It used to be ok to not worry about the difference between pointers and integers when passing arguments to functions. However, on most modern 64-bit machines pointers are wider than int. Conversely, integer types like long long int and off_t are wider than pointers on most modern 32-bit machines. Hence it's often better nowadays to use prototypes to define functions whose argument types are not trivial.

In particular, if functions accept varying argument counts or types they should be declared using prototypes containing ‘...’ and defined using stdarg.h. For an example of this, please see the Gnulib error module, which declares and defines the following function:

     /* Print a message with `fprintf (stderr, FORMAT, ...)';
        if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM).
        If STATUS is nonzero, terminate the program with `exit (STATUS)'.  */
     
     void error (int status, int errnum, const char *format, ...);

A simple way to use the Gnulib error module is to obtain the two source files error.c and error.h from the Gnulib library source code repository at http://savannah.gnu.org/cgi-bin/viewcvs/gnulib/gnulib/lib/. Here's a sample use:

     #include "error.h"
     #include <errno.h>
     #include <stdio.h>
     
     char *program_name = "myprogram";
     
     FILE *
     xfopen (char const *name)
     {
       FILE *fp = fopen (name, "r");
       if (! fp)
         error (1, errno, "cannot read %s", name);
       return fp;
     }

Avoid casting pointers to integers if you can. Such casts greatly reduce portability, and in most programs they are easy to avoid. In the cases where casting pointers to integers is essential—such as, a Lisp interpreter which stores type information as well as an address in one word—you'll have to make explicit provisions to handle different word sizes. You will also need to make provision for systems in which the normal range of addresses you can get from malloc starts far away from zero.