荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: BlueBoy (蓝孩), 信区: Program
标  题: Notes On Writing Portable Programs In C
发信站: BBS 荔园晨风站 (Fri Mar 24 11:12:16 2000), 转信

Notes On Writing Portable Programs In C
(June 1990, 5th Revision)
A. Dolenc
A. Lemmke [+]
and
D. Keppel [+]
Contents

Foreword
Introduction
Standardization Efforts
ANSI C
Translation limits
Unspecified and undefined behaviour
POSIX
Preprocessors
The Language
The syntax
The semantics
Unix flavours: System V and BSD
Header Files
ctype.h
fcntl.h and sys/file.h
errno.h
math.h
strings.h vs. string.h
time.h and types.h
varargs.h vs. stdarg.h
Run-time Library
Compiler limitations
Using floating-point numbers
Machine constants
Floating-point arguments
Floating-point arithmetic
Exceptions
VMS
File specifications
Miscellaneous
General Guidelines
Machine architectures, Type compatibility, Pointers, etc.
Compiler differences
Files
Miscellaneous
Acknowledgements
Trademarks
References
About this document ... Foreword
A few words about the intended audience before we begin. This document is
mainly for those who have never ported a program to another platform -- a
specific hardware and software environment -- and, evidently, for those who
plan to write large systems
which must be used across different vendor machines.
If you have done some porting before you may not find the information herein
very useful.
We suggest that [Can89] be read in conjunction with this document[+].
Submitters to the News group comp.lang.c have repeatedly recommended [Hor90,
Koe89][+].
Disclaimer: The code fragments presented herein are intended to make
applications ``more'' portable, meaning that they may fail with some compilers
and/or environments.
This file can be obtained via anonymous ftp from sauna.hut.fi [130.233.251.253]
in ~ftp/pub/CompSciLab/doc. The files portableC.tex, port write and/or port
programs in C to more than one platform.
In order to keep this document within reasonable bounds we must restrict
ourselves to programs which must execute under Unix-like operating systems and
those which implement a reasonable Unix-like environment. The only exception we
will consider is VMS.
A wealth of information can be obtained from programs which have been written
to run on several platforms. This is the case of publicly available software
such as developed by the Free Software Foundation and the MIT X Consortium.
When discussing portability one focuses on two issues:
The language, which includes the preprocessor and the syntax and the semantics
of the language.
The environment, which includes the location and contents of header file
refered to as the Standard - will not be extensively covered in this document[+]
.
Standardization Efforts
All standards have a good and an evil side. Due to the nature of this document
we are forced to focus our attention on the later.
The American National Standards Institute (ANSI) has recently approved of a
standard for the C programming language [X3J88]. The Standard concentrates on
the syntax and semantics of the language and specifies a minimum environment
(the name and
contents of some header files and the specification of some run-time library
functions).
Copies of the ANSI C Standard can be obtained from the following address:
American National Standards Institute
Sales Department
1430 Broadway
New York, NY 10018
(Voice) (212) 642-4900
(Fax) (212) 302-1286
ANSI C Translation limits
We first bring to attention the fact that the Standard states some
environmental limits. These limits are lower bounds, meaning that a correct
(compliant) compiler may refuse to compile an otherwise correct program which
macro expansion always results
in one line this affects the maximum size of a macro. It is unclear what the
Standard means by a logical source line in this context and in most
implementations this limit will probably apply em before macro expansion.
6 significant initial characters in an external identifier. Usually this
constraint is imposed by the environment, e.g. the linker, and not by the
compiler.
127 members in a single structure or union.
31 parameters in one function call. This may cause trouble wmaining limits
stated in the Standard can usually be obeyed if one follows ``good''
programming practices.
However, these limits may break programs which generate C code such as
compiler-compilers and many C++ compilers.
Unspecified and undefined behaviour
The following are examples of unspecified and undefined behaviour:
The order in which the function designator and the arguments in a function call
are evaluated.
The order in which the preprocessor concatenation operators # and ## are
evaluated during macro substitution.
The representation of floating types.
An identifier is used that is not visible in the current scope.
A pointer is converted to other than an integral or pointer type.
The list is long. One of the main reasons for explicitly defining what is not
covered by the Standard is to allow the implementor of the C environment to
make use the most efficient alternative.
POSIX
The objective of the POSIX working group P1003.1 is to define a common
interface for UNIX. Granted, the ANSI C standard does swing:
The interpretation of the -I command option can differ from one system to
another. Besides, it is not covered by the Standard. For example, the directive
#include ``dir/file.h'' in conjunction with -I.. would cause most preprocessors
in a Unix-like
environment to search for file.h in ../dir but under VMS file.h is only
searched for in the subdirectory dir in the current working directory.
We would not trust the following to work on all preprocessors:
#define  D  define
#D this that
The Standard does not allow such a syntax (see section 3.8.3 ?20 in [X3J88]).
Directives are very much the same in all preprocessors, except that some
preprocessors may not know about the defined operator in a #if directive nor
about the #pragma directive.
The #pragma directrguments [+].
Some preprocessors perform token substitution within quotes while others do
not. Therefore, this is intrinsicly non-portable. The Standard disallows it but
provides mechanism to obtain the same results. The following should work with
ANSI-compliant
preprocessors or with the ones that which perform token substitution within
quotes: #ifdef  __STDC__
# define  MAKESTRING(s)  # s
#else
# define  MAKESTRING(s)  ``s''
#endif
There are good publicly available preprocessors which are ANSI C compliant. One
such preprocessor is the one distributed with the X Window System developed by
the MIT X Consortium.
Take note of #pragma directives which alter the semantics of the program and
consider the case when they are not recognized by a particular compiler.
compliant compiler. The Standard extends the syntax with the following:
The inclusion of the keywords const and volatile.
The ellipsis (``...'') notation to indicate a variable number of arguments.
Function prototypes.
Trigraph notation for specifying ``wide'' character strings.
We encourage the use of the reserved words const and volatile since they aid in
documenting the code. It is useful to add the following to one's header files
if the code must be compiled by an non-conforming compiler as well:
#ifndef __STDC__
# define const
# define volatile
#endif
However, one must then make sure that the behaviour of the application does not
depend on the presence of such keywords.
The semantics
The syntax does not pose any problem with regard to interpretation because it
can be defined precisely. However, programming languages are always described
using a natural language, e.g. English, and this can lead to different
interpretations of the
same text.
Evidently, [KR78] does not provide an unambiguous definition of the C language
otherwise there would have been no need for a standard. Although the Standard
is much more precise, there is still room for different interpretations in
situations such as
f(p=&a, p=&b, p=&c). Does this mean f(&a,&b,&c) or f(&c,&c,&c)? Even ``simple''
cases such as a[i] = b[i++] are compiler-dependent [Can89].
As stated in the Introduction we would like to exclude such topics. The reader
is instead directed to the USENET news group comp.std.c or comp.lang.c where
such discussions take place and from where the above example was taken. The
Journal oapa'' for
the first time at AT&T (then called Bell Laboratories, or Ma Bell for the
intimate) on a PDP-11. Everyone liked Unix very much and its widespread use we
see today is probably due to the relative simplicity of its design and of its
implementation (it is
written, of course, mostly in C).
However, these facts also contributed for each one to develop their own
dialect. In particular, the University of Berkeley at California distribute the
so-called BSD[+] Unix whereas AT&T distribute (sell) System V Unix. All other
vendors are
descendants of one of these major dialects.
The differences between these two major flavours should not upset most
application programs. In fact, we would even say that most differences are just
annoying.
BSD Unix has an enhanced signal handling capability and implements sockets.
However, all Unix flavours differ significantly in their raw i/o interface
(that is, ioctl system call) which should be avoided if possible.
The reader interested in knowing more about the past and futurem the header
file Xos.h which is part of the X Window System distributed by MIT. We have
added changes, e.g. to support VMS.
Many header files are unprotected in many systems, notably those derived from
BSD version 4.2 and earlier. By unprotected we mean that an attempt to include
a header file more than once will either cause compilation errors (e.g. due to
recursive
includes) or, in some implementations, warnings from the preprocessor stating
that symbols are being redefined. It is good practice to protect header files.
ctype.h
They provide the same functionality in all systems except that some symbols
must be renamed.
#ifdef SYSV
# define  _ctype_  _ctype
# define  toupper  _toupper
# define  tolower  _tolower
#endif
Note however that the definitions in <ctype.h> are not portable across
character sets.
fcntl.h and sys/file.h
Many files which a BSD systems expects to find in the sys directory are placed
in /usr/include in System V. Other systems, like VMS, do not even have a sys
directory[+].
The symbols used in the open function call are defined in different header
files in both types of systems:
#ifdef  SYSV
# include <fcntl.h>
#else
# include <sys/file.h>
#endif errno.h
The semantics of the error number may differ from one system to another and the
list mis not available in most systems, although the Standard requires that
such a symbol be defined (see section 4.1.3 of [X3J88]).
The most portable way to print error messages is to use perror.
math.h
System V has more definitions in this header file than BSD-like systems. The
corresponding library has more functions as well. This header file is
unprotected under VMS and Cray, and that case we must do-it-ourselves:
#if defined(CRAY) || defined(VMS)
# ifndef  __MATH__
#  define  __MATH__
#  include <math.h>
# endif
#endif strings.h vs. string.h
Some systems cannot be treated as System V or BSD but are really a special case,
 as one can see in the following:
#ifdef  SYSV
#ifndef SYSV_STRINGS
# define  SYSV_STRINGS
#endif
#endif

#ifdef  _STDH_  /* ANSI C Standard m V-like Unix systems use different names
for index and rindex and place them in different header files. Although VMS
supports better System V features it must be treated as a special case.
time.h and types.h
When using time.h one must also include types.h. The following code does the
trick:
#ifdef macII
# include <time.h>   /* on a Mac II we need this one as well */
#endif

#ifdef  SYSV
# include <time.h>
#else
# ifdef vms
#  include <time.h>
# else
#  ifdef CRAY
#   ifndef __TYPES__   /* it is not protected under CRAY */
#   define __TYPES__
#   include <sys/types.h>
#   endif
#  else
#   include <sys/types.h>
#  endif /* of ifdef CRAY */
# include <sys/time.h>
# endif  /* of ifdef vms  */
#endif
The above is not sufficient in order for the code to be portable since the
structure which defines time values is not the same in all systems. Different
systems have vary in the way time_t values are represented. The Standard, for
instance, only
requires that it be an arithmetic type. Recognizing this difficulty, the
Standard defines a function called difftime to compute the difference between
two time values of type time_t, and mktime which takes a string and produces a
value of type time_t.
varargs.h vs. stdarg.h
In some systems the definitions in both header files are contradictory. For
instance, the following will produce compilation errors under VMS[+]:
#include <varargs.h>
#include <stdio.h>
This is because <stdio.h> includes <stdarg.h> which in turn redefines all the
symbols (va_start, va_end, etc.) in <varargs.h>. The solution we adopt is to
always include <varargs.h> last and not define in the same module functions
which use <varargs.h>
and functions which use the ellipsis notation.
Run-time Library getlogin:
This one is not defined, e.g. under VMS. In that case, one can always use
getenv(``HOME''). scanf:
Scanf can behave differently in different platforms because it's 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. setjmp and
longjmp:
Quoting anonymously from comp.std.c, ``pre-X3.159 implementations of setjmp and
loo 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 behaviour of a longjmp invoked from a nested signal
handler[+] is undefined.
Finally, the symbols _setjmp and _longjmp are only defined under SunOS, BSD,
and HP-UX.
Compiler limitations
In practice, much too frequently one runs into several, unstated compiler
limitations:
Some of these limitations are bugs. Many of these bugs are in the optimizer and
therefore when dealing with a new environment it is best to explicitly disable
optimization until one gets the application ``going''.
Some compilers cannot handle large modules or ``large'' statements[+].
Therefore, it is advisable to keep the size of modules within reasonable
bounds. Besides, large modules are more cumbersome to edit and understand.
Using floating-point numbers
To say that the implementation of numerical algorithms which exhibit the same
behaviour across a wide variety of platforms is difficult is an understatement.
This section provides very little help but we hope it is worth reading. Any
additional
suggestions and information is very much appreciated as we would like to expand
this section.
Machine constants
One problem when writing numerical algorithms is obtaining machine constants.
Typical values one needs are:
The radix of the floating-point representation.
The numbet positive floating-point number eps such that (image not reproduced).
The smallest non-vanishing normalized floating-point power of the radix.
The largest finite[+] floating-point number.
On Sun's they can be obtained in <values.h>. The ANSI C Standard recommends
that such constants be defined in the header file <float.h>.
Sun's and standards apart, these values are not always readily available, e.g.
in Tektronix workstations running UTek. One solution is to use a modified
version of a program which can be obtained from the network called machar.
Machar is described in
[Cod88] and can obtained by anonymous ftp from the netlib[+].
It is straightforward to modify the C version of machar to generate a C
preprocessor file which can be included directly by C programs.
There is also a publicly available program called config.c which attempts to
determine many properties of the C compiler and machine that it is run on. This
program was submitted to comp.sources.misc[+].
Floating-point arguments
In the days of K&R[KR78] one was ``encouraged'' to use float and double
interchangeably[+] since all expressions with such data types where always
evaluated using the double representation - a real nightmare for those
implementing efficient numerical algorithms in C. This rule applied, in
particular, to floating-point
arguments and for most compiler around it does not matter whether one defines
the argument as float or double.
According to the ANSI C Stl seldom be satisfied due to rounding errors. To get
a feeling about rounding errors, try evaluating the following expression using
your favourite C compiler[KM86]:
(image not reproduced)
Most computers will produce zero regardless if one uses float or double.
Although the absolute error is large, the relative error is quite small and
probably acceptable for many applications.
It is rather better to use expressions such as (image not reproduced) or (image
not reproduced) (if (image not reproduced)), where 0 < K < 1 is a function of:
The floating type, e.g. float or double,
the machine architecture (the machine constants defined in the previous section)
, and
the precision of the input values and the rounding errors introduced by the
numerical method used.
Other possibilities exist and the choice depends on the application.
The development of reliable and robust numerical algorithm is a very difficult
undertaking. Methods for certifying that the results are correct within
reasonable bounds must usually be implemented. A reference such as [PFTV88] is
always useful.
Keep in mind that the double representation does not necessarily increase the
precision. Actually, in most implementations the precision decreases but the
range increases.
Do not use double unnecessarily since in most cases there is a large
performance penalty. Furthermore, there is no point in using higher precision
if the additional bits which willlow, underflow, division by zero, etc) are not
signaled automatically in
some systems. In that case, they must be explicitly enabled.
Always enable floating-point exceptions since they may be an indication that
the method is unstable. Otherwise, one must be sure that such events do not
affect the output.
VMS
In this section we will report some common problems encountered when porting a
C program to a VMS environment and which we have not mentioned in the
previously.
File specifications
Under VMS one can use two flavours of command interpreters: DCL and DEC/Shell.
The syntax of file specifications under DCL differs significantly from the Unix
syntax.
Some C run-time library functions in VMS which take file specifications as
arguments or return file specifications to the caller will accept an additional
argument indicating which syntax is preferred. It is useful to use these
run-time library
functions via macros as follows:
#ifdef  VMS
#ifndef VMS_CI     /* Which Command Interpreter flavour to use */
# define VMS_CI  0 /* 0 for DEC/Shell, 1 for DCL */
#endif

# define  Getcwd(buff,siz)   getcwd((buff),(siz),VMS_CI)
# define  Getname(fd,buff)   getname((fd),(buff),VMS_CI)
# define  Fgetname(fp,buff)  fgetname((fp),(buff),VMS_CI)
#else
# define  Getcwd(buff,siz)   ges only to modules stored in libraries[+]. If
none of the global functions are explicitly used (referenced by another module)
then the module is not linked at all. It does not matter whether one of the
global variables is
used. As a side effect, the initialization of variables is not done.
The easiest solution is to force the linker to add the module using the
/INCLUDE command modifier. Of course, there is the possibility that the command
line may exceed 256 characters...(*sigh*).
General Guidelines Machine architectures, Type compatibility, Pointers, etc.
Never make any assumptions about the size of a given type, especially pointers.
[Can89] Statements such as x &= 0177770 make implicit use of the size of x. If
the intention is
to clear the lower three bits then it is best to use x &= (image not reproduced)
 07. The first alternative will also clear the high order 16 bits if x is 32
bits wide.
In some architectures the byte order is inverted; these are called
little-endian versus big-endian architectures. Thiint *p = (int *) malloc(...);
... free(p);
This code may malfunction in architectures where int* and char* have different
representations because free expects a pointer of the latter type.
[Can89] Only the operators == and != are defined for all pointers of a given
type. The remaining comparison operators (<, <=, >, and >=) can only be used
when both operands point into the same array or to the first element after the
array. The same
applies to arithmetic operators on pointers[+].
Never redefine the NULL symbol. The NULL symbol should always be the constant
zero. A null pointer of a given type will always compare equal to the constant
zero, whereas comparison with a variable with value zero or to some non-zero
constant has
implementation defined behaviour.
Aers which treat them as signed (e.g. VAX C and HP-UX). It is advisable to
always cast them when used in arithmetic expressions.
Do not rely on the initialization of auto variables and of memory returned by
malloc.
Some compilers, e.g. VAX C, require that bit fields within structs be of type
int or unsigned. Futhermore, the upper bound on the length of the bit field may
differ among different implementations.
The result of sizeof may be unsigned. Files Keep files reasonably small in
order not to upset some compilers.
File names should not exceed 14 characters (many System V derived system impose
this limit, whereas in BSD derived systems a limit of 15 is usually the case).
In some implementations this limit can be as low as 8 characters. These limits
are often not
imposed by the operating system but by system utilities such as ar.
Do not use special characters especially multiple dots (dots have a very
special meaning under VMS). Miscellaneous Functions as arguments:
when calling functions passed as arguments nimize the number of global symbols
in the application. One of the benefits is the lower probability that any
conflicts will arise with system-defined functions. String constants:
Do not modify string constants since many implementations place them in
read-only memory. Furthermore, that is what the Standard requires -- and that
is how a constant should behave! Acknowledgements
We are grateful for the help of Antti Louko (HTKK/Lsk) and Jari Helminen (HTKK)
in commenting and correcting a previous draft of this document. We thank all
the contributors of USENET News groups comp.std.c and comp.lang.c from where we
have taken a
lot of information. Some information within was obtained from [Hew88].
Trademarks
DEC, PDP-11, VMS and VAX are trademarks of Digital Equipment Corporation.
HP is a trademark of Hewlett-Packard, Inc.
MC68000 is a trademark of Motorola.
PostScript is a registred trademark of Adobe Systems, Inc.
Sun is a trademark of Sun Microsystems, Inc.
UNIX is a registred trademark of AT&T.
X Window System is a trademark of MIT.
References Can89
L.W. Cannon. Recommended C Style and Coding Standards. Technical report,
November 1989. Cod88
W. J. Cody. Algorithm 665, MACHAR: A Subroutine to Dynamically Determine
Machine Parameters. ACM Transactions on Mathematiuary 1989. PFTV88
William H. Press, Brian P. Flannery, Saul A. Teukolsky, and William T.
Vetterling. NUMERICAL RECIPES in C: The Art of Scientific Computing. Cambridge
University Press, 1988. X3J88
X3J11. Draft Proposed American National Standard for Information Systems --
Programming Language C. Technical Report X3J11/88-158, ANSI Accredited
Standards Committee, X3 Information Processing Systems, December 1988. About
this document ...
Notes On Writing Portable Programs In C
(June 1990, 5th Revision)
This document was generated using the LaTeX2HTML translator Version 96.1-h
(September 30, 1996) Copyright ? 1993, 1994, 1995, 1996, Nikos Drakos, Computer
Based Learning Unit, University of Leeds.
The command line arguments were:
latex2html portableC.tex.
The translation was initiated by Christopher Lott on Thu Mar 13 13:33:05 EST
1997
...Lemmke
Helsinki University of Technology, Laboratory of Information Processing
Sciences, SF-02150 Espoo, Finland. This document is in the public domain. Email
address (Internet) are ado@sauna.hut.fi (preferred contact) and
arl@sauna.hut.fi, respectively.
...Keppel
CS&E, University of Washington. Email address (Internet) is
pardo@cs.washington.edu.
...document
It can be obtained via anonymous ftp from cs.washington.edu in
1#1ftp/pub/cstyle.tar.Z.
...#kn:AK##1#]
We note here that none of the information herein as been taken from those two
references.
...programming
We include raw I/O, e.g. from terminals in this category.
...document
We regard this document as a living entity growing as needed and as information
is gathered. Future versions of this document may contain a lot of such
information.
...limits
Maybe there are people out there who still write compilers in FORTRAN after
all...
...indented
Old preprocessors only take directives which begin with # in the first column.
...files
Some have suggeested using #if __STDC__ == 1 instead of simply #ifdef __STDC__
to test if the compiler is ANSI-compliant.
...argume...Translation
Address is 2051, Swans Neck Way, Reston, Virginia 22091, USA.
...BSD
Berkeley Software Distribution.
...directory
Under VMS, since a path such as <sys/file.h> will evaluate to sys:file.h it is
sufficient to equate the logical name sys to sys$library.
...VMS
We are not sure this behaviour occurs only under VMS.
...handler
That is, a function invoked as a result of a signal raised during the handling
of another signal. See section 4.6.2.1 ?15 in [X3J88].
...statements
Programs which generate other programs, e.g. YACC, can generate, for instance,
very large switch statements.
...finite
Some representations have reserved values for +inf and -inf.
...netlib
Email (Internet) address is netlib@ornl.gov. For more information, send a
message containing the line send index to that address.
...comp.sources.misc
The arquive site of comp.sources.misc is uunet.uu.net.
...
In fact one wonders why they even bothered to define two representations for
floating-point numbers considering the rules applied to them.are represented as
a pair of values and only under those circumstances are two pairs comparable.
 Document processed by Christopher Lott, cml@cs.umd.edu
Thu Mar 13 13:33:05 EST 1997

--
※ 来源:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.0.90]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店