C Language Reference Manual - 5th Edition
C Language Reference Manual - 5th Edition
007–0701–150
COPYRIGHT
Copyright © 1999, 2002–2003 Silicon Graphics, Inc. All rights reserved; provided portions may be copyright in third parties, as
indicated elsewhere herein. No permission is granted to copy, distribute, or create derivative works from the contents of this electronic
documentation in any manner, in whole or in part, without the prior written permission of Silicon Graphics, Inc.
Gaussian is a trademark of Gaussian, Inc. MIPSpro is a trademark of MIPS Technologies, Inc., and is used under license to Silicon
Graphics, Inc. UNIX and the X device are trademarks of The Open Group in the United States and other countries.
Cover Design By Sarah Bolles, Sarah Bolles Design, and Dany Galgani, SGI Technical Publications.
New Features in This Manual
Information regarding the use of lint-style comments in macros has been added to
Appendix B, "lint-style Comments", page 167.
007–0701–150 iii
Record of Revision
Version Description
007–0701–150 v
Contents
1. An Overview of ANSI C . . . . . . . . . . . . . . . . . . 1
ANSI C . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Strictly Conforming Programs . . . . . . . . . . . . . . . . . . . 1
Name Spaces . . . . . . . . . . . . . . . . . . . . . . . . 1
Compiling ANSI Programs . . . . . . . . . . . . . . . . . . . 2
Guidelines for Using ANSI C . . . . . . . . . . . . . . . . . . . 2
Compiling Traditional C Programs . . . . . . . . . . . . . . . . . 3
Helpful Programming Hints . . . . . . . . . . . . . . . . . . . . 3
Recommended Practices . . . . . . . . . . . . . . . . . . . . 3
Practices to Avoid . . . . . . . . . . . . . . . . . . . . . . 4
2. C Language Changes . . . . . . . . . . . . . . . . . . . 7
Preprocessor Changes . . . . . . . . . . . . . . . . . . . . . . 7
Replacement of Macro Arguments in Strings . . . . . . . . . . . . . . 8
Token Concatenation . . . . . . . . . . . . . . . . . . . . . 9
Changes in Disambiguating Identifiers . . . . . . . . . . . . . . . . 10
Scoping Differences . . . . . . . . . . . . . . . . . . . . . . 10
Name Space Changes . . . . . . . . . . . . . . . . . . . . . 12
Changes in the Linkage of Identifiers . . . . . . . . . . . . . . . . 12
007–0701–150 vii
Contents
3. Lexical Conventions . . . . . . . . . . . . . . . . . . . 23
Comments . . . . . . . . . . . . . . . . . . . . . . . . . 23
Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Constants . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Integer Constants . . . . . . . . . . . . . . . . . . . . . . 24
Character Constants . . . . . . . . . . . . . . . . . . . . . . 25
Special Characters . . . . . . . . . . . . . . . . . . . . . . 25
Trigraph Sequences (ANSI C Only) . . . . . . . . . . . . . . . . 26
Floating Constants . . . . . . . . . . . . . . . . . . . . . . 27
Enumeration Constants . . . . . . . . . . . . . . . . . . . . . 28
String Literals . . . . . . . . . . . . . . . . . . . . . . . . 28
Operators . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Punctuators . . . . . . . . . . . . . . . . . . . . . . . . . 29
4. Meaning of Identifiers . . . . . . . . . . . . . . . . . . 31
viii 007–0701–150
C Language Reference Manual
Disambiguating Names . . . . . . . . . . . . . . . . . . . . . 31
Scope . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Block Scope . . . . . . . . . . . . . . . . . . . . . . . 32
Function Scope . . . . . . . . . . . . . . . . . . . . . . 32
Function Prototype Scope . . . . . . . . . . . . . . . . . . . 32
File Scope . . . . . . . . . . . . . . . . . . . . . . . . 32
Name Spaces . . . . . . . . . . . . . . . . . . . . . . . . 33
Name Space Discrepancies Between Traditional and ANSI C . . . . . . . . 33
Linkage of Identifiers . . . . . . . . . . . . . . . . . . . . . 34
Linkage Discrepancies Between Traditional and ANSI C . . . . . . . . . . 36
Storage Duration . . . . . . . . . . . . . . . . . . . . . . . 37
Object Types . . . . . . . . . . . . . . . . . . . . . . . . . 38
Character Types . . . . . . . . . . . . . . . . . . . . . . . 38
Integer and Floating Point Types . . . . . . . . . . . . . . . . . . 38
Derived Types . . . . . . . . . . . . . . . . . . . . . . . 40
void Type . . . . . . . . . . . . . . . . . . . . . . . . 40
Objects and lvalues . . . . . . . . . . . . . . . . . . . . . . . 41
5. Operator Conversions . . . . . . . . . . . . . . . . . . . 43
Conversions of Characters and Integers . . . . . . . . . . . . . . . . 43
Conversions of Float and Double . . . . . . . . . . . . . . . . . . 43
Conversion of Floating and Integral Types . . . . . . . . . . . . . . . . 44
Conversion of Pointers and Integers . . . . . . . . . . . . . . . . . 44
Conversion of unsigned Integers . . . . . . . . . . . . . . . . . . 44
Arithmetic Conversions . . . . . . . . . . . . . . . . . . . . . 45
Integral Promotions . . . . . . . . . . . . . . . . . . . . . . 45
Usual Arithmetic Conversions . . . . . . . . . . . . . . . . . . 45
007–0701–150 ix
Contents
x 007–0701–150
C Language Reference Manual
7. Declarations . . . . . . . . . . . . . . . . . . . . . . 69
Storage Class Specifiers . . . . . . . . . . . . . . . . . . . . . 70
Type Specifiers . . . . . . . . . . . . . . . . . . . . . . . . 71
Structure and Union Declarations . . . . . . . . . . . . . . . . . . 72
Bitfields . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Enumeration Declarations . . . . . . . . . . . . . . . . . . . . . 76
Type Qualifiers . . . . . . . . . . . . . . . . . . . . . . . . 77
Declarators . . . . . . . . . . . . . . . . . . . . . . . . . 78
Meaning of Declarators . . . . . . . . . . . . . . . . . . . . . 78
Pointer Declarators . . . . . . . . . . . . . . . . . . . . . . 79
Qualifiers and Pointers . . . . . . . . . . . . . . . . . . . . 79
Pointer-related Command Options . . . . . . . . . . . . . . . . 80
Array Declarators . . . . . . . . . . . . . . . . . . . . . . 81
Function Declarators and Prototypes . . . . . . . . . . . . . . . . 82
Prototyped Functions Summarized . . . . . . . . . . . . . . . . 84
007–0701–150 xi
Contents
Restrictions on Declarators . . . . . . . . . . . . . . . . . . . . 85
Type Names . . . . . . . . . . . . . . . . . . . . . . . . . 86
Implicit Declarations . . . . . . . . . . . . . . . . . . . . . . 87
typedef . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Initialization . . . . . . . . . . . . . . . . . . . . . . . . . 88
Initialization of Aggregates . . . . . . . . . . . . . . . . . . . 89
Examples of Initialization . . . . . . . . . . . . . . . . . . . . 90
8. Statements . . . . . . . . . . . . . . . . . . . . . . . 93
Expression Statement . . . . . . . . . . . . . . . . . . . . . . 93
Compound Statement or Block . . . . . . . . . . . . . . . . . . . 93
Selection Statements . . . . . . . . . . . . . . . . . . . . . . 94
if Statement . . . . . . . . . . . . . . . . . . . . . . . . 94
switch Statement . . . . . . . . . . . . . . . . . . . . . . 95
Iteration Statements . . . . . . . . . . . . . . . . . . . . . . 95
while Statement . . . . . . . . . . . . . . . . . . . . . . 96
do Statement . . . . . . . . . . . . . . . . . . . . . . . . 96
for Statement . . . . . . . . . . . . . . . . . . . . . . . 96
Jump Statements . . . . . . . . . . . . . . . . . . . . . . . 97
goto Statement . . . . . . . . . . . . . . . . . . . . . . . 97
continue Statement . . . . . . . . . . . . . . . . . . . . . 97
break Statement . . . . . . . . . . . . . . . . . . . . . . 98
return Statement . . . . . . . . . . . . . . . . . . . . . . 98
Labeled Statements . . . . . . . . . . . . . . . . . . . . . . . 98
xii 007–0701–150
C Language Reference Manual
007–0701–150 xiii
Contents
MPC_GANG . . . . . . . . . . . . . . . . . . . . . . . . . 120
Communicating Between Threads Through Thread Local Data . . . . . . . . . 120
Synchronization Intrinsics . . . . . . . . . . . . . . . . . . . . . 123
Atomic fetch-and-op Operations . . . . . . . . . . . . . . . . . . 124
Atomic op-and-fetch Operations . . . . . . . . . . . . . . . . . . 125
Atomic compare-and-swap Operation . . . . . . . . . . . . . . . . 126
Atomic synchronize Operation . . . . . . . . . . . . . . . . . . 127
Atomic lock and unlock Operations . . . . . . . . . . . . . . . . . 127
Atomic lock-test-and-set Operation . . . . . . . . . . . . . . . . 127
Atomic lock-release Operation . . . . . . . . . . . . . . . . . . 127
Example of Implementing a Pure Spin-Wait Lock . . . . . . . . . . . . 128
xiv 007–0701–150
C Language Reference Manual
Signals . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Signal Notes . . . . . . . . . . . . . . . . . . . . . . . 145
Diagnostics . . . . . . . . . . . . . . . . . . . . . . . 147
Streams and Files . . . . . . . . . . . . . . . . . . . . . . 148
Temporary Files . . . . . . . . . . . . . . . . . . . . . . . 150
errno and perror . . . . . . . . . . . . . . . . . . . . . . 150
Memory Allocation . . . . . . . . . . . . . . . . . . . . . . 158
abort Function . . . . . . . . . . . . . . . . . . . . . . . 158
exit Function . . . . . . . . . . . . . . . . . . . . . . . 158
getenv Function . . . . . . . . . . . . . . . . . . . . . . 158
system Function . . . . . . . . . . . . . . . . . . . . . . 159
strerror Function . . . . . . . . . . . . . . . . . . . . . . 159
Time Zones and the clock Function . . . . . . . . . . . . . . . . . 159
Locale-Specific Behavior (F.4) . . . . . . . . . . . . . . . . . . . . 160
Common Extensions (F.5) . . . . . . . . . . . . . . . . . . . . . 160
Environment Arguments (F.5.1) . . . . . . . . . . . . . . . . . . 161
Specialized Identifiers . . . . . . . . . . . . . . . . . . . . . 161
Lengths and Cases of Identifiers . . . . . . . . . . . . . . . . . . 161
Scopes of Identifiers (F.5.4) . . . . . . . . . . . . . . . . . . . . 161
Writable String Literals (F.5.5) . . . . . . . . . . . . . . . . . . . 162
Other Arithmetic Types (F.5.6) . . . . . . . . . . . . . . . . . . 162
Function Pointer Casts (F.5.7) . . . . . . . . . . . . . . . . . . . 162
Non-int Bit-Field Types (F.5.8) . . . . . . . . . . . . . . . . . . 162
fortran Keyword (F.5.9) . . . . . . . . . . . . . . . . . . . . 163
asm Keyword (F.5.10) . . . . . . . . . . . . . . . . . . . . . 163
Multiple External Definitions (F.5.11) . . . . . . . . . . . . . . . . 163
Empty Macro Arguments (F.5.12) . . . . . . . . . . . . . . . . . . 163
007–0701–150 xv
Contents
Index . . . . . . . . . . . . . . . . . . . . . . . . . . 171
xvi 007–0701–150
Tables
007–0701–150 xvii
About This Manual
This manual contains a summary of the syntax and semantics of the C programming
language as implemented on SGI workstations. It documents previous releases of the
SGI C compiler as well as the American National Standards Institute (ANSI) C
compiler.
The SGI compiler system supports three modes of compilation: the old 32-bit mode
(-o32 or -32 option), the new 32-bit mode (-n32 option), and the 64-bit mode (-64
option).
For information on compilation modes and general compiler options for the old 32-bit
mode, see the o32(5) man page and the MIPS O32 Compiling and Performance Tuning
Guide.
For information on the new 32-bit mode and 64-bit mode, see the cc(1) man page and
the MIPSpro N32/64 Compiling and Performance Tuning Guide.
The term “traditional C” refers to the dialect of C described in the first edition of The
C Programming Language by Kernighan and Ritchie.
Related Publications
The following documents contain information that may be helpful in porting code to
the newer SGI compilers:
• MIPS O32 Compiling and Performance Tuning Guide
• MIPSpro N32/64 Compiling and Performance Tuning Guide
• MIPSpro N32 ABI Handbook
• MIPSpro 64-Bit Porting and Transition Guide
The following documents contain information about SGI’s implementation of C and
C++:
• C++ Programmer’s Guide
• MIPSpro C and C++ Pragmas
007–0701–150 xix
About This Manual
Several performance evaluation and debugging tools are available to help you
optimize and evaluate your code. See the ProDev WorkShop: Overview for a
description of the different tools that are available.
See the Guides to SGI Compilers and Compiling Tools for an overview of all SGI
compilers, compiler documentation, optimization tools, porting tools, and
performance tools.
In addition to the above SGI documentation, several third party documents contain
additional information which may be helpful. These books can be ordered from any
book vendor:
• Ellis, Margaret A., and Bjarne Stroustrup. The Annotated C++ Reference Manual.
Addison-Wesley Publishing Company, 1990. ISBN 0201514591.
• Josuttis, Nicolai. The C++ Standard Library: A Tutorial and Reference.
Addison-Wesley Publishing Company, 1999. ISBN 0201379260.
• Kernighan, Brian W. and Dennis M. Ritchie. The C Programming Language.
Prentice-Hall, 1988. ISBN 0131103628.
Obtaining Publications
You can obtain SGI documentation in the following ways:
• See the SGI Technical Publications Library at http://docs.sgi.com. Various
formats are available. This library contains the most recent and most
comprehensive set of online books, release notes, man pages, and other
information.
• If it is installed on your SGI system, you can use InfoSearch, an online tool that
provides a more limited set of online books, release notes, and man pages. With
an IRIX system, select Help from the Toolchest, and then select InfoSearch. Or
you can type infosearch on a command line.
• You can also view release notes by typing either grelnotes or relnotes on a
command line.
• You can also view man pages by typing man title on a command line.
xx 007–0701–150
C Language Reference Manual
Conventions
The following conventions are used throughout this document:
Convention Meaning
command This fixed-space font denotes literal items such as
commands, files, routines, path names, signals,
messages, and programming language structures.
variable Italic typeface denotes variable entries and words or
concepts being defined.
user input This bold, fixed-space font denotes literal items that the
user enters in interactive sessions. (Output is shown in
nonbold, fixed-space font.)
[] Brackets enclose optional portions of a command or
directive line.
... Ellipses indicate that a preceding element can be
repeated.
Reader Comments
If you have comments about the technical accuracy, content, or organization of this
publication, contact SGI. Be sure to include the title and document number of the
publication with your comments. (Online, the document number is located in the
front matter of the publication. In printed publications, the document number is
located at the bottom of each page.)
You can contact SGI in any of the following ways:
• Send e-mail to the following address:
techpubs@sgi.com
• Use the Feedback option on the Technical Publications Library Web page:
http://docs.sgi.com
• Contact your customer service representative and ask that an incident be filed in
the SGI incident tracking system.
007–0701–150 xxi
About This Manual
xxii 007–0701–150
Chapter 1
An Overview of ANSI C
This chapter briefly discusses the scope of the standard and lists some programming
practices to avoid and some practices to use.
ANSI C
The ANSI standard on the C programming language is designed to promote the
portability of C programs among a variety of data-processing systems. To accomplish
this, the standard covers three major areas: the environment in which the program
compiles and executes, the semantics and syntax of the language, and the content and
semantics of a set of library routines and header files.
Name Spaces
In addition to knowing which features of the language and library you can rely on
when writing portable programs, you must be able to avoid naming conflicts with
support routines used for the implementation of the library. To avoid such naming
conflicts, ANSI divides the space of available names into a set reserved for the user
and a set reserved for the implementation. Any name is in the user’s name space if it
meets these three requirements (this rule is given for simplicity; the space of names
reserved for the user is actually somewhat larger than this):
007–0701–150 1
1: An Overview of ANSI C
2 007–0701–150
C Language Reference Manual
• Use the switch -wlint (-o32 mode only) to get lint-like warnings about the
compiled source. This option provides lint-like warnings for ANSI and -cckr
modes and can be used together with the other cc options and switches.
• Remember that the default compilation mode is shared and the libraries are shared.
Recommended Practices
Follow these recommendations as you code:
• Always use the appropriate header file when declaring standard external
functions. Avoid embedding the declaration in your code. This avoids inconsistent
declarations for the same function.
• Always use function prototypes, and write your function prologues in function
prototype form.
• Use the offsetof() macro to derive structure member offsets. The offsetof()
macro is in <stddef.h>.
007–0701–150 3
1: An Overview of ANSI C
Practices to Avoid
Avoid the following as you code:
• Never mix prototyped and nonprototyped declarations of the same function.
• Never call a function before it has been declared. This may lead to an
incompatible implicit declaration for the function. In particular, this is unlikely to
work for prototyped functions that take a variable number of arguments.
• Never rely on the order in which arguments are evaluated. For example, what is
the result of the code fragment foo(a++, a, ...)?
• Avoid using expressions with side effects as arguments to a function.
• Avoid two side effects to the same data location between two successive sequence
points (for example, x=++x;).
• Avoid declaring functions in a local context, especially if they have prototypes.
• Never access parameters that are not specified in the argument list unless using
the stdarg facilities. Use the stdarg facilities only on a function with an
unbounded argument list (that is, an argument list terminated with …).
• Never cast a pointer type to anything other than another pointer type or an
integral type of the same size (unsigned long), and vice versa. Use a union
type to access the bit-pattern of a pointer as a nonintegral and nonpointer type
(that is, as an array of chars).
• Do not hack preprocessor tokens (for example, FOO/**/BAR).
4 007–0701–150
C Language Reference Manual
007–0701–150 5
Chapter 2
C Language Changes
This chapter describes changes to the C language, which include the following:
• "Preprocessor Changes", page 7, discusses two changes in the way the
preprocessor handles string literals and tokens.
• "Changes in Disambiguating Identifiers ", page 10, covers the four characteristics
ANSI C uses to distinguish identifiers.
• "Types and Type Compatibility", page 14, describes ANSI C changes to type
promotions and type compatibility.
• "Function Prototypes", page 18, explains how ANSI C handles function
prototyping.
• "External Name Changes", page 20, discusses the changes in function,
linker-defined, and data area names.
• "Standard Headers", page 22, lists standard header files.
Preprocessor Changes
When compiling in an ANSI C mode (which is the default unless you specify
[-cckr]), ANSI-standard C preprocessing is used. The preprocessor is built into the
TM
compiler and is functionally unchanged from the version appearing on IRIX Release
3.10.
The 3.10 version of the compiler had no built-in preprocessor and used two
standalone preprocessors, for -cckr (cpp(1)) and ANSI C (acpp(5)) preprocessing,
respectively. If you compile using the -o32 option, you can activate acpp or cpp
instead of the built-in preprocessor by using the -oldcpp option, and acpp in -cckr
mode by using the -acpp option. SGI recommends that you always use the built-in
preprocessor, rather than cpp or acpp, because these standalone preprocessors may
not be supported in future releases of the compilers.
acpp is a public domain preprocessor and its source is included in
/usr/src/gnu/acpp.
007–0701–150 7
2: C Language Changes
Traditionally, the C preprocessor performed two functions that are now illegal under
ANSI C. These functions are the substitution of macro arguments within string literals
and the concatenation of tokens after removing a null comment sequence.
However, because ANSI C considers a string literal to be an atomic unit, the expected
substitution does not occur. So, ANSI C adopted an explicit preprocessor sequence to
accomplish the substitution.
In ANSI C, adjacent string literals are concatenated. Therefore, this is the result:
"abc" "def" becomes "abcdef".
This concatenation led to a mechanism for quoting a macro argument. When a macro
definition contains one of its formal arguments preceded by a single #, the substituted
argument value is quoted in the output. The simplest example of this is as follows:
In conjunction with the rule of concatenation of adjacent string literals, the following
macros can be defined:
8 007–0701–150
C Language Reference Manual
Blanks prepended and appended to the argument value are removed. If the value has
more than one word, each pair of words in the result is separated by a single blank.
Thus, the ARE macro could be invoked as follows:
Avoid enclosing your macro arguments in quotes, because these quotes are placed in
the output string. For example:
ARE ("fat cows", "big") becomes "\"fat cows\" are \"big\""
No obvious facility exists to enclose macro arguments with single quotes.
Token Concatenation
When compiling [-cckr], the value of macro arguments can be concatenated by
entering
#define glue(a,b) a/**/b
glue(FOO,BAR)
007–0701–150 9
2: C Language Changes
Furthermore, the resulting token is a candidate for further replacement. Note what
happens in this example:
Scoping Differences
ANSI C recognizes four scopes of identifiers: the familiar file and block scopes and
the new function and function prototype scopes.
• Function scope includes only labels. As in traditional C, labels are valid until the
end of the current function.
10 007–0701–150
C Language Reference Manual
• Block scope rules differ from traditional C in one significant instance: the
outermost block of a function and the block that contains the function arguments
are the same under ANSI C.
For example, when compiling the following code, ANSI C complains of a
redeclaration of x, whereas traditional C hides the argument x with the local
variable x, as if they were in distinct scopes:
int f(x);
int x;
{
int x;
x = 1;
}
The int variable name does not conflict with the parameter name because the
parameter went out of scope at the end of the prototype. However, the prototype
is still in scope.
• File scope applies to identifiers appearing outside of any block, function, or
function prototype.
One last discrepancy in scoping rules between ANSI and traditional C concerns the
scope of the function foo() in the following example:
float f;
func0() {
extern float foo() ;
f = foo() ;
}
func1() {
f = foo() ;
}
007–0701–150 11
2: C Language Changes
In the previous example, both mytime and yourtime have file scope. However,
mytime has external linkage, while yourtime has internal linkage. An object can
also have no linkage, as is the case of automatic variables.
12 007–0701–150
C Language Reference Manual
The preceding example illustrates another implicit difference between the declarations
of mytime and yourtime. The declaration of yourtime allocates storage for the
object, whereas the declaration of mytime merely references it.
If mytime is initialized as follows, storage is allocated:
int mytime = 0;
In effect, the second declaration included an implicit extern specification. This is not
true in ANSI C.
Note: Objects with external linkage that are not specified as extern at the end of the
compilation unit are considered definitions, and, in effect, initialized to zero. (If
multiple declarations of the object are in the compilation unit, only one needs the
extern specification.)
The effect of this change is to produce “multiple definition” messages from the linker
when two modules contain definitions of the same identifier, even though neither is
explicitly initialized. This is often referred to as the strict ref/def model. A more
relaxed model can be achieved by using the -common compiler flag.
The ANSI C linker issues a warning when it finds redundant definitions, indicating
the modules that produced the conflict. However, the linker cannot determine
whether the definition of the object is explicit. If a definition is given with an explicit
initialization, and that definition is not the linker’s choice, the result may be
incorrectly initialized objects. This is illustrated in the following example:
module1.c:
int ernie;
module2.c:
int ernie = 5;
007–0701–150 13
2: C Language Changes
chooses the first such module it encounters as the true definition of ernie. This
module may or may not contain the explicitly initialized copy.
These steps are not required in ANSI C. In ANSI C, the operation can be done
entirely in single-precision. (In traditional C, these operations were performed in
single-precision if the [-float] compiler option was selected.)
The second difference in arithmetic expression evaluation involves integral
promotions. ANSI C dictates that any integral promotions be “value-preserving.”
14 007–0701–150
C Language Reference Manual
ANSI C’s value-preserving rules cause each of us and them to be promoted to int,
which is the expression type. The unsignedness-preserving rules, in traditional C,
cause us and them to be promoted to unsigned. The latter case yields a large
unsigned number, whereas ANSI C yields -1. The discrepancy in this case is
inconsequential, because the same bit pattern is stored in the integer i in both cases,
and it is later interpreted as -1.
However, if the case is altered slightly, as in the following example, the result
assigned to f is quite different under the two schemes:
unsigned short us = 1, them = 2;
float f;
test() {
f = us - them;
}
If you use the -wlint option, the compiler will warn about the implicit conversions
from int or unsigned to float.
For more information on arithmetic conversions, see "Arithmetic Conversions", page
45.
b = a * PI;
007–0701–150 15
2: C Language Changes
The result type of b depends on which compilation options you use. Table 2-1, page
16, lists the effects of various options.
Traditional C (compiled with the -cckr option) does not recognize the float qualifier,
f, however. Instead, write the constant definition as follows:
#ifdef __STDC__
#define PI 3.1415926f
#else
#define PI 3.1415926
#endif
16 007–0701–150
C Language Reference Manual
Compatible Types
To determine whether or not an implicit conversion is permissible, ANSI C
introduced the concept of compatible types. After promotion, using the appropriate
set of promotion rules, two non-pointer types are compatible if they have the same
size, signedness, and integer or float characteristic, or, in the case of aggregates, are of
the same structure or union type. Except as discussed in the previous section, no
surprises should result from these changes. You should not encounter unexpected
problems unless you are using pointers.
Pointers are compatible if they point to compatible types. No default promotion rules
apply to pointers. Under traditional C, the following code fragment compiled silently:
int *iptr;
unsigned int *uiptr;
foo() {
iptr = uiptr;
}
Under ANSI C, the pointers iptr and uiptr do not point to compatible types
(because they differ in unsignedness), which means that the assignment is illegal.
Insert the appropriate cast to alleviate the problem. When the underlying pointer
type is irrelevant or variable, use the wildcard type void *.
007–0701–150 17
2: C Language Changes
unsigned) are passed as ints, other integral quantities are not changed, and floating
point quantities are passed as doubles. These rules are also used for arguments in the
variable-argument portion of a function whose prototype ends in ellipses (…).
If a prototype is in scope, an attempt is made to convert each argument to the type
indicated in the prototype prior to the call. The types of conversions that succeed are
similar to those that succeed in expressions. Thus, an int is promoted to a float if
the prototype so indicates, but a pointer to unsigned is not converted to a pointer to
int. ANSI C also allows the implementation greater freedom when passing integral
arguments if a prototype is in scope. If it makes sense for an implementation to pass
short arguments as 16-bit quantities, it can do so.
Use of prototypes when calling functions allows greater ease in coding. However, due
to the differences in argument promotion rules, serious discrepancies can occur if a
function is called both with and without a prototype in scope. Make sure that you
use prototypes consistently and that any prototype is declared to be in scope for all
uses of the function identifier.
Function Prototypes
Function prototypes are not new to SGI C. In traditional C, however, the
implementation of prototypes was incomplete. In one case, a significant difference
still exists between the ANSI C and the traditional C implementations of prototypes.
You can prototype functions in two ways. The most common method is simply to
create a copy of the function declaration with the arguments typed, with or without
identifiers for each, such as either of the following:
18 007–0701–150
C Language Reference Manual
You can also prototype a function by writing the function definition in prototype form:
int func(int i, float f, unsigned u[2])
{
< code for func >
}
In each case, a prototype is created for func() that remains in scope for the rest of
the compilation unit.
One area of confusion about function prototypes is that you must write functions that
have prototypes in prototype form. Unless you do this, the default argument
promotion rules apply.
ANSI C elicits an error diagnostic for two incompatible types for the same parameter
in two declarations of the same function. Traditional C elicits an error diagnostic
when the incompatibility may lead to a difference between the bit-pattern of the value
passed in by the caller and the bit-pattern seen in the parameter by the callee.
In the following example, the function func() is declared twice with incompatible
parameter profiles:
int func (float);
int func (f)
float f;
{ … }
007–0701–150 19
2: C Language Changes
Note: When you use -cckr you do not get warnings about prototyped functions,
unless you specify -prototypes.
20 007–0701–150
C Language Reference Manual
Definitions of some of the terms used in Table 2-3, page 21, are as follows:
• “aliased” means the two names access the same object.
• “unchanged” means the well-known version of the name is unaltered.
• “identical copies” means that two copies of the object exist—one with the
well-known name and one with the ANSI C name (prefixed with an underbar).
Applications should not alter these objects.
• “#define” means that a macro is provided in the indicated header to translate the
well-known name to the ANSI C counterpart. Only the ANSI C name exists. You
should include the indicated header if your code refers to the well-known name.
For example, the name tzname is:
007–0701–150 21
2: C Language Changes
Standard Headers
Functions in the ANSI C library are declared in a set of standard headers. This set is
self-consistent and is free of name space pollution, when compiling in the pure ANSI
mode. Names that are normally elements of the user’s name space but are specifically
reserved by ANSI are described in the corresponding standard header. Refer to these
headers for information on both reserved names and ANSI library function
prototypes. The following list contains the set of standard headers:
<assert.h>
<ctype.h>
<errno.h>
<float.h>
<limits.h>
<locale.h>
<math.h>
<setjmp.h>
<signal.h>
<stdio.h>
<stddef.h>
<stdarg.h>
<string.h>
<stdlib.h>
<sys/errno.h>
<sys/signal.h>
<time.h>
22 007–0701–150
Chapter 3
Lexical Conventions
This chapter covers the C lexical conventions including comments and tokens. A
token is a series of contiguous characters that the compiler treats as a unit.
Blanks, tabs, newlines, and comments are collectively known as “white space.” White
space is ignored except as it serves to separate tokens. Some white space is required
to separate otherwise adjacent identifiers, keywords, and constants.
If the input stream has been parsed into tokens up to a given character, the next token
is taken to include the longest string of characters that could possibly constitute a
token.
Comments
The /* characters introduce a comment; the */ characters terminate a comment.
They do not indicate a comment when occurring within a string literal. Comments do
not nest. Once the /* introducing a comment is seen, all other characters are ignored
until the ending */ is encountered.
Identifiers
An identifier, or name, is a sequence of letters, digits, and underscores (_). The first
character cannot be a digit. Uppercase and lowercase letters are distinct. Name length
is unlimited. The terms identifier and name are used interchangeably.
Keywords
The identifiers listed in Table 3-1, page 24, are reserved for use as keywords and
cannot be used for any other purpose.
007–0701–150 23
3: Lexical Conventions
Keywords
auto default float register struct volatile
break do for return switch while
case double goto short typedef
char else if signed union
const enum int sizeof unsigned
continue extern long static void
Constants
The four types of constants are integer, character, floating, and enumeration. Each
constant has a type, determined by its form and value.
In this section’s discussions of the various types of constants, a unary operator
preceding the constant is not considered part of it. Rather, such a construct is a
constant-expression (see "Constant Expressions", page 66). Thus, the integer constant
0xff becomes an integral constant expression by prefixing a minus sign, for instance,
-0xff. The effect of the - operator is not considered in the discussion of integer
constants.
As an example, the integer constant 0xffffffff has type int in traditional C, with
value -1. It has type unsigned in ANSI C, with value 232 - 1. This discrepancy is
inconsequential if the constant is assigned to a variable of integral type (for example,
int or unsigned), as a conversion occurs. If it is assigned to a double, however, the
value differs as indicated between traditional and ANSI C.
Integer Constants
An integer constant consisting of a sequence of digits is considered octal if it begins
with 0 (zero). An octal constant consists of the digits 0 through 7 only. A sequence of
digits preceded by 0x or 0X is considered a hexadecimal integer. The hexadecimal
digits include [aA] through [fF], which have values of 10 through 15.
24 007–0701–150
C Language Reference Manual
The suffixes [lL] traditionally indicate integer constants of type long. These suffixes
are allowed, but are superfluous, because int and long are the same size in -o32
and -n32 modes. The ll, LL, lL, and Ll suffixes indicate a long long constant (a
64-bit integral type). Note that long long is not a strict ANSI C type, and a warning
is given for long long constants in -ansi and -ansiposix modes. The following
are examples of long long:
12345LL
12345ll
In ANSI C, an integer constant can be suffixed with uU, in which case its type is
unsigned. (One or both of uU and lL can appear.) An integer constant also has type
unsigned if its value cannot be represented as an int. Otherwise, the type of an
integer constant is int. The following are examples of unsigned long long:
123456ULL
123456ull
Character Constants
A character constant is a character enclosed in single quotation marks, such as ’x’.
The value of a character constant is the numerical value of the character in the
machine’s character set. An explicit new-line character is illegal in a character
constant. The type of a character constant is int.
In ANSI C, a character constant can be prefixed by L, in which case it is a wide
character constant. For example, a wide character constant for ’z’ is written L’z’.
The type of a wide character constant is wchar_t, which is defined in the stddef.h
file.
Special Characters
Some special and nongraphic characters are represented by the escape sequences
shown in Table 3-2, page 26.
007–0701–150 25
3: Lexical Conventions
The \ddd escape sequence consists of the backslash followed by 1, 2, or 3 octal digits
that specify the value of the desired character. A special case of this construction is
\0 (not followed by a digit), which indicates the ASCII character NUL.
In ANSI C, \x indicates the beginning of a hexadecimal escape sequence. The
sequence is assumed to continue until a character is encountered that is not a member
of the hexadecimal character set 0,1, … 9, [aA], [bB], … [fF]. The resulting unsigned
number cannot be larger than a character can accommodate (decimal 255).
If the character following a backslash is not one of those specified in this section, the
behavior is undefined.
The character sets of some older machines lack certain members that have come into
common usage. To allow the machines to specify these characters, ANSI C defined an
alternate method for their specification, using sequences of characters that are
commonly available. These sequences are termed trigraph sequences. Nine sequences
are defined; each consists of three characters beginning with two question marks.
Each instance of one of these sequences is translated to the corresponding single
character. Other sequences of characters, perhaps including multiple question marks,
26 007–0701–150
C Language Reference Manual
are unchanged. Each trigraph sequence with the single character it represents is listed
in the following table.
Floating Constants
A floating constant consists of an integer part, a decimal point, a fraction part, an
[eE], and an optionally signed integer exponent. The integer and fraction parts both
consist of a sequence of digits. Either the integer part or the fraction part (but not
both) can be missing. Either the decimal point or the [eE] and the exponent (not both)
can be missing.
In traditional C, every floating constant has type double.
In ANSI C, floating constants can be suffixed by either [fF] or [lL]. Floating constants
suffixed with [fF] have type float. Those suffixed with [lL] have type
long double, which has greater precision than double in -n32 and -64 modes
and a precision equal to double in -o32 mode.
007–0701–150 27
3: Lexical Conventions
Enumeration Constants
Names declared as enumerators have type int. For a discussion of enumerators, see
"Enumeration Declarations", page 76. For information on the use of enumerators in
expressions, see "Integer and Floating Point Types", page 38.
String Literals
A string literal is a sequence of characters surrounded by double quotation marks, as
in "...". A string literal has type array of char and is initialized with the given
characters. The compiler places a null byte (\0) at the end of each string literal so
that programs that scan the string literal can find its end. A double-quotation
character (") in a string literal must be preceded by a backslash (\). In addition, the
same escapes as those described for character constants can be used. (See "Character
Constants", page 25, for a list of escapes.) A backslash (\) and the immediately
following newline are ignored. Adjacent string literals are concatenated.
In traditional C, all string literals, even when written identically, are distinct.
In ANSI C, identical string literals are not necessarily distinct. Prefixing a string literal
with L specifies a wide string literal. Adjacent wide string literals are concatenated.
As an example, consider the sentence “He said, Hi there.” This sentence could be
written with three adjacent string literals:
"He said, " "Hi " "there.\’"
Operators
An operator specifies an operation to be performed. The operators [ ], ( ), and ?
: must occur in pairs, possibly separated by expressions. The operators # and ## can
occur only in preprocessing directives.
operator can be one of the following:
[ ]( ). ->
++ - - & * + - ~ ! sizeof
/ % << >> < > <= >= == != ^ | && ||
? :
= *= /= %= += -= <<= >>= &= ^= |=
, # ##
28 007–0701–150
C Language Reference Manual
Punctuators
A punctuator is a symbol that has semantic significance but does not specify an
operation to be performed. The punctuators [ ], ( ), and { } must occur in pairs,
possibly separated by expressions, declarations, or statements. The punctuator # can
occur only in preprocessing directives.
punctuator; one of the [ ]( ){ } * , : = ; … #
following:
Some operators, determined by context, are also punctuators. For example, the array
index indicator [ ]is a punctuator in a declaration (see Chapter 7, "Declarations",
page 69), but an operator in an expression (see Chapter 6, "Expressions and
Operators", page 49).
007–0701–150 29
Chapter 4
Meaning of Identifiers
Disambiguating Names
This section discusses the ways C disambiguates names: scope, name space, linkage,
and storage class.
Scope
The region of a program in which a given instance of an identifier is visible is called
its scope. The scope of an identifier usually begins when its declaration is seen, or, in
the case of labels and functions, when it is implied by use. Although it is impossible
to have two declarations of the same identifier active in the same scope, no conflict
occurs if the instances are in different scopes. Of the four kinds of scope, two—file
and block—are traditional C scopes. Two other kinds of scope—function and function
prototype—are implied in traditional C and formalized in ANSI C.
007–0701–150 31
4: Meaning of Identifiers
Block Scope
Block scope is the scope of automatic variables (variables declared within a function).
Each block has its own scope. No conflict occurs if the same identifier is declared in
two blocks. If one block encloses the other, the declaration in the enclosed block hides
that in the enclosing block until the end of the enclosed block is reached. The
definition of a block is the same in ANSI C and traditional C, with one exception,
illustrated by the example below:
int f(x);
int x;
{
int x;
x = 1;
}
In ANSI C, the function arguments are in the function body block. Thus, ANSI C will
issue an error of a “redeclaration of x.”
In traditional C, the function arguments are in a separate block that encloses the
function body block. Thus, traditional C would quietly hide the argument x with the
local variable x, because they are in distinct blocks.
ANSI C and traditional C differ in the assignment of block and file scope in a few
instances. See "File Scope", page 32, for more details.
Function Scope
Only labels have function scope. Function scope continues until the end of the
current function.
File Scope
Identifiers appearing outside of any block, function, or function prototype, have file
scope. This scope continues to the end of the compilation unit. Unlike other scopes,
32 007–0701–150
C Language Reference Manual
multiple declarations of the same identifier with file scope can exist in a compilation
unit, so long as the declarations are compatible.
Whereas ANSI C assigns block scope to all declarations occurring inside a function,
traditional C assigns file scope to such declarations if they have the storage class
extern. This storage class is implied in all function declarations, whether the
declaration is explicit (as in int foo();) or implicit (if there is no active declaration
for foo() when an invocation is encountered, as in f = foo();). For a further
discussion of this discrepancy, with examples, see "Scoping Differences", page 10.
Name Spaces
In certain cases, the purpose for which an identifier is used may disambiguate it from
other uses of the same identifier appearing in the same scope. This is true, for
example, for tags and is used in traditional C to avoid conflicts between identifiers
used as tags and those used in object or function declarations. ANSI C formalizes this
mechanism by defining certain name spaces. These name spaces are completely
independent. A member of one name space cannot conflict with a member of another.
ANSI C recognizes the following four distinct name spaces:
• Tags: struct, union, and enum tags have a single name space.
• Labels: labels are in their own name space.
• Members: each struct or union has its own name space for its members.
• Ordinary identifiers: ordinary identifiers, including function and object names as
well as user-defined type names, are placed in the last name space.
The definition of name spaces causes discrepancies between traditional and ANSI C
in a few situations:
• Structure members in traditional C were nothing more than offsets, allowing the
use of a member with a structure to which it does not belong. This is illegal under
ANSI C.
• Enumeration constants were special identifiers in traditional C prior to IRIX
Release 3.3. In later releases of traditional C, as in ANSI C, these constants are
simply integer constants that can be used anywhere they are appropriate.
007–0701–150 33
4: Meaning of Identifiers
• Labels reside in the same name space as ordinary identifiers in traditional C. Thus,
the following example is legal in ANSI C but not in traditional C:
func() {
int lab;
if (lab) goto lab;
func1() ;
lab:
return;
}
Linkage of Identifiers
Two instances of the same identifier appearing in different scopes may, in fact, refer to
the same entity. For example, the references to a variable, counter, is declared with
file scope in the following example:
extern int counter;
In this example, two separate files refer to the same int object. The association
between the references to an identifier occurring in distinct scopes and the underlying
objects are determined by the identifier’s linkage.
The three kinds of linkage are as follows:
Internal linkage Within a file, all declarations of the same identifier with
internal linkage denote the same object.
External linkage Within an entire program, all declarations of an
identifier with external linkage denote the same object.
No linkage A unique entity, accessible only in its own scope, has
no linkage.
An identifier’s linkage is determined by whether it appears inside or outside a
function, whether it appears in a declaration of a function (as opposed to an object),
its storage-class specifier, and the linkage of any previous declarations of the same
identifier that have file scope. An identifier’s linkage is determined as follows:
1. If an identifier is declared with file scope and the storage-class specifier static, it
has internal linkage.
2. If the identifier is declared with the storage-class specifier extern, or is an
explicit or implicit function declaration with block scope, the identifier has the
34 007–0701–150
C Language Reference Manual
same linkage as any previous declaration of the same identifier with file scope. If
no previous declaration exists, the identifier has external linkage.
3. If an identifier for an object is declared with file scope and no storage-class
specifier, it has external linkage. (See "Changes in the Linkage of Identifiers", page
12.)
4. All other identifiers have no linkage. This includes all identifiers that do not
denote an object or function, all objects with block scope declared without the
storage-class specifier extern, and all identifiers that are not members of the
ordinary variables name space.
Two declarations of the same identifier in a single file that have the same linkage,
either internal or external, refer to the same object. The same identifier cannot appear
in a file with both internal and external linkage.
This code gives an example where the linkage of each declaration is the same in both
traditional and ANSI C:
The declaration of pete with file scope has internal linkage by rule 1 above. This
means that the declaration of pete in func0() also has internal linkage by rule 2
and refers to the same object.
By rule 2, the declaration of bert with file scope has external linkage, because there
is no previous declaration of bert with file scope. Thus, the declaration of bert in
func1() also has external linkage (again by rule 2) and refers to the same (external)
007–0701–150 35
4: Meaning of Identifiers
object. By rule 4, however, the declaration of bert in func0() has no linkage, and
refers to a unique object.
The declaration of mom with file scope has external linkage by rule 3, and, by rule 2,
so does the declaration of mom in func0(). (Again, two declarations of the same
identifier in a single file that both have either internal or external linkage refer to the
same object.) The declaration of mom in func1(), however, has no linkage by rule 4
and thus refers to a unique object.
Last, the declarations of dad in func0() and func1() refer to different objects, as
the former has no linkage and the latter, by rule 2, has external linkage.
In the preceding example, both mytime and yourtime have file scope. As discussed
previously, mytime has external linkage, while yourtime has internal linkage.
However, there is an implicit difference, which exists in both ANSI and traditional C,
between the declarations of mytime and yourtime in the preceding example. The
declaration of yourtime allocates storage for the object, whereas the declaration of
mytime merely references it. If mytime had been initialized, as in the following
example, it would also have allocated storage:
int mytime=0;
36 007–0701–150
C Language Reference Manual
Note: In ANSI C, objects with external linkage that are not specified as extern at the
end of the compilation unit are considered definitions, and, in effect, initialized to
zero. (If multiple declarations of the object occur in the compilation unit, only one
need have the extern specification.)
If two modules contain definitions of the same identifier, the linker complains of
“multiple definitions,” even though neither is explicitly initialized.
The ANSI C linker issues a warning when it finds redundant definitions, indicating
which modules produced the conflict. However, the linker cannot determine if the
initialization of the object is explicit. This may result in incorrectly initialized objects
if another module fails to tag the object with extern.
Thus, consider the following example:
module1.c:
int ernie;
module2.c:
int ernie = 5;
Storage Duration
Storage duration denotes the lifetime of an object. Storage duration is of two types:
static and automatic.
007–0701–150 37
4: Meaning of Identifiers
Objects declared with external or internal linkage, or with the storage-class specifier
static, have static storage duration. If these objects are initialized, the initialization
occurs once, prior to any reference.
Other objects have automatic storage duration. Storage is newly allocated for these
objects each time the block that contains their declaration is entered, unless the object
has a variable length array type. If the object is variably modified, and the block is
entered by a jump to a labeled statement, then the behavior is undefined.
If an object with automatic storage duration is initialized, the initialization occurs
each time the block is entered at the top. This is not guaranteed to occur if the block
is entered by a jump to a labeled statement.
Object Types
The C language supports three fundamental types of objects: character, integer, and
floating point.
Character Types
Objects declared as characters (char) are large enough to store any member of the
implementation’s character set. If a genuine character from that character set is stored
in a char variable, its value is equivalent to the integer code for that character. Other
quantities may be stored into character variables, but the implementation is machine
dependent. In this implementation, char is unsigned by default.
The ANSI C standard has added multibyte and wide character types. In the initial
SGI release of ANSI C, wide characters are of type unsigned char, and multibyte
characters are of length one. (See the header files stddef.h and limits.h for more
information.)
38 007–0701–150
C Language Reference Manual
Although SGI supports long double as a type in -cckr mode, this is viewed as an
extension to traditional C and is ignored in subsequent discussions pertinent only to
traditional C.
Differences exist between -o32 mode, -n32 mode, and -64 mode compilations.
Types long and int have different sizes (and ranges) in 64-bit mode; type long always
has the same size as a pointer value. A pointer (or address) has a 64-bit
representation in 64-bit mode and a 32-bit representation in both 32-bit modes.
Therefore, an int object has a smaller size than a pointer object in 64-bit mode.
The long long type is not a valid ANSI C type, so a warning is elicited for every
occurrence of long long in the source program text in -ansi and -ansiposix modes.
The long double type has equal range in old 32-bit, new 32-bit, and 64-bit mode,
but it has increased precision in -n32 and -64 modes.
Characteristics of integer and floating point types are defined in the standard header
files <limits.h> and <float.h>. The range of a signed integral type of size n is
[(-2n-1)... (2n-1 -1)]. The range of an unsigned version of the type is [0... (2n -1)].
Enumeration constants were special identifiers under various versions of traditional
C, before IRIX Release 3.3. In ANSI C, these constants are simply integer constants
that may be used anywhere. Similarly, ANSI C allows the assignment of other integer
variables to variables of enumeration type, with no error.
007–0701–150 39
4: Meaning of Identifiers
You can find additional information on integers, floating points, and structures in the
following tables:
• For integer types and ranges, see Table A-1, page 133
• For floating point types and ranges, see Table A-2, page 135
• For structure alignment, see Table A-3, page 137
Derived Types
Because objects of the types mentioned in "Integer and Floating Point Types", page 38,
can be interpreted usefully as numbers, this manual refers to them as arithmetic
types. The types char, enum, and int of all sizes (whether unsigned or not) are
collectively called integral types. The float and double types are collectively called
floating types. Arithmetic types and pointers are collectively called scalar types.
The fundamental arithmetic types can be used to construct a conceptually infinite
class of derived types, such as the following:
• Arrays of objects of most types
• Functions that return objects of a given type
• Pointers to objects of a given type
• Structures that contain a sequence of objects of various types
• Unions capable of containing any one of several objects of various types
In general, these constructed objects can be used as building blocks for other
constructed objects.
void Type
The void type specifies an empty set of values. It is used as the type returned by
functions that generate no value. The void type never refers to an object and
therefore, is not included in any reference to object types.
40 007–0701–150
C Language Reference Manual
Most lvalues are modifiable, meaning that the lvalue may be used to modify the
object to which it refers. Examples of lvalues that are not modifiable include array
names, lvalues with incomplete type, and lvalues that refer to an object, part or all of
which is qualified with const (see "Type Qualifiers", page 77). Whether an lvalue
appearing in an expression must be modifiable is usually obvious. For example, in the
assignment expression E1 = E2, E1 must be modifiable. This document makes the
distinction between modifiable and unmodifiable lvalues only when it is not obvious.
007–0701–150 41
Chapter 5
Operator Conversions
007–0701–150 43
5: Operator Conversions
constants. To force long double precision on constants, use the l or L suffix. For
example, 3.14l is long double precision, 3.14 is double precision, and 3.14f is
single precision in ANSI C.
For a complete discussion with examples, see "Type Promotion and Floating Point
Constants", page 15.
44 007–0701–150
C Language Reference Manual
Arithmetic Conversions
Many types of operations in C require two operands to be converted to a common
type. Two sets of conversion rules are applied to accomplish this conversion. The first,
referred to as the integral promotions, defines how integral types are promoted to one
of several integral types that are at least as large as int. The second, called the usual
arithmetic conversions, derives a common type in which the operation is performed.
ANSI C and traditional C follow different sets of these rules.
Integral Promotions
The difference between the ANSI C and traditional versions of the conversion rules is
that the traditional C rules emphasize preservation of the (un)signedness of a
quantity, while ANSI C rules emphasize preservation of its value.
007–0701–150 45
5: Operator Conversions
Each rule specifies a type, referred to as the result type. Once a rule has been chosen,
each operand is converted to the result type, the operation is performed in that type,
and the result is of that type.
46 007–0701–150
C Language Reference Manual
007–0701–150 47
5: Operator Conversions
Conversion of Pointers
A pointer to void can be converted to a pointer to any object type and back without
change in the underlying value.
The NULL pointer constant can be specified either as the integral value zero, or the
value zero cast to a pointer to void. If a NULL pointer constant is assigned or
compared to a pointer to any type, it is appropriately converted.
48 007–0701–150
Chapter 6
This chapter discusses the various expressions and operators available in C. The
sections describing expressions and operators are presented roughly in order of
precedence.
Except as indicated by the syntax or specified explicitly in this chapter, the order of
evaluation of expressions, as well as the order in which side-effects take place, is
unspecified. The compiler can arbitrarily rearrange expressions involving a
commutative and associative operator (*, +, &, |, ^).
Table 6-2, page 50, lists the precedence and associativity of all operators.
007–0701–150 49
6: Expressions and Operators
50 007–0701–150
C Language Reference Manual
Primary Expressions
The following are all considered “primary expressions:”
Identifiers An identifier referring to an object is an lvalue. An
identifier referring to a function is a function
designator. lvalues and function designators are
discussed in “Conversion of lvalues and Function
Designators” on page 59.
Constants A constant’s type is determined by its form and value,
as described in "Constants", page 24.
String literals A string literal’s type is “array of char,” subject to
modification, as described in "Conversions of
Characters and Integers", page 43.
Parenthesized A parenthesized expression’s type and value are
expressions identical to those of the unparenthesized expression.
The presence of parentheses does not affect whether the
expression is an lvalue, rvalue, or function designator.
For information on expressions, see “Constant
Expressions” on page 79.
Postfix Expressions
Postfix expressions involving ., ->, subscripting, and function calls associate left to
right. The syntax for these expressions is as follows:
postfix-expression: primary-expression
postfix-expression [expression]
postfix-expression (argument-expression-list opt)
postfix-expression. identifier
postfix-expression -> identifier
postfix-expression ++
postfix-expression - -
007–0701–150 51
6: Expressions and Operators
argument-expression-list: argument-expression
argument-expression-list, argument-expression
Subscripts
A postfix expression followed by an expression in square brackets is a subscript.
Usually, the postfix expression has type “pointer to <type>”, the expression within the
square brackets has type int, and the type of the result is <type>. However, it is
equally valid if the types of the postfix expression and the expression in brackets are
reversed. This is because the expression E1[E2] is identical (by definition) to
*((E1)+(E2)). Because addition is commutative, E1 and E2 can be interchanged.
You can find more information on this notation in the discussions on identifiers and
in the discussion of the * and + operators (in "Unary Operators", page 55, and
"Additive Operators", page 59), respectively.
Function Calls
The syntax of function call postfix expressions is as follows:
postfix-expression (argument-expression-listopt)
argument-expression-list: argument-expression
argument-expression-list, argument-expression
A function call is a postfix expression followed by parentheses containing a (possibly
empty) comma-separated list of expressions that are the arguments to the function.
The postfix expression must be of type “function returning <type>.” The result of the
function call is of type <type>, and is not an lvalue.
The behavior of function calls is as follows:
• If the function call consists solely of a previously unseen identifier foo, the call
produces an implicit declaration as if, in the innermost block containing the call,
the following declaration had appeared:
extern int foo();
• If a corresponding function prototype that specifies a type for the argument being
evaluated is in force, an attempt is made to convert the argument to that type.
52 007–0701–150
C Language Reference Manual
• If the number of arguments does not agree with the number of parameters
specified in the prototype, the behavior is undefined.
• If the type returned by the function as specified in the prototype does not agree
with the type derived from the expression containing the called function, the
behavior is undefined. Such a scenario may occur for an external function
declared with conflicting prototypes in different files.
• If no corresponding prototype is in scope or if the argument is in the variable
argument section of a prototype that ends in ellipses (…), the argument is
converted according to the following default argument promotions:
– Type float is converted to double.
– Array and function names are converted to corresponding pointers.
– When using traditional C, types unsigned short and unsigned char are
converted to unsigned int, and types signed short and signed char
are converted to signed int.
– When using ANSI C, types short and char, whether signed or unsigned,
are converted to int.
• In preparing for the call to a function, a copy is made of each actual argument.
Thus, all argument passing in C is strictly by value. A function can change the
values of its parameters, but these changes cannot affect the values of the actual
arguments. It is possible to pass a pointer on the understanding that the function
can change the value of the object to which the pointer points. (Arguments that
are array names can be changed as well, because these arguments are converted to
pointer expressions.)
• Because the order of evaluation of arguments is unspecified, side effects may be
delayed until the next sequence point, which occurs at the point of the actual call
and after all arguments have been evaluated. (For example, in the function call
func(foo++), the incrementation of foo may be delayed.)
• Recursive calls to any function are permitted.
SGI recommends consistent use of prototypes for function declarations and
definitions. Do not mix prototyped and nonprototyped function declarations and
definitions. Even though the language allows it, never call functions before you
declare them. This results in an implicit nonprototyped declaration that may be
incompatible with the function definition.
007–0701–150 53
6: Expressions and Operators
postfix-expression. identifier
The postfix expression must be a structure or a union, and the identifier must name a
member of the structure or union. The value is the value of the named member of the
structure or union, and is an lvalue if the first expression is an lvalue.The result has
the type of the indicated member and the qualifiers of the structure or union.
The postfix expression must be a pointer to a structure or a union, and the identifier
must name a member of that structure or union. The result is an lvalue referring to
the named member of the structure or union to which the postfix expression points.
The result has the type of the selected member, and the qualifiers of the structure or
union to which the postfix expression points. Thus, the expression E1->MOS is the
same as (*E1).MOS.
Structures and unions are discussed in "Structure and Union Declarations", page 72.
postfix-expression ++
postfix-expression --
54 007–0701–150
C Language Reference Manual
When postfix ++ is applied to a modifiable lvalue, the result is the value of the
object referred to by the lvalue. After the result is noted, the object is incremented by
1 (one). See the discussions in "Additive Operators", page 59, and "Assignment
Operators", page 65, for information on conversions. The type of the result is the
same as the type of the lvalue expression. The result is not an lvalue.
When postfix -- is applied to a modifiable lvalue, the result is the value of the
object referred to by the lvalue. After the result is noted, the object is decremented by
1 (one). See the discussions in "Additive Operators", page 59, and "Assignment
Operators", page 65, for information on conversions. The type of the result is the
same as the type of the lvalue expression. The result is not an lvalue.
For both postfix ++ and postfix -- operators, updating the stored value of the
operand may be delayed until the next sequence point.
Unary Operators
Expressions with unary operators associate from right to left. The syntax for unary
operators is as follows:
unary-expression: postfix-expression
++ unary-expression
- - unary-expression
unary-operator cast-expression
sizeof unary-expression
sizeof (type-name)
unary-operator: one of * & - ! ~ +
Except as noted, the operand of a unary operator must have arithmetic type.
007–0701–150 55
6: Expressions and Operators
The operand of the unary & operator can be either a function designator or an lvalue
that designates an object. If it is an lvalue, the object it designates cannot be a bitfield,
and it cannot be declared with the storage class register. The result of the unary &
operator is a pointer to the object or function referred to by the lvalue or function
designator. If the type of the lvalue is <type>, the type of the result is “pointer to
<type>”.
++ unary-expression
-- unary-expression
56 007–0701–150
C Language Reference Manual
sizeof unary-expression
sizeof (type-name)
The operand of sizeof cannot have function or incomplete type, or be an lvalue that
denotes a bitfield. It can be an object or a parenthesized type name. In traditional C,
the type of the result is unsigned. In ANSI C, the type of the result is size_t,
which is defined in <stddef.h> as unsigned int (in -o32 and -n32 modes) or as
unsigned long (in -64 mode). The result is a constant and can be used anywhere a
constant is required.
When applied to an array, sizeof returns the total number of bytes in the array. The
size is determined from the declaration of the object in the unary expression. For
variable length array types, the result is not a constant expression and is computed at
run time.
The sizeof operator can also be applied to a parenthesized type name. In that case,
it yields the size in bytes of an object of the indicated type.
When sizeof is applied to an aggregate, the result includes space used for padding,
if any.
Cast Operators
A cast expression preceded by a parenthesized type name causes the value of the
expression to convert to the indicated type. This construction is called a cast. Type
007–0701–150 57
6: Expressions and Operators
names are discussed in "Type Names", page 86. The syntax of a cast expression is as
follows:
cast-expression: unary-expression
(type-name) cast-expression
The type name specifies a scalar type or void, and the operand has scalar type.
Because a cast does not yield an lvalue, the effect of qualifiers attached to the type
name is inconsequential.
When an arithmetic value is cast to a pointer, and vice versa, the appropriate number
of bits are simply copied unchanged from one type of value to the other. Be aware of
the possible truncation of pointer values in 64-bit mode compilation, when a pointer
value is converted to an (unsigned) int.
Multiplicative Operators
The multiplicative operators *, /, and % group from left to right. The usual arithmetic
conversions are performed. The following is the syntax for the multiplicative
operators:
multiplicative expression: cast-expression
multiplicative-expression * cast-expression
multiplicative-expression / cast-expression
multiplicative-expression % cast-expression
Operands of * and / must have arithmetic type. Operands of % must have integral
type.
The binary * operator indicates multiplication, and its result is the product of the
operands.
The binary / operator indicates division of the first operator (dividend) by the second
(divisor). If the operands are integral and the value of the divisor is 0, SIGTRAP is
signalled. Integral division results in the integer quotient whose magnitude is less
than or equal to that of the true quotient, and with the same sign.
58 007–0701–150
C Language Reference Manual
The binary % operator yields the remainder from the division of the first expression
(dividend) by the second (divisor). The operands must be integral. The remainder
has the same sign as the dividend, so that the equality below is true when the divisor
is nonzero:
(dividend / divisor) * divisor + dividend % divisor == dividend
Additive Operators
The additive operators + and - associate from left to right. The usual arithmetic
conversions are performed.The syntax for the additive operators is as follows:
additive-expression: multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
In addition to arithmetic types, the following type combinations are acceptable for
additive expressions:
• For addition, one operand is a pointer to an object type and the other operand is
an integral type.
• For subtraction,
– Both operands are pointers to qualified or unqualified versions of compatible
object types.
– The left operand is a pointer to an object type, and the right operand has
integral type.
The result of the + operator is the sum of the operands. The result of the - operator is
the difference of the operands.
When an operand of integral type is added to or subtracted from a pointer to an
object type, the integral operand is first converted to an address offset by multiplying
it by the length of the object to which the pointer points. The result is a pointer of the
same type as the original pointer.
007–0701–150 59
6: Expressions and Operators
For instance, suppose a has type “array of <object>”, and p has type “pointer to
<object>” and points to the initial element of a. Then the result of p + n, where n is
an integral operand, is the same as &a[n].
If two pointers to objects of the same type are subtracted, the result is converted (by
division by the length of the object) to an integral quantity representing the number
of objects separating them. Unless the pointers point to objects in the same array, the
result is undefined. The actual type of the result is int in traditional C, and
ptrdiff_t (defined in <stddef.h> as int in -o32 and -n32 modes and as long
in -64 mode) in ANSI C.
Shift Operators
The shift operators << and >> associate from left to right. Each operand must be an
integral type. The integral promotions are performed on each operand. The syntax is
as follows:
shift-expression: additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression
The type of the result is that of the promoted left operand. If the right operand is
negative, greater than, or equal to the length in bits of the promoted left operand, the
result is undefined.
The value of E1 << E2 is E1 (interpreted as a bit pattern) left-shifted E2 bits.
Vacated bits are filled with zeros.
The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 is unsigned, or if
it is signed and its value is nonnegative, vacated bits are filled with zeros. If E1 is
signed and its value is negative, vacated bits are filled with ones.
Relational Operators
The relational operators associate from left to right. The syntax is as follows:
relational-expression: shift-expression
relational-expression < shift-expression
60 007–0701–150
C Language Reference Manual
Equality Operators
The == (equal to) and the != (not equal to) operators are exactly analogous to the
relational operators except for their lower precedence. (For example, a < b == c <
d is 1 whenever a < b and c < d have the same truth value.) The syntax of the
equality operators is as follows:
equality-expression: relational-expression
equality-expression == relational-expression
equality-expression != relational-expression
The operands must be one of the following:
• Both arithmetic, in which case the usual arithmetic conversions are performed on
them
• Both pointers to qualified or unqualified versions of compatible types
007–0701–150 61
6: Expressions and Operators
62 007–0701–150
C Language Reference Manual
Logical OR Operator
Each of the operands of the logical OR operator must have scalar type. The ||
operator associates left to right. The syntax is as follows:
logical-OR-expression: logical-AND-expression
logical-OR-expression || logical-AND-expression
The result has type int. If either of the operands evaluates to one, the result has a
value of 1. Otherwise it has a value of 0.
007–0701–150 63
6: Expressions and Operators
Unlike |, || guarantees left to right evaluation; moreover, the second operand is not
evaluated unless the first operand evaluates to zero. A sequence point occurs after
the evaluation of the first operand.
Conditional Operator
Conditional expressions associate from right to left. The syntax is as follows:
conditional-expression: logical-OR-expression
logical-OR-expression ? expression : conditional-expression
The type of the first operand must be scalar. Only certain combinations of types are
allowed for the second and third operands. These combinations are listed below,
along with the type of result that the combination yields:
• Both can be arithmetic types. In this case, the usual arithmetic conversions are
performed on them to derive a common type, which is the type of the result.
• Both are compatible structure or union objects. The result has the same type as the
operands.
64 007–0701–150
C Language Reference Manual
Assignment Operators
All assignment operators associate from right to left. The syntax is as follows:
assignment-expression: conditional-expression
unary-expression assignment-operator assignment-expression
assignment operator: one = *= /= %= += -= <<= >>= &= ^= |=
of
Assignment operators require a modifiable lvalue as their left operand. The type of
an assignment expression is that of its unqualified left operand. The result is not an
lvalue. Its value is the value stored in the left operand after the assignment, but the
actual update of the stored value may be delayed until the next sequence point.
The order of evaluation of the operands is unspecified.
007–0701–150 65
6: Expressions and Operators
Compound Assignment
For the operators += and -=, either both operators must have arithmetic types, or the
left operand must be a pointer and the right an operand integral. In the latter case,
the right operand is converted as explained in "Additive Operators", page 59. For all
other operators, each operand must have arithmetic type consistent with those
allowed for the corresponding binary operator.
The expression E1 op = E2is equivalent to the expression E1 = E1 op E2, except
that in the former, E1 is evaluated only once.
Comma Operator
A pair of expressions separated by a comma is evaluated left to right, and the value
of the left expression is discarded. This operator associates left to right. The syntax of
the comma operator is as follows:
expression: assignment-expression
expression, assignment-expression
The type and value of the result are the type and value of the right operand. In
contexts where the comma is given a special meaning, the comma operator as
described in this section can appear only in parentheses. Two such contexts are lists
of actual arguments to functions (described in "Primary Expressions", page 51) and
lists of initializers (see "Initialization", page 88). For example, the following code has
three arguments, the second of which has the value 5:
f(a, (t=3, t+2), c)
Constant Expressions
A constant expression can be used any place a constant can be used. The syntax is as
follows:
constant-expression: conditional-expression
A constant expression cannot contain assignment, increment, decrement, function-call,
or comma operators. It must evaluate to a constant that is in the range of
representable values for its type. Otherwise, the semantic rules for the evaluation of
nonconstant expressions apply.
66 007–0701–150
C Language Reference Manual
007–0701–150 67
Chapter 7
Declarations
007–0701–150 69
7: Declarations
70 007–0701–150
C Language Reference Manual
Type Specifiers
Type specifiers are listed below. The syntax is as follows:
type-specifier: struct-or-union-specifier
typedef-name
enum-specifier
char
short
int
long
signed
unsigned
float
double
void
The following is a list of all valid combinations of type specifiers. These combinations
are organized into sets. The type specifiers in each set are equivalent in all
implementations. The arrangement of the type specifiers appearing in any set can be
altered without effect.
• void
• char
007–0701–150 71
7: Declarations
• signed char
• unsigned char
• short, signed short, short int, or signed short int
• unsigned short, or unsigned short int
• int, signed, signed int, or no type specifiers
• unsigned, or unsigned int
• long, signed long, long int, or signed long int
• unsigned long, or unsigned long int
• long long, signed long long, long long int, or signed long long
int
• unsigned long long, or unsigned long long int
• float
• double
• long double
In traditional C, the type long float is allowed and is equivalent to double; its
use is not recommended. It elicits a warning if you are not in -cckr mode. Use of
the type long double is not recommended in traditional C.
long long is not a valid ANSI C type, so a warning appears for every occurrence of
it in the source program text in -ansi and -ansiposix modes.
Specifiers for structures, unions, and enumerations are discussed in "Structure and
Union Declarations", page 72, and "Enumeration Declarations", page 76. Declarations
with typedef names are discussed in "typedef", page 87.
72 007–0701–150
C Language Reference Manual
007–0701–150 73
7: Declarations
Within a structure, the objects declared have addresses that increase as the
declarations are read left to right. Each non-field member of a structure begins on an
addressing boundary appropriate to its type; therefore, there may be unnamed holes
in a structure.
A union can be thought of as a structure whose members all begin at offset 0 and
whose size is sufficient to contain any of its members. At most, one of the members
can be stored in a union at any time.
A structure or union specifier of the second form declares the identifier to be the
structure tag (or union tag) of the structure specified by the list. This type of specifier
is one of the following:
struct identifier {struct-decl-list}
union identifier {struct-decl-list}
A subsequent declaration can use the third form of specifier, one of the following:
struct identifier
union identifier
Structure tags allow definition of self-referential structures. Structure tags also permit
the long part of the declaration to be given once and used several times.
The third form of a structure or union specifier can be used before a declaration that
gives the complete specification of the structure or union in situations in which the
size of the structure or union is unnecessary. The size is unnecessary in two situations:
when a pointer to a structure or union is being declared and when a typedef name
is declared to be a synonym for a structure or union. This, for example, allows the
declaration of a pair of structures that contain pointers to each other.
The names of members of each struct or union have their own name space, and do
not conflict with each other or with ordinary variables. A particular member name
cannot be used twice in the same structure, but it can be used in several different
structures in the same scope.
Names that are used for tags reside in a single name space. They do not conflict with
other names or with names used for tags in an enclosing scope. This tag name space,
however, consists of tag names used for all struct, union, or enum declarations.
Therefore, the tag name of an enum may conflict with the tag name of a struct in
the same scope. (See "Disambiguating Names", page 31.)
A simple but important example of a structure declaration is the following binary tree
structure:
74 007–0701–150
C Language Reference Manual
struct tnode {
char tword[20];
int count;
struct tnode *left;
struct tnode *right;
};
struct tnode s, *sp;
Bitfields
A structure member can consist of a specified number of bits, called a bitfield. In
strict ANSI C mode, bitfields should be of type int, signed int, or unsigned
int. SGI C allows bitfields of any integral type, but warns for non-int types in
-ansi and -ansiposix modes.
The default type of field members is int, but whether it is signed or unsigned
int is defined by the implementation. Therefore, you should specify the signedness
of bitfields when they are declared. In this implementation, the default type of a
bitfield is signed.
The constant expression that denotes the width of the bitfield must have a value no
greater than the width, in bits, of the type of the bitfield. An implementation can
allocate any addressable storage unit (referred to in this discussion as simply a
“unit”) large enough to hold a bitfield. If an adjacent bitfield will not fit into the
remainder of the unit, the implementation defines whether bitfields are allowed to
span units or whether another unit is allocated for the second bitfield. The ordering
of the bits within a unit is also implementation-defined.
007–0701–150 75
7: Declarations
A bitfield with no declarator, just a colon and a width, indicates an unnamed field
useful for padding. As a special case, a field with a width of zero (which cannot have
a declarator) specifies alignment of the next field at the next unit boundary.
These implementation-defined characteristics make the use of bitfields inherently
nonportable, particularly if they are used in situations where the underlying object
may be accessed by another data type (in a union, for example).
In the SGI implementation of C, the first bitfield encountered in a struct is not
necessarily allocated on a unit boundary and is packed into the current unit, if
possible. A bitfield cannot span a unit boundary. Bits for bitfields are allocated from
left (most significant) to right.
There are no arrays of bitfields. Because the address-of operator, &, cannot be applied
to bitfields, there are also no pointers to bitfields.
Enumeration Declarations
Enumeration variables and constants have integral type. The syntax is as follows:
enum-specifier: enum {enum-list}
enum {identifier enum-list}
enum identifier
enum-list: enumerator
enum-list , enumerator
enumerator: identifier
identifier = constant-expression
The identifiers in an enum-list are declared as int constants and can appear wherever
such constants are allowed. If no enumerators with = appear, then the values of the
corresponding constants begin at 0 and increase by 1 as the declaration is read from
left to right. An enumerator with = gives the associated identifier the value indicated;
subsequent identifiers continue the progression from the assigned value. Note that
the use of = may result in multiple enumeration constants having the same integral
value, even though they are declared in the same enumeration declaration.
76 007–0701–150
C Language Reference Manual
Enumerators are in the ordinary identifiers name space (see "Name Spaces", page 33).
Thus, an identifier used as an enumerator may conflict with identifiers used for
objects, functions, and user-defined types in the same scope.
The role of the identifier in the enum-specifier is entirely analogous to that of the
structure tag in a struct-specifier; it names a particular enumeration. For example,
enum color { chartreuse, burgundy, claret = 20, winedark };
...
enum color *cp, col;
...
col = claret;
cp = &col;
...
if (*cp == burgundy) ...
This example makes color the enumeration-tag of a type describing various colors,
and then declares cp as a pointer to an object of that type, col. The possible values are
drawn from the set {0,1,20,21}. The tags of enumeration declarations are members of
the single tag name space, and thus must be distinct from tags of struct and union
declarations.
Type Qualifiers
Type qualifiers have the following syntax:
type-qualifier: const
volatile
__restrict
The same type qualifier cannot appear more than once in the same specifier list either
directly or indirectly (through typedefs).
The value of an object declared with the const type qualifier is constant. It cannot be
modified, although it can be initialized following the same rules as the initialization of
any other object. (See the discussion in "Initialization", page 88.) Implementations are
free to allocate const objects, that are not also declared volatile, in read-only storage.
An object declared with the volatile type qualifier may be accessed in unknown ways
or have unknown side effects. For example, a volatile object may be a special
hardware register. Expressions referring to objects qualified as volatile must be
007–0701–150 77
7: Declarations
evaluated strictly according to the semantics. When volatile objects are involved, an
implementation is not free to perform optimizations that would otherwise be valid.
At each sequence point, the value of all volatile objects must agree with that specified
by the semantics.
The __restrict qualifier applies only to pointers and is discussed in "Qualifiers
and Pointers", page 79.
If an array is specified with type qualifiers, the qualifiers are applied to the elements of
the array. If a struct or union is qualified, the qualification applies to each member.
Two qualified types are compatible if they are identically qualified versions of
compatible types. The order of qualifiers in a list has no effect on their semantics.
The syntax of pointers allows the specification of qualifiers that affect either the
pointer itself or the underlying object. Qualified pointers are covered in "Pointer
Declarators", page 79.
Declarators
Declarators have the syntax shown below:
declarator: pointeropt direct-declarator
direct-declarator: identifier
(declarator)
direct-declarator (parameter-type-listopt)
direct-declarator (identifier-listopt)
direct-declarator [constant-expressionopt]
The grouping is the same as in expressions.
Meaning of Declarators
Each declarator is an assertion that when a construction of the same form as the
declarator appears in an expression, it designates a function or object with the scope,
storage duration, and type indicated by the declaration.
Each declarator contains exactly one identifier; it is this identifier that is declared. If,
in the declaration “T D1;” D1 is simply an identifier, then the type of the identifier is
78 007–0701–150
C Language Reference Manual
Pointer Declarators
Pointer declarators have the form:
pointer: * type-qualifier-listopt
* type-qualifier-listopt pointer
The following is an example of a declaration:
T D1
In this declaration, the identifier has type .. T, where the .. is empty if D1 is just
a plain identifier (so that the type of x in int x is just int). Then, if D1 has the form
*type-qualifier-listopt D, the type of the contained identifier is ”..
(possibly-qualified) pointer to T.”
007–0701–150 79
7: Declarations
The SGI C compiler supports the following two alias-related command-line switches
that can be useful for improving performance:
-OPT:alias=restrict
Implements the following semantics: memory operations
dereferencing different named pointers in the program are assumed
not to alias with each other, nor with any named scalar in the
program.
For example, if p and q are pointers, this option means that *p does
not alias with *q, with p, or with any named scalar variable.
-OPT:alias=disjoint
Note: With either switch enabled, programs violating the corresponding aliasing
assumptions may be compiled incorrectly.
80 007–0701–150
C Language Reference Manual
Array Declarators
If in the declaration T D1, D1 has the form D[expressionopt] or D[*], then the
contained identifier has type “array of T.” Starting with version 7.2, the SGI C
compiler now supports variable length arrays as well as fixed length arrays. A
variable length array is an array that has a size (at least one dimension) that is
determined at run time. The ability to use variable length arrays enhances the
compiler’s range of use for numerical programming.
The following rules apply to array declarations:
• If the array is a fixed length array, the expression enclosed in square brackets, if it
exists, must be an integral constant expression whose value is greater than zero.
(See "Primary Expressions", page 51.)
• When several “array of” specifications are adjacent, a multi-dimensional array is
created; the constant expressions that specify the bounds of the arrays can be
missing only for the first member of the sequence.
• The absence of the first array dimension is allowed if the array is external and the
actual definition (which allocates storage) is given elsewhere, or if the declarator is
followed by initialization. In the latter case, the size is calculated from the number
of elements supplied.
007–0701–150 81
7: Declarations
• An array can be constructed from one of the basic types, from a pointer, from a
structure or union, or from another array (to generate a multi-dimensional array).
The example below declares an array of float numbers and an array of pointers to
float numbers:
float fa[17], *afp[17];
The following example declares a static three-dimensional array of integers, with rank
3 2 5 2 7.
static int x3d[3][5][7];
In the above example, x3d is an array of three items; each item is an array of five
items, each of which is an array of seven integers. Any of the expressions x3d,
x3d[i], x3d[i][j], x3d[i][j][k] can reasonably appear in an expression. The
first three have type array and the last has type int.
direct-declarator (parameter-type-listopt)
direct-declarator (identifier-listopt)
parameter-type-list:
parameter-list
parameter-list , ...
parameter-list:
parameter-declaration
parameter-list , parameter-declaration
parameter-declaration:
declaration-specifiers declarator
declaration-specifiers abstract-declaratoropt
identifier-list:
identifier
identifier-list , identifier
Function declarators cannot specify a function or array type as the return type. In
addition, the only storage class specifier that can be used in a parameter declaration is
register. For example, in the declaration T D1, D1 has one of the following forms:
82 007–0701–150
C Language Reference Manual
• D(parameter-type-listopt)
• D(identifier-listopt)
The contained identifier has the type ”.. function returning T,” and is possibly a
prototype, as discussed later in this section.
A parameter type list declares the types of, and can declare identifiers for, the formal
parameters of a function. A declared parameter that is a member of the parameter
type list that is not part of a function definition may use the [*] notation in its
sequence of declarator specifiers to specify a variable length array type.
The absence of a parameter type list indicates that no typing information is given for
the function. A parameter type list consisting only of the keyword void indicates that
the function takes zero parameters. If the parameter type list ends in ellipses (…), the
function can have one or more additional arguments of variable or unknown type.
(See <stdarg.h>.)
The semantics of a function declarator are determined by its form and context. The
possible combinations are as follows:
• The declarator is not part of the function definition. The function is defined
elsewhere. In this case, the declarator cannot have an identifier list.
– If the parameter type list is absent, the declarator is an old-style function
declaration. Only the return type is significant.
– If the parameter type list is present, the declarator is a function prototype.
• The declarator is part of the function definition:
– If the declarator has an identifier list, the declarator is an old-style function
definition. Only the return type is significant.
– If the declarator has a parameter type list, the definition is in prototype form.
If no previous declaration for this function has been encountered, a function
prototype is created for it that has file scope.
If two declarations (one of which can be a definition) of the same function in the
same scope are encountered, they must match, both in type of return value and in
parameter type list. If one and only one of the declarations has a parameter type list,
the behavior varies between ANSI C and Traditional C.
In traditional C, most combinations pass without any diagnostic messages. However,
an error message is emitted for cases where an incompatibility is likely to lead to a
007–0701–150 83
7: Declarations
run-time failure. For example, a float type in a parameter type list of a function
prototype is incompatible with any old-style declaration for the same function;
therefore, SGI considers such redeclarations erroneous.
In ANSI C, if the type of any parameter declared in the parameter type list is other
than that which would be derived using the default argument promotions, an error is
posted. Otherwise, a warning is posted and the function prototype remains in scope.
In all cases, the type of the return value of duplicate declarations of the same function
must match, as must the use of ellipses.
When a function is invoked for which a function prototype is in scope, an attempt is
made to convert each actual parameter to the type of the corresponding formal
parameter specified in the function prototype, superseding the default argument
promotions. In particular, floats specified in the type list are not converted to
double before the call. If the list terminates with an ellipsis (...), only the parameters
specified in the prototype have their types checked; additional parameters are
converted according to the default argument promotions (discussed in "Type
Qualifiers", page 77). Otherwise, the number of parameters appearing in the
parameter list at the point of call must agree in number with those in the function
prototype.
The following are two examples of function prototypes:
double foo(int *first, float second, ... );
int *fip(int a, long l, int (*ff)(float));
The first prototype declares a function foo() which returns a double and has (at
least) two parameters: a pointer to an int and a float. Further parameters can
appear in a call of the function and are unspecified. The default argument promotions
are applied to any unspecified arguments. The second prototype declares a function
fip(), which returns a pointer to an int. The function fip() has three parameters:
an int, a long, and a pointer to a function returning an int that has a single
(float) argument.
When a function call occurs, each argument is converted using the default argument
promotions unless that argument has a type specified in a corresponding in-scope
prototype for the function being called. It is easy to envision situations that could
prove disastrous if some calls to a function are made with a prototype in-scope and
some are not. Unexpected results can also occur if a function is called with different
84 007–0701–150
C Language Reference Manual
Restrictions on Declarators
Not all the possibilities allowed by the syntax of declarators are permitted. The
following restrictions apply:
• Functions cannot return arrays or functions although they can return pointers.
• No arrays of functions exist although arrays of pointers to functions can exist.
• A structure or union cannot contain a function, but it can contain a pointer to a
function.
As an example, the following declaration declares an integer i; a pointer to an
integer, ip; a function returning an integer, f(); a function returning a pointer to an
integer, fip(); and a pointer to a function that returns an integer, pfi:
It is especially useful to compare the last two. The binding of *fip() is *(fip()).
The declaration suggests, and the same construction in an expression requires, the
calling of a function fip(), and then using indirection through the (pointer) result to
yield an integer. In the declarator *pfi)(), the extra parentheses are necessary,
because they are also in an expression, to indicate that indirection through a pointer
to a function yields a function, which is then called and returns an integer.
007–0701–150 85
7: Declarations
Type Names
In several contexts (for example, to specify type conversions explicitly by means of a
cast, in a function prototype, and as an argument of sizeof), it is best to supply the
name of a data type. This naming is accomplished using a “type name,” whose
syntax is a declaration for an object of that type without the identifier.
The syntax for type names is as follows:
type-name: specifier-qualifier-list abstract-declaratoropt
abstract-declarator: pointer
pointeropt direct-abstract-declarator
direct-abstract-declarator: (abstract-declarator)
direct-abstract-declaratoropt [constant-expressionopt]
direct-abstract-declaratoropt (parameter-type-listopt)
The type name created can be used as a synonym for the type of the omitted
identifier. The syntax indicates that a set of empty parentheses in a type name is
interpreted as function with no parameter information rather than as redundant
parentheses surrounding the (omitted) identifier.
Examples of type names are shown in Table 7-1, page 86.
Type Description
int Integer
int * Pointer to integer
int *[3] Array of three pointers to integers
int (*)[3] Pointer to an array of three integers
int *(void) Function with zero arguments returning pointer to integer
86 007–0701–150
C Language Reference Manual
Type Description
int Pointer to function returning an integer, that has a variable
(*)(float, number of arguments the first of which is a float
...)
int (*[3])() Array of three pointers to functions returning an integer for
which no parameter type information is given
Implicit Declarations
It is not always necessary to specify both the storage class and the type of identifiers
in a declaration. The storage class is supplied by the context in external definitions,
and in declarations of formal parameters and structure members. Missing storage
class specifiers appearing in declarations outside of functions are assumed to be
extern (see "External Name Changes", page 20, for details. Missing type specifiers in
this context are assumed to be int. In a declaration inside a function, if a type but no
storage class is indicated, the identifier is assumed to be auto. An exception to the
latter rule is made for functions because auto functions do not exist. If the type of an
identifier is “function returning <type>”, it is implicitly declared to be extern.
In an expression, an identifier followed by a left parenthesis (indicating a function
call) that is not already declared is implicitly declared to be of type function
returning int.
typedef
Declarations with the storage class specifier typedef do not define storage. A
typedef has the following syntax:
typedef-name: identifier
An identifier appearing in a typedef declaration becomes a synonym for the type
rather than becoming an object with the given type. For example, if the int type
specifier in the following example were preceded with typedef, the identifier
declared as an object would instead be declared as a synonym for the array type:
int intarray[10]];
007–0701–150 87
7: Declarations
In the following example, the last three declarations are legal. The type of distance
is int, that of metricp is pointer to int, and that of z is the specified structure. The
zp is a pointer to such a structure:
typedef int MILES, *KLICKSP;
typedef struct {
double re, im;
}
complex;
MILES distance;
extern KLICKSP metricp;
complex z, *zp;
The typedef does not introduce brand-new types, only synonyms for types that
could be specified in another way. For instance, in the preceding example, distance
is considered to have the same type as any other int object.
typedef declarations that specify a variably modified type have block scope. The
array size specified by the variable length array type is evaluated at the time the type
definition is declared and not at the time it is used as a type specifier in an actual
declarator.
Initialization
A declaration of an object or of an array of unknown size can specify an initial value
for the identifier being declared. The initializer is preceded by = and consists of an
expression or a list of values enclosed in nested braces:
initializer: assignment-expression
{initializer-list}
88 007–0701–150
C Language Reference Manual
initializer-list: initializer
initializer-list , initializer
There cannot be more initializers than there are objects to be initialized. All the
expressions in an initializer for an object of static storage duration must be constant
expressions (see "Primary Expressions", page 51). Objects with automatic storage
duration can be initialized by arbitrary expressions involving constants and
previously declared variables and functions, except for aggregate initialization, which
can include only constant expressions.
Identifiers declared with block scope and either external or internal linkage (that is,
objects declared in a function with the storage class specifier extern) cannot be
initialized.
Variables of static storage duration that are not explicitly initialized are implicitly
initialized to zero. The value of automatic and register variables that are not explicitly
initialized is undefined.
When an initializer applies to a scalar (a pointer or an object of arithmetic type; see
"Derived Types", page 40), it consists of a single expression, perhaps in braces. The
initial value of the object is taken from the expression. With the exception of type
qualifiers associated with the scalar, which are ignored during the initialization, the
same conversions as for assignment are performed.
Initialization of Aggregates
In traditional C, it is illegal to initialize a union. It is also illegal to initialize a struct
of automatic storage duration.
In ANSI C, objects that are struct or union types can be initialized, even if they
have automatic storage duration. unions are initialized using the type of the first
named element in their declaration. The initializers used for a struct or union of
automatic storage duration must be constant expressions if they are in an initializer
list. If the struct or union is initialized using an assignment expression, the
expression need not be constant.
When the declared variable is a struct or array, the initializer consists of a
brace-enclosed, comma-separated list of initializers for the members of the aggregate
written in increasing subscript or member order. If the aggregate contains
subaggregates, this rule applies recursively to the members of the aggregate.
007–0701–150 89
7: Declarations
If the initializer of a subaggregate or union begins with a left brace, its initializers
consist of all the initializers found between the left brace and the matching right
brace. If, however, the initializer does not begin with a left brace, then only enough
elements from the list are taken to account for the members of the subaggregate; any
remaining members are left to initialize the next member of the aggregate of which
the current subaggregate is a part.
Within any brace-enclosed list, there should not be more initializers than members. If
there are fewer initializers in the list than there are members of the aggregate, then
the aggregate is padded with zeros.
Unnamed struct or union members are ignored during initialization.
In ANSI C, if the variable is a union, the initializer consists of a brace-enclosed
initializer for the first member of the union. Initialization of struct or union objects
with automatic storage duration can be abbreviated as a simple assignment of a
compatible struct or union object.
A final abbreviation allows a char array to be initialized by a string literal. In this
case, successive characters of the string literal initialize the members of the array.
In ANSI C, an array of wide characters (that is, whose element type is compatible with
wchar_t) can be initialized with a wide string literal (see "String Literals", page 28).
Examples of Initialization
The following example declares and initializes x as a one-dimensional array that has
three members, because no size was specified and there are three initializers:
int x[] = { 1, 3, 5 };
90 007–0701–150
C Language Reference Manual
The next example achieves precisely the same effect. The initializer for y begins with
a left brace but that for y[0] does not; therefore, three elements from the list are
used. Likewise, the next three are taken successively for y[1] and y[2]:
float y[4][3] =
{
1, 3, 5, 2, 4, 6, 3, 5, 7
};
The following example demonstrates the ANSI C rules. A union object, dc_u, is
initialized by using the first element only:
union dc_u {
double d;
char *cptr;
};
The final example shows a character array whose members are initialized with a
string literal. The length of the string (or size of the array) includes the terminating
NULL character, \0:
char msg[] = "Syntax error on line %s\n";
007–0701–150 91
Chapter 8
Statements
Expression Statement
Most statements are expression statements, which have the following form:
expression-statement: expressionopt;
Usually expression statements are expressions evaluated for their side effects, such as
assignments or function calls. A special case is the null statement, which consists of
only a semicolon.
007–0701–150 93
8: Statements
statement-list: statement
statement-list statement
Declarations within compound statements have block scope. If any of the identifiers
in the declaration list were previously declared, the outer declaration is hidden for the
duration of the block, after which it resumes its force. In traditional C, however,
function declarations always have file scope whenever they appear.
Initialization of identifiers declared within the block is restricted to those that have no
linkage. Thus, the initialization of an identifier declared within the block using the
extern specifier is not allowed. These initializations are performed only once, prior to
the first entry into the block, for identifiers with static storage duration. For
identifiers with automatic storage duration, it is performed each time the block is
entered at the top. It is currently possible (but a bad practice) to transfer into a block;
in that case, no initializations are performed.
Selection Statements
Selection statements include if and switch statements and have the following form:
selection-statement: if (expression) statement
if (expression) statement else statement
switch (expression) statement
Selection statements choose one of a set of statements to execute, based on the
evaluation of the expression. The expression is referred to as the controlling
expression.
if Statement
The controlling expression of an if statement must have scalar type.
For both forms of the if statement, the first statement is executed if the controlling
expression evaluates to nonzero. For the second form, the second statement is
executed if the controlling expression evaluates to zero. An else clause that follows
multiple sequential else-less if statements is associated with the most recent if
statement in the same block (that is, not in an enclosed block).
94 007–0701–150
C Language Reference Manual
switch Statement
The controlling expression of a switch statement must have integral type. The
statement is typically a compound statement, some of whose constituent statements
are labeled case statements (see "Labeled Statements", page 98, and "continue
Statement", page 97, respectively).
The following is a simple example of a complete switch statement:
switch (c) {
case ’o’:
oflag = TRUE;
break;
case ’p’:
pflag = TRUE;
break;
case ’r’:
rflag = TRUE;
break;
default :
(void) fprintf(stderr,
"Unknown option\n");
exit(2);
}
Iteration Statements
Iteration statements execute the attached statement (called the body) repeatedly until
the controlling expression evaluates to zero. In the for statement, the second
expression is the controlling expression. The format is as follows:
iteration-statement: while (expression) statement
do statement while (expression) ;
for (expressionopt ; expressionopt ; expressionopt) statement
The controlling expression must have scalar type.
The flow of control in an iteration statement can be altered by a jump statement (see
"Jump Statements", page 97).
007–0701–150 95
8: Statements
while Statement
The controlling expression of a while statement is evaluated before each execution of
the body.
do Statement
The controlling expression of a do statement is evaluated after each execution of the
body.
for Statement
The for statement has the following form:
for (expressionopt ; statement
expressionopt ;
expressionopt)
The first expression specifies initialization for the loop. The second expression is the
controlling expression, which is evaluated before each iteration. The third expression
often specifies incrementation. It is evaluated after each iteration.
This statement is equivalent to the following:
expression-1; while (expression-2)
{
statement
expression-3;
}
One exception exists, however. If a continue statement (see "continue Statement",
page 97 is encountered, expression-3 of the for statement is executed prior to the next
iteration.
Any or all of the expressions can be omitted. A missing expression-2 makes the
implied while clause equivalent to while. Other missing expressions are simply
dropped from the previous expansion.
96 007–0701–150
C Language Reference Manual
Jump Statements
Jump statements cause unconditional transfer of control. The syntax is as follows:
jump-statement: goto identifier;
continue;
break;
return expressionopt;
goto Statement
Control can be transferred unconditionally by means of a goto statement:
goto identifier;
The identifier must name a label located in the enclosing function. If the label has not
yet appeared, it is implicitly declared. (See "Labeled Statements", page 98, for more
information.)
continue Statement
The continue statement can appear only in the body of an iteration statement. It
causes control to pass to the loop-continuation portion of the smallest enclosing
while, do, or for statement; that is, to the end of the loop. Consider each of the
following statements:
while (...)
{
..
contin: ;
}
do {
...
contin: ;
} while (...) ;
for (...) {
007–0701–150 97
8: Statements
...
contin: ;
}
break Statement
The break statement can appear only in the body of an iteration statement or code
attached to a switch statement. It transfers control to the statement immediately
following the smallest enclosing iteration or switch statement, terminating its
execution.
return Statement
A function returns to its caller by means of the return statement. The value of the
expression is returned to the caller (after conversion to the declared type of the
function), as the value of the function call expression. The return statement cannot
have an expression if the type of the current function is void.
If the end of a function is reached before the execution of an explicit return, an
implicit return (with no expression) is executed. If the value of the function call
expression is used when none is returned, the behavior is undefined.
Labeled Statements
Labeled statements have the following syntax:
labeled-statement: identifier : statement
case constant-expression : statement
default : statement
A case or default label can appear only on statements that are part of a switch.
98 007–0701–150
C Language Reference Manual
Any statement can have a label attached as a simple identifier. The scope of such a
label is the current function. Thus, labels must be unique within a function. In
traditional C, identifiers used as labels and in object declarations share a name space.
Thus, use of an identifier as a label hides any declaration of that identifier in an
enclosing scope. In ANSI C, identifiers used as labels are placed in a different name
space from all other identifiers and do not conflict. Therefore, the following code
fragment is legal in ANSI C but not in traditional C:
{
int foo;
foo = 1;
…
goto foo;
…
foo: ;
}
007–0701–150 99
Chapter 9
External Definitions
007–0701–150 101
9: External Definitions
declarator has a possibly empty identifier list, and the declaration list declares the
types of the formal parameters. register is the only storage class specifier
permitted in declarations that are in the declaration list. Any identifiers in the
identifier list of the function declarator that do not have their types specified in the
declaration list are assumed to have type int.
Each parameter has block scope and automatic storage duration. ANSI C and
traditional C place parameters in different blocks. See "Scope", page 31, for details.
Each parameter is also an lvalue, but because function calls in C are by value, the
modification of a parameter of arithmetic type cannot affect the corresponding
argument. Pointer parameters, while unmodifiable for this reason, can be used to
modify the objects to which they point.
Argument promotion rules are discussed in "Function Calls", page 52.
The type of a function must be either void or an object type that is not an array.
102 007–0701–150
Chapter 10
Multiprocessing Directives
#pragma Description
#pragma copyin Copies the value from the master thread’s version of an -Xlocal-linked global
variable into the slave thread’s version.
#pragma critical Protects access to critical statements.
#pragma enter gate Indicates the point that all threads must clear before any threads are allowed to
pass the corresponding exit gate.
#pragma exit gate Stops threads from passing this point until all threads have cleared the
corresponding enter gate.
#pragma independent Starts an independent code section that executes in parallel with other code in
the parallel region.
#pragma local Tells the compiler the names of all the variables that must be local to each thread.
#pragma no side Tells the compiler to assume that all of the named functions are safe to execute
effects concurrently.
#pragma one processor Causes the next statement to be executed on only one processor.
#pragma parallel Marks the start of a parallel region.
#pragma pfor Marks a for loop to run in parallel.
007–0701–150 103
10: Multiprocessing Directives
#pragma Description
#pragma set chunksize Tells the compiler which values to use for chunksize.
#pragma set Tells the compiler which values to use for numthreads.
numthreads
#pragma set schedtype Tells the compiler which values to use for schedtype.
#pragma shared Tells the compiler the names of all the variables that the threads must share.
#pragma synchronize Stops threads until all threads reach here.
104 007–0701–150
C Language Reference Manual
reduce the overhead associated with moving from serial execution to parallel
execution.
Large mixed parallel regions avoid the forced synchronization that occurs at the end
of each parallel region. The large mixed parallel region also allows you to use
#pragma directives that execute independent code sections that run concurrently.
Thus, if a thread finishes its work early, it can go on to execute the next section of
code–provided that the next section of code is not dependent on the completion of the
previous section. However, when you create parallel regions, you need more
sophisticated synchronization methods than you need for isolated parallel loops.
007–0701–150 105
10: Multiprocessing Directives
Example 1:
#pragma parallel shared(a,b,c, n) local(i) pfor
for (i=0; i<n; i++) a[i]=b[i]+c[i];
Example 2:
#pragma parallel
#pragma shared( a )
#pragma shared( b, c, n )
#pragma local( i )
#pragma pfor
for (i=0; i<n; i++) a[i]=b[i]+c[i];
Parallel Regions
A parallel region consists of a number of work-sharing constructs. The C/C++
compiler supports the following work-sharing constructs:
• A loop executed in parallel
• “Local” code run (identically) by all threads
• An independent code section executed in parallel with the rest of the code in the
parallel region
• Code executed by only one thread
• Code run in “protected mode” by all threads
In addition, the C/C++ compiler supports three types of explicit synchronization. To
account for data dependencies, it is sometimes necessary for threads to wait for all
other threads to complete executing an earlier section of code. Three sets of directives
implement this coordination: #pragma critical, #pragma synchronize, and
#pragma enter gate and #pragma exit gate.
The parallel region should have a single entry at the top and a single exit at the
bottom.
To start a parallel region, use the #pragma parallel directive. To mark a for loop
to run in parallel, use the #pragma pfor directive. To start an independent code
section that executes in parallel with the rest of the code in the parallel region, use the
#pragma independent.
106 007–0701–150
C Language Reference Manual
When you execute a program, nothing actually runs in parallel until it reaches a
parallel region. Then multiple threads begin (or continue, if this is not the first
parallel region), and the program runs in parallel mode. When the program exits a
parallel region, only a single thread continues (sequential mode) until the program
again enters a parallel region and the process repeats.
The loop computes the cumulative sum of the elements of the array. Because the
value of a sum computed in one iteration is used in the next iteration, the loop as
written cannot be executed in parallel directly on multiprocessors.
However, you can rewrite the above loop to compute the local sum on each processor
by introducing a local variable. This breaks the iteration dependency of sum and the
loop can be executed in parallel on multiprocessors. This loop computes the local
sum of the elements on each processor, which can subsequently be serially added to
yield the final sum.
The multiprocessing C/C++ compiler provides a reduction clause as a modifier for
a pfor statement. Using this clause, the above loop can be parallelized as follows:
int a[10000];
int i;
int sum = 0
#pragma parallel shared(a, sum) local(i)
#pragma pfor reduction(sum)
for i=0; i<10000; i++)
sum = sum + a[i];
007–0701–150 107
10: Multiprocessing Directives
The preceding example that uses a reduction clause has the same semantics as the
following code that uses local variables and explicit synchronization. In this code,
because sum is shared, the computation of the final sum has to be done in a critical
region to allow each processor exclusive access to sum:
int a[10000];
int i;
int sum,localsum;
sum = 0;
#pragma parallel shared(a,sum) local(i,localsum)
{
localsum = 0;
#pragma pfor iterate(;;)
for (i = 0; i < 10000; i++) localsum +=a[i];
#pragma critical
sum = sum + localsum;
}
When the various operations are performed in parallel, they can be invoked in any
order. In order for the reduction to produce a unique result, the binary operation, op,
must therefore satisfy the associativity property, as shown below:
a op (b op c) == (a op b) op c
108 007–0701–150
C Language Reference Manual
Performance Considerations
The reduction example in "Restrictions on the Reduction Clause", page 108, has the
drawback that when the number of processors increases, there is more contention for
the lock in the critical region.
The following example uses a shared array to record the result on individual
processors. The array entries are CacheLine apart to prevent write contention on the
cache line (128 bytes in this example. The array permits recording results for up to
NumProcs processors. Both these variables CacheLine and NumProcs can be tuned for a
specific platform:
#define CacheLine 128
int a[10000];
int i, sum;
int *localsum = malloc(NumProcs * CacheLine);
sum = 0;
#pragma parallel shared (a, sum, localsum) local (i) local (myid)
{
myid = mp_my_threadnum();
#pragma pfor
for (i = 0; i < 10000; i++)
localsum [myid] += a [i];
}
for (i = 0; i < numprocs; i++)
sum += localsum[i];
The only operation in the critical region is the computation of the final result from the
local results on individual processors.
In the case when the reduction applies to an array of integers, the reduction function
can be specialized by using an intrinsic operation __fetch_and_<op> rather than
the more expensive critical region. (See "Synchronization Intrinsics", page 123">.)
For example, to add an array of integers, the critical region can be replaced by the
following call:
__fetch_and_add(&sum, localsum);
007–0701–150 109
10: Multiprocessing Directives
The intrinsic __fetch_and_<op> is defined only for the following operations: add,
sub, or, xor, nand, mpy, min, and max; and for the type integers together with their
size and signed variants. Therefore, it cannot be used in the general case.
Reduction Example
#pragma pfor
{
for (i = first; i < last - first; i++)
localresult = op(localresult,ar[i]);
}
110 007–0701–150
C Language Reference Manual
#pragma critical
result = op(result,localresult);
}
return result;
}
With the preceding definition of reduction, you can perform the following reduction:
adsum = reduction(0,size,0,ad,plus<double>());
acsum = reduction(0,size,czero,ac,plus<Complex>());
Restrictions on pfor
If you are writing a pfor loop for the multiprocessing C++ compiler, the index
variable i can be declared within the for statement using the following:
int i = 0;
The ANSI C++ standard states that the scope of the index variable declared in a for
statement extends to the end of the for statement, as in this example:
#pragma pfor
for (int i = 0, ...) { ... }
The MIPSpro 7.2 C++ compiler does not enforce this rule. By default, the scope
extends to the end of the enclosing block. The default behavior can be changed by
using the command line option -LANG:ansi-for-init-scope=on which enforces
the ANSI C++ standard.
To avoid future problems, write for loops in accordance with the ANSI standard, so a
subsequent change in the compiler implementation of the default scope rules does not
break your code.
007–0701–150 111
10: Multiprocessing Directives
try {
throw 10;
}
/* .... */
catch (int) {
printf(‘‘!!!!exception caught in process \n’’);
printf(‘‘My thread number is %d\n’’,mp_my_threadnum());
} /* end of try block */
} /* end of parallel region */
}
112 007–0701–150
C Language Reference Manual
main() {
#pragma parallel
{
ehfn();
}
}
The reason this program aborts is that the throw propagates past the
multiprocessing region.
Scoping Restrictions
The following default scope rules apply for the C++ multiprocessing compiler.
• Class objects or structures that have constructors [that is, non-pods (plain old data
structures)] cannot be placed on the local list of #pragma parallel.
The following is invalid:
class C {
....
};
main() {
C c;
007–0701–150 113
10: Multiprocessing Directives
Instead, declaring such objects within the parallel region allows the default rule to
be used to indicate that they are local (as the following example illustrates):
main() {
#pragma parallel
{
C c;
....
}
}
• Structure fields and class object members cannot be placed on the local list.
Instead, the entire class object must be made local.
• Values of variables in the local list are not copied into each processor’s local
variables; instead, initialize locals within the parallel program text. For example,
main() {
int i;
i = 0;
#pragma parallel local(i)
{
// Here i is not 0.
// Explicit initialization of i within the parallel region
// is necessary
}
}
114 007–0701–150
Chapter 11
A number of features are provided so that you can override the multiprocessing
defaults and customize the parallelism to your particular applications. The following
sections provide brief explanations of these features.
007–0701–150 115
11: Multiprocessing Advanced Features
mp_create takes a single integer argument, the total number of execution threads
desired. Note that the total number of threads includes the master thread. Thus,
mp_create(n) creates one thread less than the value of its argument. mp_destroy
takes no arguments; it destroys all the slave execution threads, leaving the master
untouched.
When the slave threads die, they generate a SIGCLD signal. If your program has
changed the signal handler to catch SIGCLD, it must be prepared to deal with this
signal when mp_destroy is executed. This signal also occurs when the program
exits; mp_destroy is called as part of normal cleanup when a parallel job terminates.
mp_blocktime
The slave threads spin wait until there is work to do. This makes them immediately
available when a parallel region is reached. However, this consumes CPU resources.
After enough wait time has passed, the slaves block themselves through blockproc.
Once the slaves are blocked, it requires a system call to unblockproc to activate the
slaves again (refer to the unblockproc(2) man page for details). This makes the
response time much longer when starting up a parallel region.
This trade-off between response time and CPU usage can be adjusted with the
mp_blocktime call. The mp_blocktime routine takes a single integer argument
that specifies the number of times to spin before blocking. By default, it is set to
10,000,000; this takes roughly one second. If called with an argument of 0, the slave
threads will not block themselves no matter how much time has passed. Explicit calls
to mp_block, however, will still block the threads.
This automatic blocking is transparent to the user’s program; blocked threads are
automatically unblocked when a parallel region is reached.
116 007–0701–150
C Language Reference Manual
mp_my_threadnum
The mp_my_threadnum routine is a zero-argument function that allows a thread to
differentiate itself while in a parallel region. If there are n execution threads, the
function call returns a value between zero and n – 1. The master thread is always
thread zero. This function can be useful when parallelizing certain kinds of loops.
Most of the time the loop index variable can be used for the same purpose.
Occasionally, the loop index may not be accessible, as, for example, when an external
routine is called from within the parallel loop. This routine provides a mechanism for
those cases.
mp_set_slave_stacksize
The mp_set_slave_stacksize routine sets the stack size (in bytes) to be used by
the slave processes when they are created (using sprocsp). The default size is 16
MB. Slave processes only allocate their local data onto their stack, shared data (even if
allocated on the master’s stack) is not counted.
007–0701–150 117
11: Multiprocessing Advanced Features
Source statement:
mp_set_numthreads (2)
Similarly, the following sh commands prevent the slave threads from autoblocking, as
does the source statement:
sh commands:
% set MP_BLOCKTIME 0
% export MP_BLOCKTIME
Source statement:
mp_blocktime (0);
118 007–0701–150
C Language Reference Manual
MP_SCHEDTYPE, CHUNK
These environment variables specify the type of scheduling to use on for loops that
have their scheduling type set to RUNTIME. For example, the following csh
commands cause loops with the RUNTIME scheduling type to be executed as
interleaved loops with a chunk size of 4:
007–0701–150 119
11: Multiprocessing Advanced Features
The defaults are the same as on the #pragma pfor directive; if neither variable is
set, SIMPLE scheduling is assumed. If MP_SCHEDTYPE is set, but CHUNK is not set, a
CHUNK of 1 is assumed. If CHUNK is set, but MP_SCHEDTYPE is not, DYNAMIC
scheduling is assumed.
MP_SLAVE_STACKSIZE
The stack size of slave processes can be controlled through the environment variable
MP_SLAVE_STACKSIZE, which may be set to the desired stacksize in bytes. The
default value is 16 MB (4 MB for more than 64 threads).
MPC_GANG
MPC_GANG specifies gang scheduling. Set MPC_GANG to ON to enable gang scheduling.
To disable gang scheduling, set MPC_GANG to OFF.
120 007–0701–150
C Language Reference Manual
007–0701–150 121
11: Multiprocessing Advanced Features
Note: Call these routines only after the threads have been created (typically, the first
pfor/parallel region). Performing these operations while the program is still serial
leads to a run-time error because each thread’s copy has not yet been created.
In the example below, compiling with -Wl,-Xlocal, myvars ensures that each
thread has a private copy of x and y.
struct {
int x;
double y[100];
} myvars;
122 007–0701–150
C Language Reference Manual
The following example copies the value of x on thread 3 into the private copy of x for
the current thread.
mp_shmem_get32 (&x, &x, 1, 3)
The next example copies the value of localvar into the thread 5 copy of x.
mp_shmem_put32 (&x, &localvar, 1, 5)
The example below fetches values from the thread 7 copy of array y into
localarray.
mp_shmem_get64 (&localarray, &y, 100, 7)
The next example copies the value of every other element of localarray into the
thread 9 copy of y.
mp_shmem_iput64 (&y, &localarray, 2, 2, 50, 9)
Synchronization Intrinsics
The intrinsics described in this section provide a variety of primitive synchronization
operations. Besides performing the particular synchronization operation, each of these
intrinsics has two key properties:
• The function performed is guaranteed to be atomic (typically achieved by
implementing the operation using a sequence of load-linked and/or
store-conditional instructions in a loop).
• Associated with each instrinsic are certain memory barrier properties that restrict
the movement of memory references to visible data across the intrinsic operation
(by either the compiler or the processor).
A visible memory reference is a reference to a data object potentially accessible by
another thread executing in the same shared address space. A visible data object can
be one of the following:
• C/C++ global data
• Data declared extern
• Volatile data
• Static data (either file-scope or function-scope)
007–0701–150 123
11: Multiprocessing Advanced Features
124 007–0701–150
C Language Reference Manual
The ellipses (...) refer to an optional list of variables protected by the memory
barrier.
Each of these operations behaves as follows:
• Atomically performs the specified operation with the given value on *ptr, and
returns the old value of *ptr.
{tmp = *ptr; *ptr <op>= value; return tmp;}
• Full barrier
007–0701–150 125
11: Multiprocessing Advanced Features
int
long
long long
unsigned int
unsigned long
unsigned long long
• Full barrier
• Full barrier
126 007–0701–150
C Language Reference Manual
The ellipses (...) refer to an optional list of variables protected by the memory
barrier.
• Acquire barrier
007–0701–150 127
11: Multiprocessing Advanced Features
• Release barrier
The memory barrier semantics of the intrinsics guarantee that no memory reference to
visible data is moved out of the above critical section, either ahead of the lock-acquire
or past the lock-release.
Note: Pure spin-wait locks can perform poorly under heavy contention.
If the data structures protected by the lock are known precisely (for example, x, y,
and z in the example below), then those data structures can be precisely identified as
follows:
int lockvar = 0;
while (__lock_test_and_set (&lockvar, 1, x, y, z) != 0);
... read/modify the variables x, y, and z ...
__lock_release (&lockvar, x, y, z);
128 007–0701–150
Appendix A
Implementation-Defined Behavior
Translation (F.3.1)
• Whether each nonempty sequence of white-space characters other than newline
is retained or replaced by one space character (2.1.1.2).
A nonempty sequence of white-space characters (other than newline) is retained.
• How a diagnostic is identified (2.1.1.3).
Successful compilations are silent. Diagnostics are, in general, emitted to standard
error. Diagnostic messages have the general pattern of
file-name,line-number:severity(number): message in -n32 and -64 modes.
Diagnostics have a slightly different pattern in -o32 mode. Also, the range of
numbers in -o32 mode is disjointed from the range in -n32 and -64 modes.
For example, typical messages from the ANSI C compiler front end in -n32 and
-64 mode look like this:
"t4.c’’, line 4: error(1020):identifier "x’’ is undefined
"t4.c’’, line 5: warning(1551):variable "y’’ is used before its
value is set
007–0701–150 129
A: Implementation-Defined Behavior
Environment (F.3.2)
• Support of freestanding environments.
No support is provided for a freestanding environment.
• The semantics of the arguments to main (2.1.2.2.1).
main is defined to have the two required parameters argc and argv. A third
parameter, envp, is provided as an extension. That is, main would have the
equivalent of the following prototype:
int main(int argc, char *argv[], char *envp[])
130 007–0701–150
C Language Reference Manual
Identifiers (F.3.3)
• The number of significant initial characters (beyond 31) in an identifier without
external linkage (3.1.2).
All characters are significant.
• The number of significant initial characters (beyond 6) in an identifier with
external linkage (3.1.2).
All characters are significant.
• Whether case distinctions are significant in an identifier with external linkage
(3.1.2).
Case distinctions are always significant.
Characters (F.3.4)
• The members of the source and execution character sets, except as explicitly
specified in the standard (2.2.1).
Only the mandated characters are present. The source character set includes all
printable ASCII characters, hexadecimal 0x20 through 0x7e, and 0x7 through 0xc
(the standard escape sequences).
• The values to which the standard escape sequences are translated (2.2.2).
The escape sequences are translated as specified for standard ASCII: \a = 0x7, \b
= 0x8, \f = 0xc, \n = 0xa, \r = 0xd, \t = 0x9, \v=0xb
• The shift states used for the encoding of multibyte characters (2.2.1.2).
The multibyte character set is identical to the source and execution character sets.
There are no shift states.
• The number of bits in a character in the execution character set (2.2.4.2.1).
There are eight bits per character.
007–0701–150 131
A: Implementation-Defined Behavior
• The mapping of members of the source character set (in character constants and
string literals) to members of the execution character set (3.1.3.4).
The mapping is the identity mapping.
• The value of an integer character constant that contains a character or escape
sequence not represented in the basic execution character set or in the extended
character set for a wide character constant (3.1.3.4).
With the exception of newline (0xa), backslash (’\’), and 0xff (end-of-file), eight-bit
values appearing in an integer character constant are placed in the resultant
integer in the same fashion as are characters that are members of the execution
character set (see below). A backslash, newline, or 0xff can be placed in a
character constant by preceding it with a backslash (that is, “escaping” it).
• The value of an integer character constant that contains more than one character
or a wide character constant that contains more than one multibyte character
(3.1.3.4).
You can assign up to four characters to an int using a character constant, as the
following example illustrates:
int t = ’a’; /* integer value 0x61 */
int t2 = ’ab’; /* integer value 0x6162 */
int t4 = ’abcd’; /* integer value 0x61626364 */
int t4 = ’abcde’; /* error: too many characters for */
/* character constant */
132 007–0701–150
C Language Reference Manual
• Whether a “plain” char has the same range of values as signed char or
unsigned char.
Plain char is the same as unsigned char by default. Use the -signed option
to cc to switch the range to be that of signed char.
Integers (F.3.5)
• The representations and sets of values of the various types of integers (3.1.2.5).
Integers are two’s complement binary. Table A-1, page 133, lists the sizes and
ranges of the various types of integer. The use of long long results in a warning
in -ansi and -ansiposix modes.
In -o32 and -n32 mode implementations, to take full advantage of the support
for 64-bit integral values in -ansi and -ansiposix modes, you can define the
macro _LONGLONG on the cc command line when using the types __uint64_t,
__int64_t, or library routines that are prototyped in terms of these types.
007–0701–150 133
A: Implementation-Defined Behavior
134 007–0701–150
C Language Reference Manual
See ANSI/IEEE Standard 754-1985 and IEEE Standard for Binary Floating-Point
Arithmetic. Table A-2, page 135, lists ranges of floating point types.
007–0701–150 135
A: Implementation-Defined Behavior
Registers (F.3.8)
• The extent to which objects can actually be placed in registers by use of the
register storage-class specifier (3.5.1).
The compilation system can use up to eight of the register storage-class specifiers
for nonoptimized code in -32 mode, and it ignores register specifiers for formal
parameters. Use of register specifiers is not recommended.
The register storage-class specifier is always ignored and the compilation system
makes its own decision about what should be in registers for optimized code (-O2
and above).
136 007–0701–150
C Language Reference Manual
For floating types, the access might cause a trap if the bits are not a legal floating
point value. For pointer types, the 32 bits (64 bits if in -64 mode) of the pointer
are picked up. The usability of the pointer depends on whether it points to a valid
object or function, and whether it is used appropriately. For example, a pointer
whose least-significant bit is set can point to a character, but not to an integer.
• The padding and alignment of members of structures (3.5.2.1).
This should present no problem unless binary data written by one implementation
are read by another.
Members of structures are on the same boundaries as the base data type
alignments anywhere else. A word is 32 bits and is aligned on an address, which
is a multiple of 4. unsigned and signed versions of a basic type use identical
alignment. Type alignments are given in Table A-3, page 137.
Type Alignment
long double Double- word boundary (-32 mode)
Quad-word boundary (-n32 and -64 modes)
double Double-word boundary
float Word boundary
long long Double-word boundary
long Word boundary (-n32 and -32 modes)
double-word boundary (-64 mode)
int Word boundary
pointer Word boundary
short Half-word boundary
char Byte boundary
007–0701–150 137
A: Implementation-Defined Behavior
Bits in a bitfield are allocated with the most-significant bit first within a unit.
• Whether a bitfield can straddle a storage-unit boundary (3.5.2.1).
Bitfields cannot straddle storage unit boundaries (relative to the beginning of the
struct or union), where a storage unit can be of size 8, 16, 32, or 64 bits.
• The integer type chosen to represent the values of an enumeration type (3.5.2.2).
Qualifiers (F.3.10)
• What constitutes an access to an object that has volatile-qualified type (3.5.3).
Objects of volatile-qualified type are accessed only as specified by the abstract
semantics, and as would be expected on a RISC architecture, no complex
instructions exist (for example, read-modify-write). Volatile objects appearing on
the left side of an assignment expression are accessed once for the write. If the
assignment is not simple, an additional read access is performed. Volatile objects
appearing in other contexts are accessed once per instance. Incrementation and
decrementation require both a read and a write access.
Volatile objects that are memory-mapped are accessed only as specified. If such an
object is of size char, for example, adjacent bytes are not accessed. If the object is
a bitfield, a read may access the entire storage unit containing the field. A write of
an unaligned field necessitates a read and write of the storage unit that contains it.
Declarators (F.3.11)
• The maximum number of declarators that can modify an arithmetic, structure, or
union type (3.5.4).
There is no limit.
138 007–0701–150
C Language Reference Manual
Statements (F.3.12)
• The maximum number of case values in a switch statement (3.6.4.2).
There is no limit.
007–0701–150 139
A: Implementation-Defined Behavior
See MIPSpro C and C++ Pragmas on the SGI Tech Pubs Library
(http://techpubs.sgi.com/library) for details on all supported #pragma
directives.
• The definitions for __DATE__ and __TIME__ when, respectively, the date and
time of translation are not available.
The date and time of translation are always available in this implementation.
• What is the maximum nesting depth of include files (3.8.2).
The maximum nesting depth of include files is 200.
• The diagnostic printed by and the termination behavior of the assert function
(4.2).
If an assertion given by assert(EX) fails, the following message is printed on
stderr using _write to its underlying fileno:
Assertion failed: EX, file <filename>, line <linenumber>
140 007–0701–150
C Language Reference Manual
Signals
• The set of signals for the signal function (4.7.1.1).
The signal set is listed in Table A-4, page 142, which is from the signal(2)
reference page. The set of signals conforms to the SVR4 ABI. Note that some of
the signals are not defined in -ansiposix mode. References in square brackets
beside the signal numbers are described under “Signal Notes” in the discussion of
signal semantics.
007–0701–150 141
A: Implementation-Defined Behavior
142 007–0701–150
C Language Reference Manual
• The semantics for each signal recognized by the signal function (4.7.1.1).
In the signal invocation signal(sig, func), func can be the address of a
signal handler, handler, or one of the two constant values (defined in
<sys/signal.h>) SIG_DFL or SIG_IGN. The semantics of these values are as
follows:
SIG_DFL Terminate process upon receipt of signal sig. (This
is the default if no call to signal for signal sig
occurs.) Upon receipt of the signal sig, the
receiving process is to be terminated with all of the
consequences outlined in the exit(2) reference
page. See note 1 under "Signal Notes", page 145.
SIG_IGN Ignore signal. The signal sig is to be ignored.
handler Catch signal. func is the address of function
handler.
If func is the address of handler, upon receipt of the signal sig, the receiving
process is to invoke handler as follows:
007–0701–150 143
A: Implementation-Defined Behavior
The remaining arguments are supplied as extensions and are optional. The value
of the second argument code is meaningful only in the cases shown in Table A-5,
page 144.
144 007–0701–150
C Language Reference Manual
The signal catching function is executed and then the interrupted system call
returns a -1 to the calling process with errno set to EINTR.
Signal Notes
Note: The core file can be truncated if the resultant file size would exceed either
ulimit (see the ulimit(2) reference page) or the process’s maximum core file
size (see the setrlimit(2) reference page).
2. For the signals SIGCLD, SIGWINCH, SIGPWR, SIGURG, and SIGIO, the actions
associated with each of the three possible values for func are as follows:
SIG_DFL Ignore signal. The signal is to be ignored.
SIG_IGN Ignore signal. The signal is to be ignored. Also, if
sig is SIGCLD, the calling process’s child processes
do not create zombie processes when they
terminate (see the exit(2) reference page).
handler Catch signal. If the signal is SIGPWR, SIGURG,
SIGIO, or SIGWINCH, the action to be taken is the
same as that previously described when func is the
007–0701–150 145
A: Implementation-Defined Behavior
146 007–0701–150
C Language Reference Manual
sent from the terminal driver in response to the SWTCH character being entered
from the keyboard (see the termio(7) reference page. SIGTTIN is sent from the
terminal driver when a background process attempts to read from its controlling
terminal. If SIGTTIN is ignored by the process, then the read returns EIO.
SIGTTOU is sent from the terminal driver when a background process attempts to
write to its controlling terminal when the terminal is in TOSTOP mode. If
SIGTTOU is ignored by the process, then the write succeeds, regardless of the
state of the controlling terminal.
signal does not catch an invalid function argument, func, and results are undefined
when an attempt is made to execute the function at the bad address.
SIGKILL immediately terminates a process, regardless of its state.
Processes stopped via job control (typically CTRL+Z) do not act upon any delivered
signals other than SIGKILL until the job is restarted. Processes blocked via a
blockproc system call unblock if they receive a signal that is fatal (that is, a
non-job-control signal that they are not catching). These processes remained stopped,
however, if the job they are a part of is stopped. Only upon restart do they die. Any
non-fatal signals received by a blocked process do not cause the process to be
unblocked. An unblockproc or unblockprocall system call is necessary.
If an instance of signal sig is pending when signal(sig, func) is executed, the pending
signal is cancelled unless it is SIGKILL.
signal fails if sig is an illegal signal number, including SIGKILL and SIGSTOP, or if
an illegal operation is requested (such as ignoring SIGCONT, which is ignored by
default). In these cases, signal returns SIG_ERR and sets errno to EINVAL.
After a fork, the child inherits all handlers and signal masks. If any signals are
pending for the parent, they are not inherited by the child.
The exec routines reset all caught signals to the default action; ignored signals remain
ignored; the blocked signal mask is unchanged and pending signals remain pending.
The following reference pages contain other relevant information: intro(2),
blockproc(2), kill(2), pause(2), ptrace(2), sigaction(2), sigset(2), wait(2),
setjmp(3c), sigvec, and kill(1).
Diagnostics
Upon successful completion, signal returns the previous value of func for the
specified signal sig. Otherwise, a value of SIG_ERR is returned and errno is set to
indicate the error. SIG_ERR is defined in the <sys/signal.h> header file.
007–0701–150 147
A: Implementation-Defined Behavior
Caution: Signals raised by the instruction stream, SIGILL, SIGEMT, SIGBUS, and
! SIGSEGV, will cause infinite loops if their handler returns, or the action is set to
SIG_IGN. The POSIX signal routines (sigaction, sigpending, sigprocmask,
sigsuspend, sigsetjmp), and the BSD 4.3 signal routines (sigvec, signal,
sigblock, sigpause, sigsetmask) must never be used with signal or sigset.
Before entering the signal-catching function, the value of func for the caught signal is
set to SIG_DFL, unless the signal is SIGILL, SIGTRAP, or SIGPWR. This means that
before exiting the handler, a signal call is necessary to again set the disposition to
catch the signal.
Note that handlers installed by signal execute with no signals blocked, not even the
one that invoked the handler.
• The default handling and the handling at program startup for each signal
recognized by the signal function (4.7.1.1).
Each signal is set to SIG_DFL at program start up.
• If the equivalent of signal (sig, SIG_DFL); is not executed prior to the call of a
signal handler, the blocking of the signal that is performed(4.7.1.1).
The equivalent of signal(sig, SIG_DFL) is executed prior to the call of a signal
handler unless the signal is SIGILL, SIGTRAP, or SIGPWR. See the signal
reference page for information on the support for the BSD 4.3 signal facilities.
• Whether the default handling is reset if the SIGILL signal is received by a
handler specified to the signal function (4.7.1.1).
No.
148 007–0701–150
C Language Reference Manual
• Whether space characters that are written out to a text stream immediately
before a newline character appear when read in (4.9.2).
All text characters (including spaces before a newline character) written out to a
text stream appear exactly as written when read back in.
• The number of null characters that can be appended to data written to a binary
stream (4.9.2).
The library never appends nulls to data written to a binary stream. Only the
characters written by the application are written to the output stream, whether
binary or text. Text and binary streams are identical: there is no distinction.
• Whether the file position indicator of an append mode stream is initially
positioned at the beginning or end of the file (4.9.2).
The file position indicator of an append stream is initially positioned at the end of
the file.
• Whether a write on a text stream causes the associated file to be truncated
beyond that point (4.9.3).
A write on a text stream does not cause the associated file to be truncated.
• The characteristics of file buffering (4.9.3).
Files are fully buffered, as described in paragraph 3, section 4.9.3, of ANSI
X3.159-1989.
• Whether a zero-length file actually exists (4.9.3).
Zero-length files exist, but have no data, so a read on such a file returns an
immediate EOF.
• The rules for composing valid file names (4.9.3).
Filenames consist of 1 to FILENAME_MAX characters. These characters can be
selected from the set of all character values excluding \0 (null) and the ASCII code
for / (slash).
It is generally unwise to use *, ?, [, or ]as part of filenames because of the special
meaning attached to these characters by the shell (see the sh(1) reference page).
Although permitted, the use of unprintable characters should be avoided.
• Whether the same file can be opened multiple times (4.9.3).
007–0701–150 149
A: Implementation-Defined Behavior
Temporary Files
• Whether a temporary file is removed if a program terminates abnormally
(4.9.4.3).
Temporary files are removed if a program terminates abnormally.
150 007–0701–150
C Language Reference Manual
007–0701–150 151
A: Implementation-Defined Behavior
152 007–0701–150
C Language Reference Manual
007–0701–150 153
A: Implementation-Defined Behavior
91: Error 91 (-o32 mode) 91: Restartable system call (-n32 and -64 modes)
154 007–0701–150
C Language Reference Manual
92: Error 92 (-o32 mode) 92: If pipe/FIFO, don’t sleep in stream head (-n32 and
-64 modes)
93: Error 93 (-o32 mode) 93: Directory not empty (-n32 and -64 modes)
94: Error 94 (-o32 mode) 94: Too many users (-n32 and -64 modes)
95: Error 95 (-o32 mode) 95: Socket operation on non-socket (-n32 and -64
modes)
96: Error 96 (-o32 mode) 96: Destination address required (-n32 and -64 modes)
97: Error 97 (-o32 mode) 97: Message too long (-n32 and -64 modes)
98: Error 98 (-o32 mode) 98: Protocol wrong type for socket (-n32 and -64
modes)
99: Error 99 (-o32 mode) 99: Option not supported by protocol (-n32 and -64
modes)
100: Error 100
101: Operation would block (-o32 mode) 101: Error 101 (-n32 and -64 modes)
102: Operation now in progress (-o32 mode) 102: Error 102 (-n32 and -64
modes)
103: Operation already in progress (-o32 mode) 103: Error 103 (-n32 and -64
modes)
104: Socket operation on non-socket (-o32 mode) 104: Error 104 (-n32 and -64
modes)
105: Destination address required (-o32 mode) 105: Error 105 (-n32 and -64
modes)
106: Message too long (-o32 mode) 106: Error 106 (-n32 and -64 modes)
107: Protocol wrong type for socket (-o32 mode) 107: Error 107 (-n32 and -64
modes)
108: Option not supported by protocol (-o32 mode) 108: Error 108 (-n32 and
-64 modes)
109: Protocol not supported (-o32 mode) 109: Error 109 (-n32 and -64 modes)
110: Socket type not supported (-o32 mode) 110: Error 110 (-n32 and -64 modes)
007–0701–150 155
A: Implementation-Defined Behavior
111: Operation not supported on socket (-o32 mode) 111: Error 111 (-n32 and
-64 modes)
112: Protocol family not supported (-o32 mode) 112: Error 112 (-n32 and -64
modes)
113: Address family not supported by protocol family (-o32 mode) 113: Error 113
(-n32 and -64 modes)
114: Address already in use (-o32 mode) 114: Error 114 (-n32 and -64 modes)
115: Can’t assign requested address (-o32 mode) 115: Error 115 (-n32 and -64
modes)
116: Network is down (-o32 mode) 116: Error 116 (-n32 and -64 modes)
117: Network is unreachable (-o32 mode) 117: Error 117 (-n32 and -64 modes)
118: Network dropped connection on reset (-o32 mode) 118: Error 118 (-n32 and
-64 modes)
119: Software caused connection abort (-o32 mode) 119: Error 119 (-n32 and -64
modes)
120: Connection reset by peer (-o32 mode) 120: Protocol not supported (-n32
and -64 modes)
121: No buffer space available (-o32 mode) 121: Socket type not supported (-n32
and -64 modes)
122: Socket is already connected (-o32 mode) 122: Operation not supported on
transport endpoint (-n32 and -64 modes)
123: Socket is not connected (-o32 mode) 123: Protocol family not supported
(-n32 and -64 modes)
124: Can’t send after socket shutdown (-o32 mode) 124: Address family not
supported by protocol family (-n32 and -64 modes)
125: Too many references: can’t splice (-o32 mode) 125: Address already in use
(-n32 and -64 modes)
126: Connection timed out (-o32 mode) 126: Cannot assign requested address
(-n32 and -64 modes)
156 007–0701–150
C Language Reference Manual
127: Connection refused (-o32 mode) 127: Network is down (-n32 and -64
modes)
128: Host is down (-o32 mode) 128: Network is unreachable (-n32 and -64
modes)
129: Host is unreachable (-o32 mode) 129: Network dropped connection because
of reset (-n32 and -64 modes)
130: Too many levels of symbolic links (-o32 mode) 130: Software caused
connection abort (-n32 and -64 modes)
131: Filename too long (-o32 mode) 131: Connection reset by peer (-n32 and -64
modes)
132: Directory not empty (-o32 mode) 132: No buffer space available (-n32 and
-64 modes)
133: Disk quota exceeded (-o32 mode) 133: Transport endpoint is already
connected (-n32 and -64 modes)
134: Stale NFS 14 file handle (-o32 mode) 134: Transport endpoint is not
®
007–0701–150 157
A: Implementation-Defined Behavior
Memory Allocation
• The behavior of the calloc, malloc, or realloc function if the size requested
is zero (4.10.3).
The malloc in libc.a returns a pointer to a zero-length space if a size of zero is
requested. Successive calls to malloc return different zero-length pointers. If the
library libmalloc.a is used, malloc returns 0 (the NULL pointer).
abort Function
• The behavior of the abort function with regard to open and temporary files
(4.10.4.1).
Open files are not flushed, but are closed. Temporary files are removed.
exit Function
• The status returned by the exit function if the value of the argument is other
than zero, EXIT_SUCCESS or EXIT_FAILURE (4.10.4.3).
The status returned to the environment is the least significant eight bits of the
value passed to exit.
getenv Function
• The set of environment names and the method for altering the environment list
used by the getenv function (4.10.4.4).
158 007–0701–150
C Language Reference Manual
Any string can be used as the name of an environment variable, and any string
can be used for its value. The function putenv alters the environment list of the
application. For example,
putenv(‘‘MYNAME=foo’’)
This sets the value of the environment variable MYNAME to “foo.” If the
environment variable MYNAME already existed, its value is changed. If it did not
exist, it is added. The string passed to putenv actually becomes part of the
environment, and changing it later alters the environment. Further, the string
should not be space that was automatically allocated (for example, an auto array);
rather, it should be space that is either global or malloced. For more information,
see the putenv(3c) reference page.
It is not wise to alter the value of well-known environment variables. For the
current list, see the environ(5) reference page.
system Function
• The contents and mode of execution of the string passed to the system function
(4.10.4.5).
The contents of the string should be a command string, as if typed to a normal
IRIX shell, such as sh(1). A shell (sh) is forked, and the string is passed to it.
The current process waits until the shell has completed and returns the exit status
of the shell as the return value.
strerror Function
• The contents of the error message strings returned by the strerror function
(4.11.6.2).
The string is exactly the same as the string output by perror, which is
documented in "errno and perror", page 150.
007–0701–150 159
A: Implementation-Defined Behavior
160 007–0701–150
C Language Reference Manual
or predefined macros with names that do not begin with an underscore. The
Standard’s description of each extension is followed by a definition of any SGI
support/nonsupport of each common extension.
Specialized Identifiers
• Characters other than the underscore _, letters, and digits, that are not defined in
the required source character set (such as dollar sign $, or characters in national
character sets) can appear in an identifier.
If the -dollar option is given to cc, then the dollar sign ($) is allowed in
identifiers.
007–0701–150 161
A: Implementation-Defined Behavior
162 007–0701–150
C Language Reference Manual
007–0701–150 163
A: Implementation-Defined Behavior
164 007–0701–150
C Language Reference Manual
007–0701–150 165
Appendix B
lint-style Comments
The following table lists the lint-style comments available with the SGI C compiler,
along with a short description. See the lint(1) reference page for more details.
The preprocessor automatically strips out comments. This prevents the lint-style
comments from being seen by the rest of the compiler and therefore these comments
will not work from within macros.
To work around this, turn off the offending message locally by using set woff
pragmas, as in this example:
#define MY_DEFS() \
\
int X; \
int Y; \
int Z;
void
func2(void)
{
X = 2;
Z = 4;
junk (X,X,Z);
}
In this example, 1174 refers only to the message for unreferenced variables. The
other link-style suppression will need different message numbers.
007–0701–150 167
B: lint-style Comments
168 007–0701–150
Appendix C
Built-in Functions
The following table lists the built-in functions available in the SGI C compiler, along
with a short description.
007–0701–150 169
C: Built-in Functions
170 007–0701–150
Index
007–0701–150 171
Index
172 007–0701–150
C Language Reference Manual
007–0701–150 173
Index
174 007–0701–150
C Language Reference Manual
007–0701–150 175
Index
176 007–0701–150
C Language Reference Manual
007–0701–150 177
Index
178 007–0701–150
C Language Reference Manual
007–0701–150 179
Index
180 007–0701–150
C Language Reference Manual
007–0701–150 181
Index
182 007–0701–150
C Language Reference Manual
007–0701–150 183
Index
for, 96 references, 54
goto, 97 Structure designators, 4
if, 94 Structures, 136
iteration, 95 alignment, 137
jump, 97 padding, 137
labeled, 99 structures
null, 93 initialization, 89
return, 98 Subroutines
selection, 94 See "Routines", 117
switch, 94 Subscripts
while, 96 in postfix expressions, 52
static Switch statements
function definitions, 101 maximum number of case values, 139
Static keyword, 70 switch statements, 94, 95
Static storage duration, 37, 70 labels, 99
stdarg, 4, 83 Switches, 2
stderr, 131 –ansi, 2
Storage class sizes, 39 –xansi, 2
storage class sizes, 39 Synchronization intrinsics, 109, 123
Storage class specifiers, 70 Synchronize operation
Storage duration, 70 __synchronize, 127
auto, 71 system function, 159
automatic, 38
static, 37, 70
strerror function, 159 T
String literals, 5, 28, 51, 162
wide, 28 Tabs, 23
wide characters, 90 Temporary files, 150, 158
Struct Text stream
namespace last line, 148
changes, 12 newline, 148
struct, 73 Text streams
initialization, 89 writes on, 149
members Thread
restrictions, 73 master, 117
Structs slave, 117
alignment, 137 Threads
Structure and processors, 120
declaration, 73 Time
indirect references, 54 availability, 140
members clock function, 160
restrictions, 73
184 007–0701–150
C Language Reference Manual
007–0701–150 185
Index
X
V
-xansi compiler option
valid filenames, 149 external names and, 21
Variable length array -Xlocal, 120
as specifier for type declarator, 69
Variable length arrays, 81
Variables Z
float, 14
void, 40, 83 Zero-length files, 149
conversions, 47
186 007–0701–150