[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
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); |
Instead, use unsigned char
as follows. (The unsigned
is for portability to unusual systems where char
is signed and
where there is integer overflow checking.)
int c; while ((c = getchar ()) != EOF) { unsigned char u = c; write (file_descriptor, &u, 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.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |