Consider two programs, writer and reader. The writer program is written as follows:
#include <stdio.h> main() /* writer.c */ { long i; for (i = 0; i < 8; i++) { if (fwrite((char *)&i, sizeof(i), 1, stdout) != 1) { fprintf(stderr, "failed!\n"); exit(1); } } exit(0); }
The reader program is written as follows:
#include <stdio.h> main() /* reader.c */ { long i, j; for (j = 0; j < 8; j++) { if (fread((char *)&i, sizeof (i), 1, stdin) != 1) { fprintf(stderr, "failed!\n"); exit(1); } printf("%ld ", i); } printf("\n"); exit(0); }
The two programs appear to be portable because they pass lint checking and exhibit the same behavior when executed on two different hardware architectures, such as an IBM machine and a VAX machine.
Piping the output of the writer program to the reader program gives identical results on an IBM machine or a VAX machine, as follows:
ibm% writer | reader 0 1 2 3 4 5 6 7 ibm% vax% writer | reader 0 1 2 3 4 5 6 7 vax%
The following output results if the first program produces data on an IBM machine and the second consumes data on a VAX machine:
ibm% writer | rsh vax reader 0 16777216 33554432 50331648 67108864 83886080 100663296 117440512 ibm%
Executing the writer program on the VAX machine and the reader program on the IBM machine produces results identical to the previous example. These results occur because the byte ordering of long integers differs between the VAX machine and the IBM machine, even though word size is the same.
Note: The value 16777216 equals 224 . When 4 bytes are reversed, the 1 winds up in the 24th bit.
Data must be portable when shared by two or more machine types. Programs can be made data-portable by replacing the read and write system calls with calls to the xdr_long subroutine, which is a filter that interprets the standard representation of a long integer in its external form.
Following is the revised version of the writer program:
#include <stdio.h> #include <rpc/rpc.h> /* xdr is a sub-library of rpc */ main() /* writer.c */ { XDR xdrs; long i; xdrstdio_create(&xdrs, stdout, XDR_ENCODE); for (i = 0; i < 8; i++) { if (!xdr_long(&xdrs, &i)) { fprintf(stderr, "failed!\n"); exit(1); } } exit(0); }
Following is the result of the reader program:
#include <stdio.h> #include <rpc/rpc.h> /* xdr is a sub-library of rpc */ main() /* reader.c */ { XDR xdrs; long i, j; xdrstdio_create(&xdrs, stdin, XDR_DECODE); for (j = 0; j < 8; j++) { if (!xdr_long(&xdrs, &i)) { fprintf(stderr, "failed!\n"); exit(1); } printf("%ld ", i); } printf("\n"); exit(0); }
The new programs, executed on an IBM machine, then on a VAX machine, and then from an IBM to a VAX, yield the following results:
ibm% writer | reader 0 1 2 3 4 5 6 7 ibm% vax% writer | reader 0 1 2 3 4 5 6 7 vax% ibm% writer | rsh vax reader 0 1 2 3 4 5 6 7 ibm%
Integers are one type of portable data. Arbitrary data structures present portability problems, particularly with respect to alignment and pointers. Alignment on word boundaries can cause the size of a structure to vary from machine to machine. Pointers, though convenient to use, have meaning only on the machine where they are defined.