The programs in this book are based on the Single UNIX Specification, Version 3. We refer to this specification by its IEEE name, POSIX. Essentially identical documents have been published by three standards organizations, the IEEE [49, 50, 51, 52], ISO/IEC [57], and the Open Group [89]. The IEEE and ISO/IEC publish print and electronic versions of the standard that are available for a fee. The Open Group publishes the standard on CD-ROM, but this organization also makes the standard freely available on their web site, http://www.UNIX-systems.org/single_unix_specification/. You must register the first time you enter the web site, but it is open to the public at no charge. The standard is organized into the following four parts.
Base Definitions: general terms and concepts, header files
System Interfaces: definitions of functions
Shell and Utilities: definitions of commands
Rationale: discussion of historical information and why features were or were not included in the standard
Use section 2 of the standard to find out about system calls and library functions such as pipe
and socket
. Look in section 3 for information about commands, such as ls
and cat
, that can be executed from the shell.
Most UNIX systems have online documentation called the man pages. Here, “man” stands for “manual” as in system manual. The man
utility displays these pages of online documentation in a readable format.
SYNOPSIS man [-k] name POSIX:Shell and Utilities
Unfortunately, the standard does not require much functionality from the manual facility. If name
is a standard utility, the standard requires only that a message describing its syntax, options and operands be displayed. The -k
option lists the summaries of manual entries that contain name
.
Most UNIX implementations divide the manual pages into sections, with typical section numbers shown in Table A.1. The first three sections are of most interest to us. Most implementations of man
display only the information about the first occurrence of an entry. For example, write
of section 1 is a command that can be executed from the shell to send a message to a terminal of another user. Users of this book would probably be more interested in the write
description of section 2, which is the library function described in Section 4.2. Most implementations of man
provide an option called -a
to display all manual entries and an option called -s
or -S
to display only entries from a given section for the manual.
Table A.1. Typical sections numbers for UNIX man pages.
section | contents |
---|---|
1 | user commands |
2 | system calls |
3 | C library functions |
4 | devices and network interfaces |
5 | file formats |
6 | games and demos |
7 | environments, tables and |
8 | system maintenance |
Example A.1.
The following command can be used under Solaris to display the manual entry for write
from section 2.
man -s 2 write
Under Linux or Mac OS X the corresponding command is the following.
man -S 2 write
Figure A.1 shows the typical output of the man
utility when the man tee
command executes. The first line or header line of the man page gives the name of the command followed in parentheses by the man page section number. The tee(1)
in Figure A.1 refers to the tee
command described in section 1 of the man pages. Do not try to execute tee(1)
. The (1) suffix is not part of the command name, rather it is a man page section indicator.
Example A.1. Typical man page listing for the tee
command.
tee(1) User Commands tee(1) NAME tee - duplicate standard output SYNOPSIS tee [ -ai ] [ file ... ] DESCRIPTION The tee utility shall copy standard input to standard output, making a copy in zero or more files. The tee utility shall not buffer output. The options determine if the specified files are overwritten or appended to. OPTIONS The following options shall be supported. -a Append the output to the files rather than overwriting them. -i Ignore the SIGINT signal. OPERANDS The following operands are supported: file A pathname of an output file. Processing of at least 13 file operands shall be supported. ENVIRONMENT VARIABLES ... EXIT STATUS The following exit values are returned: 0 The standard input was successfully copied to all output files. >0 The number of files that could not be opened or whose status could not be obtained. APPLICATION USAGE The tee utility is usually used in a pipeline, to make a copy of the output of some utility. The file operand is technically optional, but tee is no more useful than cat when none is specified. EXAMPLES Save an unsorted intermediate form of the data in a pipeline: ... | tee unsorted | sort > sorted SEE ALSO cat(1), attributes(5), environ(5)
Each man page covers some aspect of UNIX (e.g., a command, a utility, a library call). The individual man pages are organized into sections like the tee
man page of Figure A.1. Some common section titles are given in Table A.2.
Table A.2. Typical sections of a UNIX man page.
section title | contents |
---|---|
| title for the individual man page |
| one-line summary |
| description of usage |
| values returned on exit from a command |
| discussion of what the command or function does |
| possible return values |
| summary of |
| list of the system files that the command or function uses |
| list of related commands or additional sections of the manual |
| list of relevant environment variables |
| information on unusual usage or implementation features |
| list of known bugs and caveats |
The name section of a man page lists the names of the items described on that man page. The man pages contain information about many types of items. The man page on write(1)
describes a command, and the man page on write(2)
describes a library function. The two write
entries have completely different purposes. Look at the synopsis section to determine which write
you want. The synopsis summarizes how a command or function is invoked. The synopsis for a library function has function prototypes along with the required header files. The write(2)
function is called from a C program. In contrast, write(1)
is executed from the command prompt or from a shell script.
In addition to the standard documents and manual pages, many UNIX vendors make detailed documentation accessible through the Web. Sun provides documentation at http://docs.sun.com. The Linux Documentation Project web page, http://tldp.org/, has the Linux manual pages, HOWTO guides and other information. Apple provides documentation for Mac OS X on their developer’s web site, http://developer.apple.com.
The C compiler, cc
, translates a collection of C source programs and object files into either an executable file or an object file. On your system, the compiler may have another name, such as gcc
. The cc
command may be a symbolic link to another executable.
Compilation proceeds in stages. In the first stage, a preprocessor expands macros and includes header files. The compiler then makes several passes to translate the code, first to the assembly language of the target machine and then into machine code. The result is an object module, which has machine code and tables of unresolved references. The final stage of compilation links a collection of object modules together to form the executable module with all references resolved. An executable file is ready to be loaded and run. The executable contains exactly one main
function.
Example A.2.
The following command compiles mine.c
and produces the executable mine
.
cc -o mine mine.c
If the -o mine
option is omitted, the C compiler produces an executable called a.out
. Use the -o
option to avoid the noninformative default name.
Example A.3.
The following mine.c
source file contains an undefined reference to the serr
function.
void serr(char *msg); int main(void) { serr("This program does not do much "); return 0; }
When mine.c
of Example A.3 is compiled as in Example A.2, the C compiler displays a message indicating that serr
is an unresolved reference and does not produce an executable.
Programs are usually organized into multiple source files that must be linked together. You can compile all the source files with a single cc
command. Alternatively, you can compile the source into separate object modules and link these object modules to form an executable module in a separate step.
Example A.4.
Suppose that the serr
function is contained in the source file minelib.c
. The following command compiles the mine.c
source file of Example A.3 with minelib.c
to produce an executable module called mine
.
cc -o mine mine.c minelib.c
The -c
option of cc
causes the C compiler to produce an object module rather than an executable. An object module cannot be loaded into memory or executed until it is linked to libraries and other modules to resolve references. The C compiler does not complain about unresolved references in object modules. A misspelled variable or missing library function might not be detected until that object module is linked into an executable.
Example A.5.
The following command produces the object module mine.o
.
cc -c mine.c
When the -c
option is used, the C compiler produces an object module named with the .o
extension. The mine.o
produced by the cc
command of Example A.5 can later be linked with another object file (e.g., minelib.o
) to produce an executable.
Example A.6.
The following command links the object modules mine.o
and minelib.o
to produce the executable mine
.
cc -o mine mine.o minelib.o
Before a function such as serr
in Example A.3 is referenced, it should either be defined or have a prototype. Often, prototypes are contained in header files.
Before compilation, the C preprocessor copies the header files specified by #include
statements into the source. By convention, header files have a .h
extension. Put declarations of constants, types and functions in header files. Do not put variable declarations in header files, because this can result in multiply-defined variables. The next exercise illustrates the difficulties caused by placing variable declarations in header files.
Example A.7.
What happens if you execute the following commands?
cc -o mystuff my.c mylib.c mystuff
The file myinc.h
contains the following segment.
#include <stdio.h> static int num; void changenum(void);
The file my.c
contains the following main
program.
#include "myinc.h" int main (void) { num = 10; changenum(); printf("num is %d ", num); return 0; }
The file mylib.c
contains the following function.
#include "myinc.h" void changenum(void) { num = 20; }
Answer:
Both my.c
and mylib.c
contain a num
variable because its definition appears in myinc.h
. The call by the main
program to changenum
does not affect the value of the variable num
defined in my.c
. The mystuff
program outputs 10
rather than 20
.
Enclose system-defined header files in angle brackets (as in #include <stdio.h>
) since the compiler then looks in the standard place for the file. The standard place depends on the system, but the man page for cc
usually describes how the standard search occurs. The /usr/include
directory holds many of the standard header files. The files in this directory often include other .h
files from subdirectories beneath /usr/include
. The /usr/include/sys
directory is a standard location for many of the .h
files needed for this book. Be sure to include the header files specified by the man page synopsis when using a library function. Enclose personal header filenames in double quotes as follows.
#include "myinc.h"
The quotes tells the compiler to look for the header file in the directory containing the source file before looking in the standard place.
Example A.8.
A program uses the error symbol EAGAIN
in conjunction with a call to write
. The compiler complains that EAGAIN
is not defined. Now what?
Answer:
Try the following steps to solve the problem.
Make sure to include all the header files mentioned in the synopsis for write
. The man page specifies the header file <unistd.h>
.
Buried somewhere in the man pages is a statement mentioning that errno.h
must be included in programs that refer to error symbols. If the program includes errno.h
, the problem is solved.
If the errno.h
statement in the man page escapes your notice, look for the symbol EAGAIN
directly in the system header files by using
cd /usr/include grep EAGAIN *
The grep
command searches for the string EAGAIN
in all of the files in the directory /usr/include
. Unfortunately, the EAGAIN
symbol is not in any of the files in /usr/include
.
Change to the /usr/include/sys
directory and try grep
again. The following is a typical response to grep
.
errno.h:#define EAGAIN 11 errno.h:#define EWOULDBLOCK EAGAIN
It might be tempting to eliminate the problem by including the file sys/errno.h
in the source, but what the compiler really wants is errno.h
. Using errno.h
directly is better because it includes sys/errno.h
and also contains additional definitions.
Just because a program has the right header files does not mean that your troubles are over. A header file gives symbol declarations and function prototypes, but it does not supply the actual code for the function call.
Example A.9.
The mylog.c
source file calculates the logarithm of a value. After including math.h
in that source file, the user compiles the program and receives an error message that the log
function could not be found. Why not?
Answer:
The math.h
header file just tells the C compiler what the form (prototype) of the log
function is. It does not actually supply the function.
Compilation takes place in two distinct phases. In the first phase, the compiler translates each C source file into object code. The cc -c
option stops at this point. Object code is not ready to execute because the program may reference outside items that have not been located. To produce an executable module, the compile must find all the undefined symbols (unresolved external references). The cc
compiler calls the link editor, ld
, to accomplish this task.
Example A.10.
The following command compiles the mylog.c
source file with the system math library to produce an executable called mylog
.
cc -o mylog mylog.c -lm
To use C mathematics library functions, put #include <math.h>
in the source file and also specify that the program should be linked with the math library (-lm
) when it is compiled.
The names of libraries are specified by the -l
option. The object files are processed in the order in which they appear on the cc
command line, so the location of -l
on the cc
line is significant. It should come after the object files because only those entries that match unresolved references are loaded. By default, the link editor automatically searches the standard C library.
Example A.11.
What happens if the math library in Example A.10 is linked, but the header file math.h
is not included in the source?
Answer:
The compiler assumes that log
has a return value of type int
rather than double
. If the program calls the log
function, the calculation produces an incorrect numerical result. The compiler may not produce an error or warning message. However, lint
(Section A.4) reports that log
has been implicitly declared to return int
.
Example 1.12.
The following linking command processes the object files in the order my.o
, the math library, and then mylib.o
.
cc -o my my.o -lm mylib.o
The link editor includes only those objects in the library that correspond to unresolved references. Thus, if mylib.o
contains a reference to the math library, that reference is not resolved by this command.
The -lx
option is short for either libx.a
(a library archive) or libx.so
(a shared library). Which is the default depends on how the system is set up. Many compilers allow you to specify -Bstatic -lx
in the cc
command for a library archive and -Bdynamic -lx
for a shared library. The compiler scans the shared libraries for references, but it does not actually put the functions in the executable output file. Instead, the runtime system loads them by dynamic loading and binding.
Several versions of a particular library may coexist on a system—at least one for each version of the C compiler. A typical search order for libraries is the following.
-L
directories specified on the cc
line
Directories in the LD_LIBRARY_PATH
environment variable
Standard library directories (e.g., /usr/lib
)
The -L
option of cc
explicitly specifies pathnames for directories to be searched for libraries. The LD_LIBRARY_PATH
environment variable specifies default pathnames for searching for load libraries. Generally, LD_LIBRARY_PATH
includes pathnames for the directories in which the compilers are installed, as well as directories such as /usr/local/lib
. Your system administrator has probably set up the LD_LIBRARY_PATH
variable for using the standard compilers.
Before the Single UNIX Specification, there were several incompatible UNIX standards, and vendors would use conditional compilation to adjust for these differences. The preprocessor can produce different code for the compiler from a single source file through the use of the #if, #ifdef
and #ifndef
preprocessor statements. Such conditional compilation can be used to allow a program to be compiled under different implementations or in different environments.
Example A.13.
The UICI restart library sets errno
to ETIME
when the function waitfdtimed
times out. Some systems do not define ETIME
but instead use the error ETIMEDOUT
. The file restart.h
solves this problem with the following.
#ifndef ETIME #define ETIME ETIMEDOUT #endif
If ETIME
is not already defined, it is defined as ETIMEDOUT
.
ETIME
and ETIMEDOUT
are examples of simple macros specified by a #define
statement. The preprocessor replaces these defined constants with their values before passing the code to the C compiler.
Most C compilers have a -D
option that allows the setting of macros at compile time.
Example A.14.
The Linux header files provide a number of options to support different standards and implementations. Linux uses the constant _GNU_SOURCE
for many of the features that are now part of the Single UNIX Specification. If this constant is defined, then these features are turned on. Some of the programs in this book require this constant to be defined when the programs are compiled under Linux. To compile the program myprog.c
with this constant defined, use the following command.
cc -D_GNU_SOURCE -o myprog myprog.c
This causes the constant _GNU_SOURCE
to be defined with the default value of 1, as if the following statement appeared as the first line of the source file.
#define _GNU_SOURCE 1
Example A.15.
The UICI name library in Section C.2 gives four implementations of the function addr2name
and name2addr
, using conditional compilation to choose one of the implementations. The general format of the code is as follows.
#ifndef REENTRANCY #define REENTRANCY_NONE #endif #if REENTRANCY==REENTRANT_NONE /* default code using gethostbyname and gethostbyaddr */ #elif REENTRANCY==REENTRANT_R /* code using gethostbyname_r and gethostbyaddr_r */ #elif REENTRANCY==REENTRANT_MUTEX /* code using mutex locks */ #elfi REENTRANCY==REENTRANT_POSIX /* code using getnameinfo and getaddrinfo */ #endif
The first three lines guarantee that REENTRANCY
has its default value if it is not otherwise defined.
Example A.16.
Execute the following command to compile the program client.c
with the restart library, the UICI library, and the UICI name library. Use the getnameinfo
and getaddrinfo
functions.
cc -DREENTRANCY=REENTRANT_POSIX -o client client.c restart.c uiciname.c uici.c
Additional libraries may be needed on your system.
The make
utility, which allows users to incrementally recompile a collection of program modules, is convenient and helps avoid mistakes. To use make
, you must specify dependencies among modules in a description file. The make
utility uses the description file to see if anything needs updating.
The description file specifies dependency relationships that exist between targets and other components. Lines starting with #
are comments. The dependencies in the description file have the following form.
target: components TAB rule
The first line is called a dependency, and the second line is called a rule. The first character on a rule line in a description file must be the TAB character. A dependency may be followed by one or more rule lines.
The default description filenames are makefile
and Makefile
. When the user types make
with no additional arguments, the make
utility looks for makefile
or Makefile
in the current directory to use as its description file.
Example A.17.
In Example A.6, the executable mine
depends on the object files mine.o
and minelib.o
. The following description specifies that dependency relationship.
mine: mine.o minelib.o cc -o mine mine.o minelib.o
The dependency relationship specifies that the target mine
should be updated by executing the rule cc -o mine mine.o minelib.o
if either mine.o
or minelib.o
has been modified since mine
was last changed,
Example A.18.
A makefile target may depend on components that are themselves targets. The following makefile
description file has three targets.
my: my.o mylib.o cc -o my my.o mylib.o my.o: my.c myinc.h cc -c my.c mylib.o: mylib.c myinc.h cc -c mylib.c
The target my
depends on the targets my.o
and mylib.o
. Just type make
to do the required updates.
Sometimes it is helpful to visualize the dependencies of a description file by a directed graph. Use graph nodes (with no duplicates) to represent the targets and components. Draw a directed arc from node A to node B if target A depends on B. A proper description file’s graph should have no cycles. Figure A.2 shows the dependency graph for the description file of Example A.18.
Description files can also contain macro definitions of the following form.
NAME = value
Whenever $(NAME)
appears in the description file, make
substitutes value
before processing. Do not use tabs in macros.
Example A.19.
The following description file uses a macro to represent the compiler options. With this definition, the compiler options need only be changed in a single place rather than in the entire file.
OPTS = -g my: my.c my.h cc $(OPTS) -o my my.c
The make
command also allows the name of a target to be specified on the command line. In this case, make
updates only the specified target. When developing multiple targets in the same directory (e.g., send and receive programs), use this feature to debug one target at a time. If no targets are explicitly specified on the command line, make
checks only the first target in the description file. Often, a description file has a first target called all
that depends on all the other targets.
Example A.20.
The following command causes make
to update only the target my
.
make my
The command of Example A.20 does not interpret my
as a description file but as a target within the default description file (either makefile
or Makefile
in the current directory).
Use the -f
option with make
for description files with names other than makefile
or Makefile
.
This section discusses the lint
utility, debuggers, the truss
utility and profiling.
The lint
utility finds errors and inconsistencies in C source files. The lint
program performs type checking, tries to detect unreachable statements, and points out code that might be wasteful or nonportable; lint
also detects a variety of common errors, such as using =
instead of ==
or omitting &
in arguments of scanf
. You should call lint
for all programs. Pay attention to the resulting warning messages, since lint
is pickier than the C compiler in many areas. The C compiler presumes that programs have already been linted and is usually implemented to be fast rather than fussy.
Example A.22.
Add the following lines to the description file of Example A.18 to lint the sources.
lintall: lint my.c mylib.c > my.lint
Type make lintall
to lint the programs. The output of lint
is in my.lint
.
Example A.23.
How should the following lint
message be interpreted?
implicitly declared to return int: (14) strtok
Answer:
This lint
message warns that the program did not include the string.h
header file associated with strtok
appearing on line 14 of the source file. Lacking information to the contrary, the compiler assumes that strtok
returns int
. Unfortunately, strtok
returns char*
. The lack of header can lead to disastrous results at execution time.
Example A.24.
How should the following lint
message be interpreted?
(5) warning: variable may be used before set: p
Answer:
This message usually appears when the program uses a pointer before setting its value, as in the following code segment.
char *p; scanf("%s", p);
The pointer p
is not pointing to an appropriate character buffer. The code may compile, but the program will probably produce a segmentation error when executed.
Debuggers are runtime programs that monitor and control the execution of other programs. Common debuggers found in UNIX environments are dbx
, adb
, sdb
and debug
. Debuggers allow a user to single-step through a program and monitor changes to specified variables. To use a debugger, compile the program with the -g
option.
Example A.25.
Compile the program my.c
with the -g
option as follows to instrument the executable for debugger control.
cc -g -o my my.c
Run my
under the dbx
debugger by typing the following command.
dbx my
The debugger responds with the following prompt.
(dbx)
Respond with help
for a list of commands or run
to run the program. Set a stopping point with stop
, or turn on tracing when a variable changes, by typing trace
before typing run
.
Many programmers, especially beginning programmers, find debuggers useful for pointer problems. Some debuggers have graphical user interfaces that make them easier to use. Standard debuggers are less useful in a concurrent environment, in which processes interact or timing can change the behavior of a program. Thread debuggers are also available on a limited basis. Debuggers may help you find a particular execution error, but using a debugger is no substitute for having a program test plan. Good error trapping for function calls is probably the most valuable debugging strategy to follow.
For runtime debugging, the truss
command is useful if it is available. The truss
command produces a trace of system calls that are made and the signals delivered while a particular process is running. Use the -f
option with truss
to trace the calls of all children of the process. The truss
command is not part of POSIX and is not available on all systems.
Example A.26.
Suppose that a program called dvips
is installed on a system and that this program accesses the psfonts.map
file. You have placed a copy of psfonts.map
in the bin
subdirectory of your home directory. When you run the program, you receive the following error message.
unable to open file
How can you figure out how to correct the problem?
Answer:
Try executing the following command (from a C shell).
truss dvips -f t.dvi |& grep psfonts.map
The truss
program runs the command dvips -f t.dvi
, and grep
displays the output lines containing psfonts.map
. The |&
argument causes both the standard output and the standard error of truss
to be piped to the standard input of grep
. The output might appear as follows.
open("./psfonts.map", O_RDONLY, 0666) Err#2 ENOENT open("/usr/local/tex/dvips/psfonts.map", O_RDONLY, 0666) Err#2 ENOENT
The output reports that the program first looked for psfonts.map
in the current directory and then in the directory /usr/local/tex/dvips
. Copy the psfonts.map
to /usr/local/tex/dvips
and everything should be ready to go!
Most C compilers have options for profiling programs. Profilers accumulate statistical information such as execution times for basic blocks and frequency of calls. Consult the man pages for prof
, gprof
, monitor
, profil
and tcov
as well as for cc
to obtain additional information about profiling.
Programmers are often confused about the meaning of the keyword static
, in part because C uses the word two different ways. The main points to remember are the following.
If static
is applied to a function, that function can only be called from the file in which it is defined.
If a variable definition appears outside any block, the variable exists for the duration of the program. If static
is applied, the variable can only be accessed from within the file containing the definition. Otherwise, the variable can be accessed anywhere in the program.
If a variable is defined inside a block, it can only be accessed within the block. If static
is applied, the variable exists for the duration of the program and it retains its value when execution leaves the block. Otherwise, the variable is created when the block is entered, and it is destroyed when execution leaves the block. Such a variable needs to be explicitly initialized before it can be used.
These rules are based on C’s notion of scope of an identifier and linkage, which we now discuss.
According to the ISO C standard, “An identifier can denote an object; a function; a tag or member of a structure, union, or enumeration; a typedef name; a label name; a macro name; or a macro parameter.” [56, section 6.2.1] Here, we mainly discuss identifiers that are associated with variables and functions.
An identifier can be used only in a region of program text called its scope. If two different entities are designated by the same identifier, their scopes must be disjoint, or one scope must be completely contained in the other. In the inner scope, the other entity is hidden and cannot be referenced by that identifier.
The scope begins at the identifier declaration. If the declaration occurs inside a block, the identifier has block scope and the scope ends at the end of the block. If the declaration occurs outside any block, the identifier has file scope, and the scope ends at the end of the file in which it is declared.
Identifiers declared more than once may refer to the same object because of linkage. Each identifier has a linkage class of external, internal or none. Declarations in a program of a particular identifier with external linkage refer to the same entity. Declarations in a file of a particular identifier with internal linkage represent the same entity. Each declaration of an identifier with no linkage represents a unique entity.
An identifier representing a function has external linkage by default. This means that it can be referenced in any file of the program. Referencing it in a file other than the one in which it is defined requires a function prototype. You can hide a function from other files by giving it internal linkage, using the static
qualifier.
An identifier representing an object (such as a variable) has a linkage class related to its storage class, also called storage duration. The storage duration determines the lifetime of the object, the portion of the program execution for which storage is guaranteed to be reserved for the object. There are three storage durations: static, automatic and allocated. Allocated objects have a lifetime that begins with a successful malloc
or related function and ends when the object is explicitly freed or the program terminates. The lifetimes of other objects are determined by the declaration of the corresponding identifier.
An identifier of an object declared outside any block has static storage class. Objects with static storage class have a lifetime that is the duration of the program. They are initialized once and retain their last stored value. If no explicit initialization is given in the declaration, they are initialized to 0. As with functions, these identifiers have external linkage by default but can be given internal linkage by means of the static
qualifier.
An identifier of an object declared inside a block has no linkage. Each identifier denotes a unique object. These identifiers have automatic storage duration by default. Objects with automatic storage class have a lifetime that begins when execution enters the block and ends when execution exits the block. These objects are not initialized by default and do not necessarily retain their last stored value after execution exits the block. If the block is entered through recursion or with multiple threads, each entry into the block creates a distinct object. A variable with automatic storage class is called an automatic variable.
An identifier of an object declared inside a block can be given static storage duration with the static
qualifier. The object then has a lifetime that is the duration of the program and retains its last stored value. If the block is entered through recursion or multiple threads, the same object is used.
Objects with identifiers having static storage duration are often called static variables; those with identifiers having automatic storage duration are called automatic variables.
As described above, the static
qualifier can affect either the storage class or linkage class of an object depending on the context. When static
is applied to a function, it always changes its linkage class from the default of external to internal. Functions do not have a storage duration. For objects declared inside a block, the linkage class is always none and static
changes the storage class from automatic to static. For objects declared outside any block, the storage class is always static and the static
specifier changes the linkage class from external to internal. These rules are summarized in Table A.3.
Table A.3. Effect of using the static
keyword modifier on an object in a C program.
where declared |
|
| storage class | linkage class |
---|---|---|---|---|
inside a block | storage class | yes | static | none |
inside a block | storage class | no | automatic | none |
outside any block | linkage class | yes | static | internal |
outside any block | linkage class | no | static | external |
UNIX SYSTEM V: A Practical Guide, 3rd ed., by Sobell [108] is an up-to-date reference on using the UNIX utilities. UNIX System Administration Handbook, 3rd ed., by Nemeth et al. [86] is an excellent and readable introduction to many of the configuration issues involved in setting up UNIX systems. O’Reilly Press has individual books on many of the topics in this appendix including emacs
[20], the libraries [27], lint
[28], make
[120], and vi
[69].