63
INTR
character from generating a signal. Give another way to have a similar effect. What
are the differences between the two schemes?
12.
Why would you ever use the
TCSADRAIN
option of the
tcsetattr()
function?
13.
When would a program use the
tcdrain()
function?
14.
Should ordinary applications change the settings in the
c_cc
array? Why or why not?
P
age 171
Chapter 9
Posix And Standard C
This chapter tells you how to use Standard C to achieve maximum portability for your
POSIX applications. Some people use the name "ANSI C" for Standard C. I prefer the name
Standard C to reflect its use as an international standard and not just an American
National
Standard. The POSIX standard is written in terms of the C programming language. It
recognizes two forms of the C language support: C Standard Language Dependent System
Support and Common Usage C Language-Dependent System Support.
Common Usage C
To allow the greatest possible support for POSIX, implementors are not required to meet the C
standard in implementing POSIX. They may support existing pre-standard C compilers and
"use common usage as guidance." Since common usage varied from one system t
o another,
portability of applications is reduced in this type of implementation. Common usage support is
still the default on many systems; you have to work to get Standard C support.
Standard C
In a Standard C implementation, the system is required to support International Standard
ISO/IEC 9899:
Information processing systems—Programming languages—C
for all
required POSIX functions. Standard C adds a number of features and capabilities that are n
ot
present in Common Usage C. In general, you are better off writing new applications with
Standard C. Because the definition of Standard C is more precise than Common Usage C, you
will find that Standard C programs are more portable and more easily mainta
ined. Over time,
most systems and programmers will convert to Standard C. This chapter is your guide into the
future.
Getting Standard C
Since Standard C is new, most systems default to pre-Standard C behavior. Typically, you must
specify Standard C as a compiler option. For example, under AT&T System V.4, you specify
the
-Xa
option on the
cc
command line. The GNU C compiler requires the
an
si
switch.
*
See
your system documentation for details.
* The -pedantic switch will cause warnings for all non-standard features.
49
P
age 172
The Standard C Preprocessor
The most non-standard and least specified part of Common Usage C is the preprocessor.
Operations like recognition of white space and macro replacement did not have a guaranteed
ordering. Standard C eliminates this problem by supplying a rigorous definition
of the
preprocessing and translation process. While the committee was nailing down the exact
definition, they also threw in a few new features. These features are discussed in the following
sections.
Translation Phases
The standard defines eight translation phases:
1.
Every trigraph in the source file is replaced. This usually has no effect. For a discussion of
trigraphs, see "Character Sets" on Page 184.
2.
Every backslash-newline character pair is deleted. This means that a backslash-newline
can be used to continue a line in any context. In older C compilers, the backslash-newline
pairs were allowed only as a way to continue a directive, a string literal,
or a character
constant.
3.
The source file is converted into preprocessing tokens and white space. Each comment is
replaced by a space.
4.
Every preprocessing directive is handled and all macro invocations are replaced. Each file
read by the
#include
directive is run through phases 1 to 4 and replaces the
#include
line.
5.
Every escape sequence in character constants and string literals is interpreted.
6.
Adjacent character string literals are concatenated.
7.
The result of steps 1-6 is compiled.
8.
All external references are resolved. The result is a complete program.
As you can see, most of the work is done in step 7. Most compilers do not perform these
phases as distinct steps but fold them together. The standard does not require distinct phases;
however, the result must be "as if" separate phases are used.
Macro Replacement
Traditional C compilers did not follow the simple sequence of steps described above. Instead,
macros were processed on a moment-by-moment basis and the expansion of complex macros
would vary from system to system. Many macros were not truly portable.
P
age 173
Standard C also allows many simple macros to work correctly. For example:
49
#define allen *allen
will replace all uses of
allen
with
*allen
. Many traditional C compilers would die in the
#define
statement and complain about macro recursion.
Conversion of Macro Arguments to Strings
There was a big argument about the correct operation of the following example:
#define p(a) printf("a = %d\n",a)
p(sue);
It could expand to either:
printf("sue = %d\n",sue);
or to:
printf("a = %d\n",sue);
Traditional C compilers gave the first result. They looked inside quoted strings for possible
macro arguments. Standard C will produce the second result. String literals are not examined.
To allow the intended effect of the above macro, the
#
operator was
invented. In Standard C,
you would write:
#define p(a) printf(#a " = %d\n",a)
The
#
sign converts the argument into a string literal. The concatenation of string literals
produces the desired result.
Token Pasting
In some traditional C compilers, the code:
#define paste(a,b) a/**/b
*
x = paste(x,1) + paste(y,2);
would produce:
x = xl + y2;
By Standard C rules, this code would produce:
x = x 1 + y 2;
which is not what you want. To get the correct result, use the new Standard C
##
operator. The
Standard C macro would be:
* You cannot just put a next to b because ab is a unique symbol.
P
age 174
#define paste(a,b) a ## b
Since
##
is a real operation and not an artifact of the preprocessor, it is not sensitive to white
space.
73
New Directives
The
#elif
directive has been added as a shorthand form of the
#else #if
preprocessor
sequence.
The identifier
defined
is reserved during the
#if
or
#elif
so that:
#if defined(NULL)
#if !defined(TRUE)
is equivalent to:
#ifdef NULL
#ifndef TRUE
In addition to the two legal ways of including a header file:
#include <header>
#include "file"
it is now legal to write:
#include MACRO
where
MACRO
expands to one of the first two cases.
Namespace Issues
In traditional C implementations, the contents of the various header files would vary from
system to system. This caused portability problems. There was no way to protect yourself from
implementation-defined symbols in the headers you used. Standard C solv
es this problem by
defining a strict set of rules on the use of names. There are a set of names reserved to various
parts of the implementation. If you avoid those names, there will be no conflicts.
Names Reserved by the C Language
The Standard C language defines a list of keywords. These have special meaning to the
compiler and may not be used for any other purpose. They are:
auto
double
int
struct
break
else
long
switch
case
enum
register
typedef
char
extern
return
union
const
float
short
unsigned
continue
for
signed
void
default
goto
sizeof
volatile
do
if
static
while
P
age 175
Names Reserved by Header Files
The C library uses many identifiers that begin with an underscore. Although there are places
120
where one can safely use an identifier that begins with an underscore, the rules are complex
and it is better just to avoid them. Some of these are POSIX restricti
ons, not part of standard C.
Using
#include
to read a header file causes a set of symbols to be reserved. These symbols
depend on the header file and are listed in the following table:
Header File
Reserved Names
<ctype.h>
All symbols starting with
is
or
to
.
<dirent.h>
All symbols starting with
d_
.
<errno.h>
All symbols starting with
E
followed by any uppercase letter or a digit.
<fcntl.h>
All symbols starting with
l_
.
*
Symbols starting w
ith
F_
,
0_
, or
S_
may be
used if an
#undef
is done for each symbol prior to any other use.
<grp.h>
All symbols starting with
gr_
.
<limits.h>
All symbols ending with
_MAX
.
<locale.h>
All symbols starting with
LC_
followed by an uppercase letter.
<math.h>
Th
e names of existing math functions followed by an
f
or an
l
.
<pwd.h>
All symbols starting with
pw_
.
<signal.h>
All symbols starting with
sa_
. Symbols starting with
SIG
or
SA_
may be
used if an
#undef
is done for each symbol prior to any other use.
<string.
h>
All symbols starting with
mem
,
str
, or
wcs
.
<sys/stat.h>
All symbols starting with
st_
. Symbols starting with
S_
may be used if an
#undef
is done for each symbol prior to any other use.
<sys/times.h>
All symbols starting with
tms_.
* That is lowercase letter "l" followed by an underscore.
P
age 176
Header File
Reserved Names
<termios.h>
All symbols starting with
c_
. Symbols starting with
V,I,O,
or
TC
may be
used if an
#undef
is done for each symbol prior to any other use. Symbols
starting with
B
followed by a digit may be used if an
#undef
is done fo
r
each symbol prior to any other use.
Any POSIX header
All symbols ending with
_t
.
148
The Header files section in the Reference Manual of this book spells out the contents of the
header files in detail. The POSIX interpretation committee has ruled that POSIX 1003.1-1988
is ambiguous. A system conforming to the 1988 standard may define any P
OSIX symbol in any
POSIX header. Systems meeting the 1990 standard must obey the stricter rules set forth in the
Header Files section.
C Library Functions
The Standard C library defines a large number of functions. It is legal for a system to load
every function in the library even if you do not use it in your program. You should consider the
following names reserved by the Standard C library:
abort
fprintf
longjmp
strcat
abs
fputc
malloc
strchr
acos
fputs
mblen
strcoll
asctime
fread
mbstowcs
strcopy
asin
free
mbtowc
strcspn
atan
freopen
memchr
strerror
atan2
frexp
memcmp
strftime
atexit
fscanf
memcpy
strlen
atof
fsetpos
memmove
strncat
atoi
fte
ll
memset
strncmp
atol
fwrite
mktime
strncpy
bsearch
getc
modf
strpbrk
ceil
getchar
perror
strrchr
calloc
getenv
printf
strspn
clearerr
gets
putc
strstr
clock
gmtime
putchar
strtod
cos
isalnum
puts
strtok
cosh
isalpha
qsort
strtol
ctime
iscntrl
raise
strto
ul
difftime
isdigit
rand
strxfrm
div
isgraph
realloc
system
exit
islower
remove
tan
exp
isprint
rename
tanh
fabs
ispunct
rewind
time
fclose
isspace
scanf
tmpfile
feof
isupper
setbuf
tmpnam
ferror
isxdigit
setlocale
tolower
fflush
labs
setvbuf
toupper
fgetc
ldexp
sin
ungetc
fgetpos
ldiv
sprintf
vfprintf
fgets
localeconv
sqrt
vprintf
floor
localtime
srand
vsprintf
fmod
log
strcmp
wcstombs
fopen
logl0
sscanf
wctomb
133
P
age 177
POSIX Library Functions
The POSIX standard defines the following library functions:
access
fdopen
mkdir
sigpending
alarm
fork
mkfifo
sigprocmask
asctime
fpathconf
open
sigsetjmp
cfgetispeed
fstat
opendir
sigsuspend
cfgetospeed
getcwd
pathconf
sleep
cfsetispeed
getegid
pause
stat
cfsetospeed
getenv
pipe
sysconf
chdir
geteuid
read
tcdrain
c
hmod
getgid
readdir
tcflow
chown
getgrgid
rename
tcflush
close
getgrnam
rewinddir
tcgetattr
closedir
getgroups
rmdir
tcgetpgrp
creat
getlogin
setgid
tcsendbreak
ctermid
getpgrp
setjmp
tcsetattr
cuserid
getpid
setlocale
tcsetpgrp
dup
getppid
setpgid
time
du
p2
getpwnam
setuid
times
execl
getpwuid
sigaction
ttyname
execle
getuid
sigaddset
tzset
execlp
isatty
sigdelset
umask
execv
kill
sigemptyset
uname
execve
link
sigfillset
unlink
execvp
longjmp
sisismember
utime
_exit
lseek
siglongjmp
waitpid
fcntl
write
Avoiding Pitfalls
The chances of stumbling over a reserved C or POSIX name can be minimized by following a
few simple rules:
1.
Start each source file with the line:
#define _POSIX_SOURCE 1
All symbols not defined by Standard C or the POSIX standard will be hidden, except those
with leading underscores.
*
2.
Following the definition of
_POSIX_SOURCE
, place the
#include
statements for any
standard header files.
3.
Use
#undef
for any symbols that are used by your application and reserved by the header
files you use.
4.
After the standard
#include
statements, place any
#include
or
#define
statements
45
for this application. The local definitions will redefine any symbol defined in the standard
headers.
* There is also the reverse of this pitfall. If you forget the
_POSIX_SOURCE
but specify Standard C,
all of the POSIX symbols will be hidden.
P
age 178
Of course, this practice will merely prevent problems from identifiers that we do not know
about. We can't redefine a macro and still use its standard definition.
Here is a brief example:
#define _POSIX_SOURCE 1
#include <stdio.h>
#include <termios.h>
#include <limits.h>
/*
* #undef symbols that I use in my program, but are
* reserved to POSIX headers.
* See
Headers section in the reference part of
* this book.
*/
#undef B52 /* <termios.h> reserves B<digit> */
#undef BOMB_MAX /* <limits.h> reserves ???_MAX */
#undef SIGMA /* <signal.h>
reserves SIG??? */
/*
* Now, my application specific headers
*
#include "planes.h"
#include "ships.h"
/* ~
* Now all of the #defines local to this file
*/
#define B52 "Bomber"
#define BOMBMAX 60
#define SIGMA 2.378
rest of the program goes here. . .
Function Prototypes
Standard C adds some additional checking to the traditional C language. The
argument
declarations
can now define the type of each argument. So, we might have a definition as
follows:
long sum(short count, long *vect[])
73
This call defines a function called sum which returns a
long
.
The
sum()
function has two
arguments, a short called
count
and a pointer to an array called
vect
.
The identifiers
vect
and count are for descriptive purposes only and do not go beyond the scope
of
sum
.
If the parameter list terminates with an ellipsis (
,
...
), no information about the number or
types of the parameters after the comma is supplied. It is used for functions
P
age 179
with a variable number of arguments. If a function takes no arguments, the parameter list should
have
void
as the only entry.
If a function declaration does not include arguments, as in:
double julie();
then nothing is to be assumed about the arguments of
julie
, and parameter checking is turned
off. This allows older C programs to compile with new compilers, but it is a bad idea to use it
with new programs. If the function takes arguments, declare them; i
f it takes no arguments, use
void
.
Avoiding Pitfalls
The syntax of function prototypes was borrowed almost completely from C++. Here are some
rules for good use:
1.
The parameters are comma separated, instead of semicolon terminated as other
declarations are.
2.
The last parameter in a prototype must not be followed by a comma. This is different from
enum
and
struct
where the trailing comma is optional.
3.
If you use a prototype in one place, use them every place!
The compiler is allowed to
generate better code using the knowledge gained from the prototypes. For example, if your
header contains:
int myfunc(char a, unsigned short b, float f);
but the code for
myfunc
is:
myfunc()
char a;
unsigned short b;
float f;
{
. . .
}
the code generated for the call to
myfunc
may not match what the function is expecting.
4.
You do not have to use parameter names in function prototypes. However, they may make
the operation of the function much clearer. Consider:
int copy(char *,char *);
Documents you may be interested
Documents you may be interested