home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 10 Tools
/
10-Tools.zip
/
VSCPPv4.zip
/
VACPP
/
IBMCPP
/
HELP
/
CPPLNG.INF
(
.txt
)
< prev
next >
Wrap
OS/2 Help File
|
1995-04-27
|
602KB
|
23,790 lines
ΓòÉΓòÉΓòÉ 1. How to Use the Online Language Reference ΓòÉΓòÉΓòÉ
The VisualAge C++ Online Language Reference is a language reference guide for C
and C++ programmers. It provides information about the VisualAge C++
implementation of both the C and C++ languages, including code examples, to
enable you to write C and C++ programs.
This document is a reference rather than a tutorial. It assumes you are already
familiar with C and C++ programming concepts.
With the exception of the separate introductory sections for each language and
the additional C++-specific topics (such as templates and exception handling),
information applies to both the C and C++ languages. Differences between the
two languages in the implementation of a construct or concept are indicated.
Before you begin to use this information, it would be helpful to understand how
to navigate through it. You can use the Table of Contents and Index facility to
locate topics and the Search facility to search the text of this document. You
can use hypertext links to acquire related information on the current topic.
Hypertext links appear in a different color (which you can customize using the
OS/2 Scheme Palette). For example, here is a link to another panel:
Communicating Your Comments to IBM. By double-clicking on the text of the link
or by pressing Enter on a highlighted link, you will open a panel of related
information. When you open a panel, the first link has the focus; to shift the
focus to other links, use the Tab key.
You should also understand:
How to Use the Contents
How to Get More Information on a Topic
How to Use Action Bar Choices
How to Cut and Paste Examples
ΓòÉΓòÉΓòÉ 1.1. How to Use the Contents ΓòÉΓòÉΓòÉ
When the Contents window first appears, some topics have a plus (+) sign beside
them. The plus sign indicates that additional topics are available.
To expand the Contents if you are using a mouse, click on the plus sign. If you
are using the keyboard, use the Up or Down Arrow key to highlight the topic,
and press the plus (+) key. For example, Preprocessor Directives has a plus
sign beside it. To see additional topics for that heading, click on the plus
sign or highlight that topic and press the plus (+) key.
To view a topic, double-click on the topic (or press the Up or Down Arrow key
to highlight the topic, and then press the Enter key).
ΓòÉΓòÉΓòÉ 1.2. How to Get More Information on a Topic ΓòÉΓòÉΓòÉ
After you select a topic, the information for that topic appears in a window.
Highlighted words or phrases indicate that additional information is available.
Words and phrases highlighted in a different color from the surrounding text
are called hypertext terms.
If you are using a mouse, double-click on the highlighted word. If you are
using a keyboard, press the Tab key to move to the highlighted word, and then
press the Enter key. Additional information then appears in a window.
ΓòÉΓòÉΓòÉ 1.3. How to Use Action Bar Choices ΓòÉΓòÉΓòÉ
Several choices are available for managing information presented in the
VisualAge C++ Online Language Reference. There are three pull-down menus on the
action bar: the Services menu, the Options menu, and the Help menu.
The actions that are selectable from the Services menu operate on the active
window currently displayed on the screen. These actions include the following:
Bookmark
You can set a placeholder so you can retrieve information of interest to
you.
Search
You can find occurrences of a word or phrase in the current topic, selected
topics, or all topics.
Print
You can print one or more topics. You can also print a set of topics by
first marking the topics in the Contents list.
Copy
You can copy a topic that you are viewing to the System Clipboard or to a
file that you can edit. This method is particularly useful for copying
syntax definitions and program samples into the application that you are
developing.
Using the actions that are selectable from the Options menu, you can change
the way your Contents list is displayed. To expand the Contents and show all
levels for all topics, choose Expand all from the Options pull-down. You can
also press the Ctrl, Shift, and * keys together.
The actions that are selectable from the Help menu allow you to select
different types of help information.
For information about any of the menu choices, highlight the choice in the
menu and press F1.
ΓòÉΓòÉΓòÉ <hidden> Placing Bookmarks ΓòÉΓòÉΓòÉ
When you place a bookmark on a topic, it is added to a list of bookmarks you
have previously set. You can view the list, and you can remove one or all
bookmarks from the list. If you have not set any bookmarks, the list is empty.
To set a bookmark, do the following:
1. Select a topic from the Contents.
2. When that topic appears, select the Bookmark option from the Services
menu.
3. If you want to change the name used for the bookmark, type the new name
in the field.
4. Click on the Place radio button (or press the Up or Down Arrow key to
select it).
5. Click on OK (or select it and press Enter). The bookmark is then added to
the bookmark list.
ΓòÉΓòÉΓòÉ <hidden> Searching for Information ΓòÉΓòÉΓòÉ
You can specify a word or phrase to be searched. You can also limit the search
to a set of topics by first marking the topics in the Contents list.
To search for a word or phrase in all topics, do the following:
1. Select the Search option from the Services menu.
2. Type the word or words to be searched for.
3. Click on All sections (or press the Up or Down Arrow keys to select it).
4. Click on Search (or select it and press Enter) to begin the search.
5. The list of topics where the word or phrase appears is displayed.
ΓòÉΓòÉΓòÉ <hidden> Printing Information ΓòÉΓòÉΓòÉ
You can print one or more topics, the index, or the table of contents. Make
sure that your printer is connected to the serial port, configured correctly,
and ready for input. To print:
1. Select Print from the Services pull-down.
2. Select what you want to print. Note that the This section and Marked
sections choices are only available if you are viewing a topic or if you
have marked topics, respectively. To mark topics in the table of
contents, press the Ctrl key and click on the topics, or use the arrow
keys.
3. Select Print to print what you've chosen on your printer.
ΓòÉΓòÉΓòÉ <hidden> Copying Information to a File ΓòÉΓòÉΓòÉ
You can copy a topic that you are viewing in two ways:
Copy copies the topic that you are viewing into the System Clipboard. If
you are using a Presentation Manager (PM) editor (for example, the
Enhanced Editor) that copies or cuts (or both) to the System Clipboard,
and pastes to the System Clipboard, you can easily add the copied
information to your program source module.
Copy to file copies the topic that you are viewing into a temporary file
named TEXT.TMP. You can later edit that file by using any editor.
TEXT.TMP is placed in the directory where your viewable document resides.
To copy a topic, do the following:
1. Expand the Contents list and select a topic.
2. When the topic appears, select Copy to file from the Services menu.
3. The system puts the text pertaining to that topic into the temporary file
TEXT.TMP.
ΓòÉΓòÉΓòÉ 1.4. How to Cut and Paste Examples ΓòÉΓòÉΓòÉ
You can copy examples (or information) from this reference/guide/book to
compile, link, and run them, or to paste them into your own code.
To copy an example or information:
1. Make the topic you want to copy the active window.
2. From the Services menu, select Copy to file. The text in that topic is
placed in the temporary file TEXT.TMP, in the same directory as this
reference.
3. You can then modify or use TEXT.TMP as you want.
Note: Because the system copies the entire contents of the topic to the file,
you may need to edit it to remove additional text. Most examples in this
reference are ready to compile, link, and run as they appear, and do not
require any editing.
ΓòÉΓòÉΓòÉ 1.5. Other Information You Might Find Helpful ΓòÉΓòÉΓòÉ
This product provides a number of online guides and references that we hope
you'll find helpful as you develop applications. This information includes
User's Guides, References, and How Do I help that gives you specific
instructions for performing common tasks. You can get to this online
information from the Information folder inside the main product folder. You can
also get to it from the Help menu in any of the components of the product. You
can get help in four ways:
Inside VisualAge C++
From the Command Line
For a Keyword or Construct
BookManager Books
For a list of VisualAge C++ documents that are available online, see Online
Documents Available in VisualAge C++.
ΓòÉΓòÉΓòÉ <hidden> Getting Help Inside VisualAge C++ ΓòÉΓòÉΓòÉ
Three kinds of help are available directly within the VisualAge C++ interface:
To get general contextual help for the component of VisualAge C++ that
you are using, press F1 anywhere in the window.
To get contextual help on a particular menu, menu item, or button,
highlight the element and press F1.
To get access to all of the help information that is available to you in
a particular window, click on Help in the menu bar at the top of the
window.
The Help menu includes the following selections:
Help Index An alphabetical list of all of the help topics that are available
from this window
General Help Overall help for the window
Using Help General information about the help facility
How Do I... The How Do I help for the component
Product Information A dialog that shows the level of VisualAge C++ being used
In addition, there are selections that let you open all of online documents
that are available in VisualAge C++.
To get detailed information, open the Information folder in the VisualAge C++
folder. In this folder you will find icons for a variety of online documents
that describe, in detail, the different aspects of VisualAge C++. To open a
particular online document, double click on its icon.
ΓòÉΓòÉΓòÉ <hidden> Getting Help from the Command Line ΓòÉΓòÉΓòÉ
You can look at the online documents by issuing the view command. The
installation routine stores the online document files in the \IBMCPP\HELP
directory. To view the Language Reference, for example, make C:\IBMCPP\HELP
your current directory (substituting the drive where you installed VisualAge
C++ for C:) and enter the following command:
VIEW CPPLNG.INF
If you want to get information on a specific topic, you can specify a word or a
series of words after the file name. If the words appear in an entry in the
table of contents or the index, the online document is opened to the associated
section. For example, if you want to read the section on operator precedence in
the Language Reference, you can enter the following command:
VIEW CPPLNG.INF OPERATOR PRECEDENCE
ΓòÉΓòÉΓòÉ <hidden> Getting Help for a Keyword or Construct ΓòÉΓòÉΓòÉ
If you are editing a file using Editor, you can get help for a keyword or
construct by highlighting the word and pressing F1. In the other tools, you can
get help for a keyword or construct by highlighting the word and pressing
Ctrl-H.
ΓòÉΓòÉΓòÉ <hidden> BookManager Books ΓòÉΓòÉΓòÉ
The online documents for VisualAge C++ are also available in BookManager format
in the CD-ROM version of VisualAge C++. You can read this information using
either the IBM Library Reader/2 or IBM Library Reader/DOS. For details on
installing and using the IBM Library Reader and BookManager books, see the
README.ENG file in the root directory of the CD-ROM.
ΓòÉΓòÉΓòÉ 1.6. Online Documents Available in VisualAge C++ ΓòÉΓòÉΓòÉ
The following documents are available in standard format (.INF files):
Building VisualAge C++ Multimedia Subsystem SOM Programming Reference
Parts for Fun and Profit Programming Guide
C Library Reference Open Class Library Ref- User's Guide and Refer-
erence ence
Control Program Guide and Open Class Library Visual Builder User's
Reference User's Guide Guide
Graphics Programming OS/2 Bidirectional Lan- Visual Builder Parts Ref-
Guide and Reference guage Support Develop- erence
ment Guide
IPF Guide and Reference OS/2 Tools Reference Editor Command Reference
Kernel Debug Reference Presentation Manager Welcome to VisualAge C++
Guide and Reference
Language Reference Programming Guide Workplace Shell Program-
ming Guide
Multimedia Application REXX Reference Workplace Shell Program-
Programming Guide ming Reference
Multimedia Programming SOM Programming Guide
Reference
ΓòÉΓòÉΓòÉ 1.7. Communicating Your Comments to IBM ΓòÉΓòÉΓòÉ
If there is something you like, or dislike, about this book, please let us
know. You can use one of the methods listed below to send your comments to IBM.
Please be sure to include the complete title of the publication that you are
commenting on.
The comments you send should only pertain to the information in this document
and its presentation. To request additional publications or to ask questions or
make comments about the functions of IBM products or systems, you should talk
to your IBM representative or your authorized IBM remarketer.
When you send comments to IBM, you grant IBM a nonexclusive right to use or
distribute your comments in any way it believes appropriate without incurring
any obligation to you.
You can send your comments to IBM in the following ways:
By mail to the following address:
IBM Canada Ltd. Laboratory
Information Development
2G/345/1150/TOR
1150 EGLINTON AVENUE EAST
NORTH YORK, ONTARIO
CANADA M3C 1H7
By FAX to the following number:
- United States and Canada: (416) 448-6161
- Other countries (+1) 416-448-6161
By electronic mail to one of the following IDs. Be sure to include your
entire network address if you wish to get a reply.
- Internet: torrcf@vnet.ibm.com
- IBMLink: toribm(torrcf)
- IBM/PROFS: torolab4(torrcf)
- IBMMAIL: ibmmail(caibmwt9)
ΓòÉΓòÉΓòÉ <hidden> Related Information ΓòÉΓòÉΓòÉ
Copyright
Edition Notice
Notices
Trademarks and Service Marks
ΓòÉΓòÉΓòÉ 1.8. Copyright ΓòÉΓòÉΓòÉ
Copyright International Business Machines Corporation, 1995. All rights
reserved.
Note to U.S. Government Users - Documentation related to restricted rights -
Use, duplication, or disclosure is subject to restrictions set forth in GSA ADP
Schedule Contract with IBM Corp.
ΓòÉΓòÉΓòÉ 1.9. Edition Notice ΓòÉΓòÉΓòÉ
First Edition, May 1995.
This edition applies to Version 3.0 of IBM VisualAge C ++ for OS/2 (30H1664,
30H1665, 30H1666) and to all subsequent releases and modifications until
otherwise indicated in new editions. Make sure you are using the correct
edition for the level of the product.
This publication could include technical inaccuracies or typographical errors.
Changes are periodically made to the information herein; any such changes will
be reported in subsequent revisions.
Requests for publications and for technical information about IBM products
should be made to your IBM Authorized Dealer or your IBM Marketing
Representative.
When you send information to IBM, you grant IBM a nonexclusive right to use or
distribute the information in any ways it believes appropriate without
incurring any obligation to you.
ΓòÉΓòÉΓòÉ 1.10. Notices ΓòÉΓòÉΓòÉ
Any reference to an IBM licensed program in this publication is not intended to
state or imply that only IBM's licensed program may be used. Any functionally
equivalent product, program, or service that does not infringe any of IBM's
intellectual property rights may be used instead of the IBM product, program,
or service. Evaluation and verification of operation in conjunction with other
products, except those expressly designated by IBM, is the user's
responsibility.
IBM may have patents or pending patent applications covering subject matter in
this document. The furnishing of this document does not give you any license to
these patents. You can send license inquiries, in writing, to the IBM Director
of Licensing, IBM Corporation, 500 Columbus Avenue, Thornwood, NY, 10594, USA.
This publication contains examples of data and reports used in daily business
operations. To illustrate them as completely as possible, the examples include
the names of individuals, companies, brands, and products. All of these names
are fictitious and any similarity to the names and addresses used by an actual
business enterprise is entirely coincidental.
ΓòÉΓòÉΓòÉ 1.11. Trademarks and Service Marks ΓòÉΓòÉΓòÉ
The following terms are trademarks of IBM Corporation in the United States or
other countries or both:
BookManager
C/2
C Set/2
C Set ++
Common User Access
CUA
IBM
IBMLink
Library Reader
Operating System/2
OS/2
Personal System/2
Presentation Manager
PS/2
VisualAge
WorkFrame
Other company, product, and service names, which may be denoted by a double
asterisk(**), may be trademarks or service marks of others.
ΓòÉΓòÉΓòÉ <hidden> signed/unsigned types ΓòÉΓòÉΓòÉ
The signed and unsigned types can be used with either a character (char) or an
integer (int, short, long). The unsigned prefix indicates that the value of the
object is to be a nonnegative value.
For more information on conversions between signed and unsigned types, see
Implicit Type Conversions.
ΓòÉΓòÉΓòÉ <hidden> \ Character ΓòÉΓòÉΓòÉ
The backslash can appear in string literals or comments as a punctuator. A
backslash followed by a single character in C code indicates an escape
sequence. A backslash by itself at the end of a line of C code is a
continuation character.
Escape Sequences provides help on escape sequences and the continuation
character.
ΓòÉΓòÉΓòÉ <hidden> ( ) ΓòÉΓòÉΓòÉ
Parentheses are used to group expressions to force a particular order of
evaluation. A parenthesized expression usually contains one or more operators
and operands (variables or constants), and is often part of an assignment
expression. For example, ((x + y) * g) is a parenthesized expression.
Parentheses are also used in function calls to group the argument list for the
function. The opening parenthesis appears directly after the function name, and
may be followed by any number of arguments (or no arguments), followed by the
closing parenthesis and a semicolon. For example, fgets(line, 100, stream); is
a function call.
For help on parenthesized expressions, see Parenthesized Expressions ( ).
For help on function calls, see Function Calls ( ).
ΓòÉΓòÉΓòÉ <hidden> + ΓòÉΓòÉΓòÉ
The plus (+) sign can be used as a unary operator to maintain the value of an
operand, for example, +quality. The unary plus sign is the opposite of the
unary minus (-) sign, which negates the value of an operand, for example,
-quality.
The plus sign can also be used as a binary operator in an arithmetic expression
to represent the addition operation. For example, x + y indicates that the
variables x and y are to be added together.
For more information on the unary plus operator, see Unary Plus +.
For more information on the addition operator, see Addition +.
ΓòÉΓòÉΓòÉ <hidden> - ΓòÉΓòÉΓòÉ
The minus (-) sign can be used as a unary operator to negate the value of an
operand, for example -quality.
It can also be used as a binary operator in an arithmetic expression to
represent the subtraction operation. For example, x - y indicates that the
variable y is to be subtracted from the variable x.
For more information on the unary minus operator, see Unary Minus -.
For more information on the subtraction operator, see Subtraction -.
ΓòÉΓòÉΓòÉ <hidden> & ΓòÉΓòÉΓòÉ
The ampersand (&) sign can be used as a unary operator to indicate the address
of its operand. For example, &anyvar represents the address of the variable
anyvar.
The ampersand can also be used as a binary operator to represent the bitwise
AND operation. For example, in the expression x & y, the corresponding bits of
the x and y values are compared to see if the bits are both 1.
A double ampersand (&&) is the logical AND operator. which indicate For
example, in the expression x && y, the values of x and y are checked to see if
both are nonzero values.
For more information on the address operator, see Address &.
For more information on the bitwise AND operator, see Bitwise AND &.
For more information on the logical AND operator, see Logical AND &&.
ΓòÉΓòÉΓòÉ <hidden> * ΓòÉΓòÉΓòÉ
The asterisk (*) can be used as a unary operator to indicate indirection. For
example, int *anyvar; declares anyvar as a pointer to int, that points to the
value of *anyvar.
The asterisk can also be used as a binary operator in an arithmetic expression
to represent the multiplication operation. For example, x * y indicates that
the variable x is to be multiplied by y.
For more information on the indirection operator, see Indirection *.
For more information on the multiplication operator, see Multiplication *.
ΓòÉΓòÉΓòÉ <hidden> % ΓòÉΓòÉΓòÉ
The percent (%) sign can be used simply to indicate percentage in a string
literal. For example, "More than 50% agree".
The percent sign is also used as part of the format specifier for the printf
and scanf functions. For example, in the statement printf("The value is %d",
anyint); the integer value of anyint is printed in the place of the %d
specifier.
The percent sign can also be used as a binary operator to represent the modulus
(or remainder) operation. For example, the expression x % y indicates that x is
divided by y, and the result of the expression is the remainder of the
division.
For more information on string literals, see String Literals.
For more information on format specifiers, see the printf function or the scanf
function in the IBM VisualAge C++ for OS/2 C Library Reference.
For more information on the remainder operator, see Remainder %.
ΓòÉΓòÉΓòÉ <hidden> : ΓòÉΓòÉΓòÉ
The colon (:) is used to indicate the end of a label and separate it from the
following statement. For example, in the expression anylabl: x = y;, anylabl is
a label that could be part of a switch statement or the target of a goto
statement.
The colon is also used in bit-field declarations to separate the identifier of
the bit field and the storage it is given. For example, in the structure
struct {
unsigned x : 4
unsigned y : 1
}
the bit fields x and y are assigned 4 bits and 1 bit of storage, respectively.
The colon can also be used as part of the compound conditional expression (?:)
to separate the two action expressions. For example, in the expression x = (y <
z) ? y : z;, if y is less than z, the value of y is assigned to x; if y is not
less than z, the value of z is assigned to x.
For more information on labels, see Labels.
For more information on bit fields, see the section Declaring and Using Bit
Fields under Structures.
For more information on the compound conditional expression, see Conditional
Expressions.
ΓòÉΓòÉΓòÉ <hidden> ? ΓòÉΓòÉΓòÉ
The question mark (?) is used for both trigraphs (three characters starting
with ??) and as the first part of the operator for the conditional expression.
For more information on the compound conditional expression, see Conditional
Expressions.
ΓòÉΓòÉΓòÉ <hidden> , ΓòÉΓòÉΓòÉ
The comma (,) can be used to separate items such as parameters in a function
call.
The comma is also used in the comma expression to separate two operands. For
example, in the expression x = (y++, z * 4);, the left operand is evaluated
then discarded, and the value of the right operand is assigned to x.
For more information on the comma expression, see Comma Expression ,.
ΓòÉΓòÉΓòÉ <hidden> ; ΓòÉΓòÉΓòÉ
The semicolon (;) is used to indicate the end of a C expression, for example,
int x = 4;.
The semicolon can also be used by itself as a null statement to show a
nonexistent action.
For more information on expressions, see Expression.
For more information on null statements, see Null Statement.
ΓòÉΓòÉΓòÉ 2. Introduction to the C and C++ Languages ΓòÉΓòÉΓòÉ
This chapter describes the C and C++ programming languages implemented by
VisualAge C++ and shows you how to structure C and C++ source programs. It also
briefly summarizes the differences between C and C++, and discusses the
principles of object-oriented programming.
This section discusses:
Overview of the C Language
C Source Programs
C Source Files
Program Execution
Scope in C
Program Linkage
Storage Duration
Name Spaces
Related Information
Overview of the C++ Language
ΓòÉΓòÉΓòÉ 2.1. Overview of the C Language ΓòÉΓòÉΓòÉ
C is a programming language designed for a wide variety of programming tasks.
It is used for system-level code, text processing, graphics, and in many other
application areas.
C supports several data types, including characters, integers, floating-point
numbers and pointers - each in a variety of forms. In addition, C also supports
arrays, structures (records), unions, and enumerations.
The C language contains a concise set of statements, with functionality added
through its library. This division enables C to be both flexible and efficient.
An additional benefit is that the language is highly consistent across
different systems.
The C library contains functions for input and output, mathematics, exception
handling, string and character manipulation, dynamic memory management, as well
as date and time manipulation. Use of this library helps to maintain program
portability, because the underlying implementation details for the various
operations need not concern the programmer.
Related Information
Lexical Elements of C and C++
Functions
Type Specifiers
Overview of the C++ Language
ΓòÉΓòÉΓòÉ 2.2. C Source Programs ΓòÉΓòÉΓòÉ
A C source program is a collection of one or more directives, declarations, and
statements contained in one or more source files.
Statements Specify the action to be performed.
Directives Instruct the preprocessor to act on the text of the program.
Pragma directives affect compiler behavior.
Declarations Establish names and define characteristics such as scope, data
type and linkage.
Definitions Are declarations that allocate storage for data objects or
define a body for functions. An object definition allocates
storage and may optionally initialize the object.
A function declaration precedes the function body. The function body is a
compound statement that can contain declarations and statements that define
what the function does. The function declaration declares the function name,
its parameters, and the data type of the value it returns.
A program must contain one, and only one, function called main. The main
function is the first function called when a program is run.
C++ Note: This is not the case for C++ programs. If a C++ program
instantiates an object in file scope, the constructor for that object is
executed first.
By convention, main is the starting point for running a program. It can call
other functions. A program usually stops running at
the end of the main function
a return statement in the main function
an exit function call.
Source for a Simple C Program
Related Information
C Source Files
Overview of the C Language
C++ Programs
ΓòÉΓòÉΓòÉ <hidden> Source for a Simple C Program ΓòÉΓòÉΓòÉ
/************************************************************************
*
This is the source code of a simple C program:
*
************************************************************************/
#include <stdio.h> /* standard I/O library header that
contains macros and function declarations
such as printf used below */
#include <math.h> /* standard math library header that
contains macros and function declarations
such as cos used below */
#define NUM 46.0 /* Preprocessor directive */
double x = 45.0; /* External variable definitions */
double y = NUM;
int main(void) /* Function definition
for main function */
{
double z; /* Local variable definitions */
double w;
z = cos(x); /* cos is declared in math.h as
double cos(double arg) */
w = cos(y);
printf ("cosine of x is %f\n", z); /* Print cosine of x */
printf ("cosine of y is %f\n", w); /* Print cosine of y */
return 0;
}
/************************************************************************
*
This source program defines main and declares a reference to the function cos.
The program defines the global variables x and y, initializes them, and
declares two local variables z and w.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 2.3. C Source Files ΓòÉΓòÉΓòÉ
A C source file is a text file that contains all or part of a C source program.
It can include any of the functions that the program needs. To create an
executable module, you compile the separate source files individually and then
link them as one program. With the #include directive, you can combine source
files into larger source files.
A source file contains any combination of directives, declarations, and
definitions. You can split items such as function definitions and large data
structures between text files, but you cannot split them between compiled
files. Before the source file is compiled, the preprocessor alters the source
file in a predictable way. The preprocessor directives determine what changes
are made to the source text. As a result of the preprocessing stage,
preprocessor directives are completed, macros are expanded, and a source file
is created containing C statements, completed directives, declarations, and
definitions.
It is sometimes useful to gather variable definitions into one source file and
declare references to those variables in any source files that use them. This
procedure makes definitions easy to find and change, if necessary. You can also
organize constants and macros into separate files and include them into source
files as required.
Directives in a source file apply to that source file and its included files
only. Each directive applies only to the part of the file following the
directive.
Example of C Source Files
Related Information
C Source Programs
Overview of the C Language
Declarations
Statements
Functions
Preprocessor Directives
C++ Programs
ΓòÉΓòÉΓòÉ <hidden> Example of C Source Files ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example is a C program in two source files. The main and max
functions are in separate files. The program starts running with the main
function.
Source file 1
*
************************************************************************/
/************************************************************************
* Source file 1 - main function *
************************************************************************/
#define ONE 1
#define TWO 2
#define THREE 3
extern int max(int, int); /* Function declaration */
int main(int argc, char * argv[]) /* Function definition */
{
int u, w, x, y, z;
u = 5;
z = 2;
w = max(u, ONE);
x = max(w,TWO);
y = max(x,THREE);
z = max(y,z);
return 0;
}
/************************************************************************
*
Source file 2
*
************************************************************************/
/************************************************************************
* Source file 2 - max function *
************************************************************************/
int max (int a,int b) /* Function definition */
{
if ( a > b )
return (a);
else
return (b);
}
/************************************************************************
*
The first source file declares the function max, but does not define it. This
is an external declaration, a declaration of a function defined in source file
2. Four statements in main are function calls of max.
The lines beginning with a number sign (#) are preprocessor directives that
direct the preprocessor to replace the identifiers ONE, TWO, and THREE with the
digits 1, 2, and 3. The directives do not apply to the second source file.
The second source file contains the function definition for max, which is
called four times in main. After you compile the source files, you can link and
run them as a single program.
/************************************************************************
*
ΓòÉΓòÉΓòÉ 2.4. Program Execution ΓòÉΓòÉΓòÉ
Every program must have a function called main and usually contains other
functions.
The main function is the starting point for running a program. The statements
within the main function are executed sequentially. There may be calls to other
functions. A program usually stops running at the end of the main function,
although it can stop at other points in the program.
You can make your program more modular by creating separate functions to
perform a specific task or set of tasks. The main function calls these
functions to perform the tasks. Whenever a function call is made, the
statements are executed sequentially starting with the first statement in the
function. The function returns control to the calling function at the return
statement or at the end of the function.
You can declare any function to have parameters. When functions are called,
they receive values for their parameters from the arguments passed by the
calling functions. You can declare parameters for the main function so you can
pass values to main from the command line. The command function that starts the
program can pass such values as described in The main() Function.
Related Information
Functions
The main() Function
Calling Functions and Passing Arguments
C Source Files
Overview of the C Language
C++ Programs
ΓòÉΓòÉΓòÉ 2.5. Scope in C ΓòÉΓòÉΓòÉ
An identifier becomes visible with its declaration. The region where an
identifier is visible is referred to as the identifier's scope. The four kinds
of scope are:
Block
Function
File
Function prototype
The scope of an identifier is determined by where the identifier is declared.
See Identifiers for more information on identifiers.
In the following example, the variable x, which is defined on line 1, is
different from the x defined on line 2. The variable defined on line 2 has
function prototype scope and is visible only up to the closing parenthesis of
the prototype declaration. Visibility of the variable x defined on line 2
resumes after the end of the prototype declaration.
1 int x = 4; /* variable x defined with file scope */
2 long myfunc(int x, long y); /* variable x has function */
3 /* prototype scope */
4 int main(void)
5 {
6 /* . . . */
7 }
Functions with static storage class are visible only in the source file they
are defined in. All other functions can be globally visible.
Example of Scope
Related Information
Block
Labels
goto
Functions
static Storage Class Specifier
C Source Files
Scope in C++
ΓòÉΓòÉΓòÉ <hidden> Block Scope ΓòÉΓòÉΓòÉ
The identifier's declaration is located inside a statement block. A block
starts with an opening brace ({) and ends with a closing brace (}). An
identifier with block scope is visible from the point where it is declared to
the closing brace that ends the block. Block scope is sometimes referred to as
local scope.
You can nest block visibility. A block nested inside a block can contain
declarations that redeclare variables declared in the outer block. The new
declaration of the variable applies to the inner block. The original
declaration is restored when program control returns to the outer block. A
variable from the outer block is visible inside inner blocks that do not
redefine the variable.
ΓòÉΓòÉΓòÉ <hidden> Function Scope ΓòÉΓòÉΓòÉ
The only type of identifier with function scope is a label name. A label is
implicitly declared by its appearance in the program text and is visible
throughout the function that declares it.
ΓòÉΓòÉΓòÉ <hidden> File Scope ΓòÉΓòÉΓòÉ
The identifier's declaration appears outside of any block. It is visible from
the point where it is declared to the end of the source file. If source files
are included by #include preprocessor directives, those files are considered to
be part of the source and the identifier will be visible to all included files
that appear after the declaration of the identifier. The identifier can be
declared again as a block scope variable. The new declaration replaces the
file-scope declaration until the end of the block.
ΓòÉΓòÉΓòÉ <hidden> Function Prototype Scope ΓòÉΓòÉΓòÉ
The identifier's declaration appears within the list of parameters in a
function prototype. It is visible from the point where it is declared to the
closing parenthesis of the prototype declaration.
ΓòÉΓòÉΓòÉ <hidden> Example of Scope ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program illustrates blocks, nesting, and scope. The example shows
two kinds of scope: file and block. The main function prints the values 1, 2,
3, 0, 3, 2, 1 on separate lines. Each instance of i represents a different
variable.
*
************************************************************************/
#include <stdio.h>
int i = 1; /* i defined at file scope */
int main(int argc, char * argv[])
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ {
Γöé
Γöé printf("%d\n", i); /* Prints 1 */
Γöé
Γöé ΓöîΓöÇΓöÇΓöÇΓöÇ {
Γöé Γöé int i = 2, j = 3; /* i and j defined at
Γöé Γöé block scope */
Γöé Γöé printf("%d\n%d\n", i, j); /* Prints 2, 3 */
Γöé Γöé
Γöé Γöé ΓöîΓöÇΓöÇ {
Γöé Γöé Γöé int i = 0; /* i is redefined in a nested block */
Γöé Γöé Γöé /* previous definitions of i are hidden */
Γöé Γöé Γöé printf("%d\n%d\n", i, j); /* Prints 0, 3 */
Γöé Γöé ΓööΓöÇΓöÇ }
Γöé Γöé
Γöé Γöé printf("%d\n", i); /* Prints 2 */
Γöé Γöé
Γöé ΓööΓöÇΓöÇΓöÇΓöÇ }
Γöé
Γöé printf("%d\n", i); /* Prints 1 */
Γöé
Γöé return 0;
Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ }
ΓòÉΓòÉΓòÉ 2.6. Program Linkage ΓòÉΓòÉΓòÉ
The association, or lack of association, between two identical identifiers is
known as linkage. The kind of linkage that an identifier has depends on the way
that it is declared.
A file scope identifier has one of the following kinds of linkage:
Internal Identical identifiers within a single source file refer to the
same data object or function.
External Identical identifiers in separately compiled files refer to
the same data object or function.
No linkage Each identical identifier refers to a unique object.
Note: Program linkage is not the same as a function calling convention, which
is also commonly referred to as linkage. While it is related to program
linkage, a calling convention concerns itself with linkage specifications and
the use of certain keywords. This section discusses only program linkage.
Function calling conventions are described in the IBM VisualAge C++ for OS/2
User's Guide and Reference.
C++ Notes:
Linkage specifications are used to link to non-C++ declarations.
During compilation, the compiler encodes all function names and certain
other identifiers to include type and scope information. This encoding
process is called mangling, and the mangled names are used in the object
files and final executable file. Tools that use these files must use the
mangled names and not the original names used in the source code.
VisualAge C++ provides two methods of converting mangled names to the
original source code names, demangling functions and the CPPFILT utility.
The demangling functions are described in the appendix on Mapping in the
IBM VisualAge C++ for OS/2 User's Guide and Reference and in the
<demangle.h> header file. The CPPFILT utility is described in the online
VisualAge C++ Compiler Utilities Reference.
Related Information
Internal Linkage
External Linkage
No Linkage
Scope in C
Declarations
ΓòÉΓòÉΓòÉ <hidden> Internal Linkage ΓòÉΓòÉΓòÉ
The following kinds of identifiers have internal linkage:
All identifiers with file or block scope that have the keyword static in
their declarations. Functions with static storage class are visible only
in the source file in which you define them.
Functions qualified with _Inline and C++ inline functions.
C++ identifiers declared at file scope with the specifier const and not
explicitly declared extern. In C, const objects have external linkage by
default
A variable that has static storage class can be defined within a block or
outside a function. If the definition occurs within a block, the variable has
internal linkage and is only visible within the block after its declaration is
seen. If the definition occurs outside a function, the variable has internal
linkage and is available from the point where it is defined to the end of the
current source file.
A class that has no static members or noninline member functions, and that has
not been used in the declaration of an object or function or class is local to
its translation unit.
If the declaration of an identifier has the keyword extern and if a previous
declaration of the identifier is visible at file scope, the identifier has the
same linkage as the first declaration.
ΓòÉΓòÉΓòÉ <hidden> External Linkage ΓòÉΓòÉΓòÉ
The following kinds of identifiers have external linkage:
Identifiers with file or block scope that have the keyword extern in
their declarations.
If a previous declaration of the identifier is visible at file scope, the
identifier has the same linkage as the first declaration. For example, a
variable or function that is first declared with the keyword static and
later declared with the keyword extern has internal linkage.
Function identifiers declared without storage-class specifiers.
Object identifiers that have file scope declarations without a
storage-class specified. Storage is allocated for such object
identifiers.
Static class members and noninline member functions.
Identifiers declared with the keyword extern can be defined in other
translation units.
Related Information
Program Linkage
Internal Linkage
No Linkage
Storage Class Specifiers
ΓòÉΓòÉΓòÉ <hidden> No Linkage ΓòÉΓòÉΓòÉ
The following kinds of identifiers have no linkage:
Identifiers that do not represent an object or a function, including
labels, enumerators, typedef names, type names, and template names
Identifiers that represent a function argument
Identifiers declared inside a block without the keyword extern
Related Information
Program Linkage
Internal Linkage
External Linkage
extern Storage Class Specifier
Identifiers
ΓòÉΓòÉΓòÉ 2.7. Storage Duration ΓòÉΓòÉΓòÉ
Storage duration determines how long storage for an object exists. An object
has either static storage duration or automatic storage class depending on its
declaration.
Static storage Is allocated at initialization and remains available until
the program ends. Objects have static storage duration if
they:
Have file scope
Have external or internal linkage
OR
Contain the static storage class specifier.
Automatic storage Is allocated and removed according to the scope of the
identifier. Objects have automatic storage duration if
they are:
Parameters in a function definition.
Declared at block scope and do not have any storage
class specifier.
OR
Declared at block scope and have the register or auto
storage class specifier.
For example, storage for an object declared at block scope
is allocated when the identifier is declared and removed
when the closing brace (}) is reached.
Note: Objects can also have heap storage duration. Heap objects are declared
at runtime by calling a function such as malloc().
Related Information
Storage Class Specifiers
Scope in C
Program Linkage
ΓòÉΓòÉΓòÉ 2.8. Name Spaces ΓòÉΓòÉΓòÉ
The compiler sets up name spaces to distinguish among identifiers referring to
different kinds of entities. Identical identifiers in different name spaces do
not interfere with each other, even if they are in the same scope.
You must assign unique names within each name space to avoid conflict. The same
identifier can be used to declare different objects as long as each identifier
is unique within its name space. The syntactic context of an identifier within
a program lets the compiler resolve its name space without ambiguity.
Identifiers in the same name space can be redefined within enclosed program
blocks as described in Scope in C.
Within each of the following four name spaces, the identifiers must be unique.
Tags of these types must be unique within a single scope:
- Enumerations
- Structures and unions
Members of structures, unions, and classes must be unique within a single
structure, union or class type.
Statement labels have function scope and must be unique within a
function.
All other ordinary identifiers must be unique within a single scope:
- Function names
- Variable names
- Names of function parameters
- Enumeration constants
- typedef names.
Structure tags, structure members, variable names, and statement labels are in
four different name spaces; no conflict occurs among the four items named
student in the following example:
int get_item()
{
struct student /* structure tag */
{
char student[20]; /* structure member */
int section;
int id;
} student; /* structure variable */
goto student;
student: ; /* null statement label */
return (0);
}
Each occurrence of student is interpreted by its context in the program. For
example, when student appears after the keyword struct, it is a structure tag.
When student appears after either of the member selection operators . or ->,
the name refers to the structure member. When student appears after the goto
statement, control is passed to the null statement label. In other contexts,
the identifier student refers to the structure variable.
Related Information
Scope in C
Identifiers
Type Specifiers
Expressions and Operators
ΓòÉΓòÉΓòÉ 2.9. Overview of the C++ Language ΓòÉΓòÉΓòÉ
C++ is an object-oriented language based on the C programming language. It can
be viewed as a superset of C. Almost all of the features and constructs
available in C are also available in C++. However, C++ is more than just an
extension of C. Its additional features support the programming style known as
object-oriented programming. Several features that are already available in C,
such as input and output may be implemented differently in C++. In C++ you may
use the conventional C input and output routines or you may use object oriented
input and output by using the I/O Stream class library.
C++ was developed by Bjarne Stroustrup of AT&T Bell Laboratories. It was
originally based on the definition of the C language stated in The C
Programming Language by Brian W. Kernighan and Dennis M. Ritchie. This C
language definition is commonly called K&R C. Since then, the International
Standards Organization C language definition (referred to here as ISO/ANSI C)
has been approved. It specifies many of the features that K&R left unspecified.
Some features of ISO/ANSI C have been incorporated into the current definition
of C++, and some parts of the ISO/ANSI C definition have been motivated by C++.
While there is currently no C++ standard comparable to the ISO/ANSI C
definition, an ISO committee is working on such a definition. The draft of the
Working Paper for Draft Proposed American National Standard for Information
Systems - Programming Language C++, X3J16/92-0091, is the base document for the
ongoing standardization of C++. The VisualAge C++ compiler adheres to the
version of the ISO/ANSI working paper dated September 17, 1992.
ΓòÉΓòÉΓòÉ 2.10. C++ Support for Object-Oriented Programming ΓòÉΓòÉΓòÉ
Object-oriented programming is based on the concepts of data abstraction,
inheritance, and polymorphism. Unlike procedural programming, it concentrates
on the data objects that are involved in a problem and how they are
manipulated, not on how something is accomplished. Based on the foundation of
data abstraction, object-oriented programming allows you to reuse existing code
more efficiently and increase your productivity.
Data Abstraction
Encapsulation
Inheritance
Dynamic Binding and Polymorphism
Other Features of C++
Related Information
C++ Classes
Member Access
Inheritance Overview
Derivation
Overview of the C++ Language
ΓòÉΓòÉΓòÉ <hidden> Data Abstraction ΓòÉΓòÉΓòÉ
Data abstraction provides the foundation for object-oriented programming. In
addition to providing fundamental data types, object-oriented programming
languages allow you to define your own data types, called user-defined or
abstract data types. In the C programming language, related data items can be
organized into structures. These structures can then be manipulated as units of
data. In addition to providing this type of data structure, object-oriented
programming languages allow you to implement a set of operations that can be
applied to the data elements. The data elements and the set of operations
applicable to the data elements together form the abstract data type.
To support data abstraction, a programming language must provide a construct
that can be used to encapsulate the data elements and operations that make up
an abstract data type. In C++, this construct is called a class. An instance of
a class is called an object. Classes are composed of data elements called data
members and member functions that define the operations that can be carried out
on the data members.
ΓòÉΓòÉΓòÉ <hidden> Encapsulation ΓòÉΓòÉΓòÉ
Another key feature of object-oriented programming is encapsulation.
Encapsulation means a class can hide the details of:
The representation of its data members
The implementation of the operations that can be performed on these data
members
Application programs manipulate objects of a class using a clearly defined
interface. As long as this interface does not change, you can change the
implementation of a class without having to change the application programs
that use the class. Encapsulation provides the following advantages:
Users of a class do not have to deal with unnecessary implementation
details.
Programs are easier to debug and maintain.
Permitted alterations are clearly specified.
In C++, encapsulation is accomplished by specifying the level of access for
each member of a class. Both the data members and member functions of a class
can be declared public, protected, or private depending on the kind of access
required.
Note: C++ encapsulation is not a true security mechanism. It is possible to
circumvent the class access controls that make encapsulation possible. The
language is not designed to prevent such misuse.
ΓòÉΓòÉΓòÉ <hidden> Inheritance ΓòÉΓòÉΓòÉ
Inheritance lets you reuse existing code and data structures in new
applications. In C++, inheritance is implemented through class derivation. You
can extend a library of existing classes by adding data elements and operations
to existing classes to form derived classes. A derived class has all the
members of its parent or base class, as well as extensions that can provide
additional features. When you create a new derived class, you only have to
write the code for the additional features. The existing features of the base
class are already available.
A base class can have more than one class derived from it. In addition, a
derived class can serve as a base class for other derived classes in a
hierarchy. Typically, a derived class is more specialized than its base class.
A derived class can inherit data members and member functions from more than
one base class. Inheritance from more than one base class is called multiple
inheritance.
ΓòÉΓòÉΓòÉ <hidden> Dynamic Binding and Polymorphism ΓòÉΓòÉΓòÉ
Another key concept that allows you to write generic programs is dynamic or
late binding. Dynamic binding allows a member function call to be resolved at
run time, according to the run-time type of an object reference. This permits
each user-defined class in an inheritance hierarchy to have a different
implementation of a particular function. Application programs can then apply
that function to an object without needing to know the specifics of the class
that the object belongs to.
In C++, dynamic binding hides the differences between a group of classes in an
inheritance hierarchy from the application program. At run time, the system
determines the specific class of the object and invokes the appropriate
function implementation for that class.
Dynamic binding is distinguished from static or compile-time binding, which
involves compile-time member function resolution according to the static type
of an object reference.
ΓòÉΓòÉΓòÉ <hidden> Other Features of C++ ΓòÉΓòÉΓòÉ
C++ provides several other powerful extensions to the C programming language.
Among these are:
Constructors and destructors, which are used to create, initialize and
destroy class objects
Overloaded functions and operators, which lets you extend the operations
a function or operator can perform on different data types
Inline functions, which make programs more efficient
References, which allow a function to modify its arguments in the calling
function
Template functions and classes, which allow the definition of generic
classes and functions
Object-Oriented Exception handling, which provides transfer of control
and recovery from errors and other exceptional circumstances
ΓòÉΓòÉΓòÉ 2.11. C++ Programs ΓòÉΓòÉΓòÉ
C++ programs contain many of the same programming statements and constructs as
C programs:
C++ has the same fundamental types (built-in) data types as C, as well as
some types that are not built-in to C.
Like ISO/ANSI C, C++ allows you to declare new type names by using the
typedef construct. These new type names are not new types.
In general, the scope and storage class rules for C also apply in C++.
C and C++ have the same set of arithmetic and logical operators.
A C++ name can identify any of the following:
an object
a function
a set of functions
an enumerator
a type
a class member
a template
a value
a label
A declaration introduces a name into a program and can define an area of
storage associated with that name.
An expression can be evaluated and is composed of operations and operands. An
expression ending with a ; (semicolon) is called a statement. A statement is
the smallest independent computational unit. Functions are composed of groups
of one or more statements.
A C++ program is composed of one or more functions. These functions can all
reside in a single file or can be placed in different files that are linked to
each other. In C++, a program must have one and only one non-member function
called main().
Source for a Simple C++ Program
Related Information
C++ Support for Object-Oriented Programming
Overview of the C Language
C Source Files
C Source Programs
ΓòÉΓòÉΓòÉ <hidden> Source for a Simple C++ Program ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following is a simple C++ program containing declarations, expressions,
statements, and two functions:
*
************************************************************************/
/**
** A simple C++ program containing declarations,
** expressions, statements, and two functions:
**/
#include <math.h> // contains definition of abs()
double multiplier, common_ratio; // variable declarations
double geo_series(double a, double r) // function definition
{
if (r == 1) // if statement
return -1.0; // return statement
else if (abs(r) < 1.0) // else if statement
return (a / (1 - r)); // statement containing
// expression
else return -2.0;
}
void main() // program execution begins here
{
double sum; // variable declaration
multiplier = 2.2; // initialization of external variable
common_ratio = 3.1; // initialization of external variable
sum = geo_series(multiplier, common_ratio); // function call
// ..
}
ΓòÉΓòÉΓòÉ 2.12. Scope in C++ ΓòÉΓòÉΓòÉ
The area of the code where an identifier is visible is referred to as the scope
of the identifier. The four kinds of scope are:
Local
Function
File
Class
The scope of a name is determined by the location of the name's declaration.
A type name first declared in a function return type has file scope. A type
name first declared in a function argument list has local scope.
A function name that is first declared as a friend of a class is in the first
nonclass scope that encloses the class.
If the friend function is a member of another class, it has the scope of that
class. The scope of a class name first declared as a friend of a class is the
first nonclass enclosing scope.
Related Information
Scope in C
Friend Scope
Scope of Class Names
Member Scope
ΓòÉΓòÉΓòÉ <hidden> Local Scope ΓòÉΓòÉΓòÉ
A name has local scope if it is declared in a block. A name with local scope
can be used in that block and in blocks enclosed within that block, but the
name must be declared before it is used. When the block is exited, the names
declared in the block are no longer available.
Formal argument names for a function have the scope of the outermost block of
that function.
If a local variable is a class object with a destructor, the destructor is
called when control passes out of the block in which the class object was
constructed.
When one block is nested inside another, the variables from the outer block are
usually visible in the nested block. However, if an outer block variable is
redefined in a nested block, the new declaration is in effect in the inner
block. The original declaration is restored when program control returns to the
outer block. This is called block visibility.
ΓòÉΓòÉΓòÉ <hidden> Function Scope ΓòÉΓòÉΓòÉ
The only type of identifier with function scope is a label name. A label is
implicitly declared by its appearance in the program text and is visible
throughout the function that declares it.
ΓòÉΓòÉΓòÉ <hidden> File Scope ΓòÉΓòÉΓòÉ
A name has file scope if its declaration appears outside of all blocks and
classes. A name with file scope is visible from the point where it is declared
to the end of the source file. The name is also made accessible for the
initialization of global variables. If a name is declared extern, it is also
visible, at linkage time, in all object files being linked. Global names are
names declared with file scope.
ΓòÉΓòÉΓòÉ <hidden> Class Scope ΓòÉΓòÉΓòÉ
The name of a class member has class scope and can only be used in the
following cases:
In a member function of that class
In a member function of a class derived from that class
After the . (dot) operator applied to an instance of that class
After the . (dot) operator applied to an instance of a class derived from
that class
After the -> (arrow) operator applied to a pointer to an instance of that
class
After the -> (arrow) operator applied to a pointer to an instance of a
class derived from that class
After the :: (scope resolution) operator applied to the name of a class
After the :: (scope resolution) operator applied to a class derived from
that class.
For more information on class scope, see Scope of Class Names.
ΓòÉΓòÉΓòÉ 2.13. Simple C++ Input and Output ΓòÉΓòÉΓòÉ
Like C, the C++ language has no built-in input and output facilities. Instead,
input and output facilities for C++ are provided by the I/O Stream Library. For
compatibility with C, C++ also supports the standard I/O functions of C. The
I/O Stream Library supports a set of I/O operations, written in the C++
language, for the built-in types. You can extend these facilities to provide
input and output functions for user-defined data types. For a complete
description of the I/O Stream Library, see the Standard Class Library Guide.
There are four predefined I/O stream objects that you can use to perform
standard I/O:
cout
cin
cerr
clog
You can use these in conjunction with the overloaded << (insertion or output)
and >> (extraction or input) operators. To use these streams and operators,
you must include the header file iostream.h. The following example prints
Hello World! to standard output:
/**
** Hello World
**/
#include <iostream.h>
void main()
{
cout << "Hello World!" << endl;
}
The manipulator endl acts as a newline character, causing any output following
it to be directed to the next line. Because it also causes any buffered output
to be flushed, endl is preferred over \n to end lines.
Related Information
Overview of the C++ Language
Overview of the C Language
Overloading Operators
ΓòÉΓòÉΓòÉ <hidden> cout ΓòÉΓòÉΓòÉ
The cout stream is associated with standard output. You can use the output
operator in conjunction with cout to direct a value to standard output.
Successive output operators are concatenated when applied to cout. The
following example prints out three strings in a row and produces the same
result as the previous example, printing Hello World! to standard output.
/**
** Another Hello World, illustrating concatenation with cout
**/
#include <iostream.h>
void main()
{
cout << "Hello "
<< "World"
<< "!"
<< endl;
}
Output operators are defined to accept arguments of any of the fundamental data
types, as well as pointers, references, and array types. You can also overload
the output operator to define output for your own class types.
ΓòÉΓòÉΓòÉ <hidden> cerr and clog ΓòÉΓòÉΓòÉ
The cerr and clog streams direct output to standard error. cerr provides
unbuffered output, while clog provides buffered output. The following example
checks for a division by zero condition. If one occurs, a message is sent to
standard error.
/**
** Check for a division by zero condition.
** If one occurs, a message is sent to standard error.
**/
#include <iostream.h>
void main()
{
double val1, val2;
cout << "Divide Two Values" << endl;
cout << "Enter two numeric values: " << endl;
cin >> val1 >> val2;
if (val2 == 0 )
{
cerr << "The second value must be non-zero" << endl;
} else
cout << "The answer is " << val1 / val2 << endl;
}
ΓòÉΓòÉΓòÉ <hidden> cin ΓòÉΓòÉΓòÉ
The cin class object is associated with standard input. You can use the input
operator in conjunction with cin to read a value from standard input. By
default, white space (including blanks, tabs, and new lines) is disregarded by
the input operator. For example:
/**
** This example illustrates the cin operator
**/
#include <iostream.h>
main()
{
double val1, val2;
cout << "Enter two numeric values:" << endl;
cin >> val1 >> val2;
cout << "The first value entered is " << val1
<< " and the second value is "
<< val2 << "." << endl;
}
If the values 1.2 and 3.4 are entered through standard input, the above program
prints the following to standard output:
Enter two numeric values:
1.2
3.4
The first value entered is 1.2 and the second value is 3.4.
Any white space entered between the two numeric values is disregarded by the
input operator.
The input operator is defined to accept arguments of any of the fundamental
data types, as well as pointers, references and array types. You can also
overload the input operator to define input for your own class types.
ΓòÉΓòÉΓòÉ 2.14. Linkage Specifications - Linking to non-C++ Programs ΓòÉΓòÉΓòÉ
You can link C++ object modules to object modules produced using other source
languages such as C and Fortran by using a linkage specification.
The syntax is:
>>ΓöÇΓöÇexternΓöÇΓöÇstring-literalΓöÇΓöÇΓö¼ΓöÇdeclarationΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇ{ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ}ΓöÇΓöÿ
ΓööΓöÇdeclarationΓöÇΓöÿ
The string-literal is used to specify the linkage associated with a particular
function. For example:
/**
** This example illustrates linkage specifications
**/
extern "C" int printf(const char*,...);
void main()
{
printf("hello\n");
}
Here the string-literal, "C", tells the compiler that the routine
printf(char*,...) has C linkage. Note that string literals used in linkage
specifications are not case sensitive.
Some valid values for string-literal are:
"C++" Default
"C" C type linkage
If the value of string-literal is not recognized, C type linkage is used. For
more information on linkage specifications, see Chapter 13, "Calling
Conventions" in the IBM VisualAge C++ for OS/2 User's Guide and Reference.
Related Information
Overview of the C++ Language
Overview of the C Language
ΓòÉΓòÉΓòÉ 3. Lexical Elements of C and C++ ΓòÉΓòÉΓòÉ
This section describes the following lexical elements of C and C++:
Tokens
Source Program Character Set
Trigraph Sequences
Escape Sequences
Comments
Identifiers
Keywords
Constants
ΓòÉΓòÉΓòÉ 3.1. Tokens ΓòÉΓòÉΓòÉ
Source code is treated during preprocessing and compilation as a sequence of
tokens. There are five different types of tokens:
Identifiers
Keywords
Literals
Operators
Other separators
Adjacent identifiers, keywords and literals must be separated with white
space. Other tokens should be separated by white space to make the source code
more readable. White space includes blanks, horizontal and vertical tabs, new
lines, form feeds and comments.
ΓòÉΓòÉΓòÉ 3.2. Source Program Character Set ΓòÉΓòÉΓòÉ
The following lists the basic character set that must be available at both
compile and run time:
The uppercase and lowercase letters of the English alphabet
The decimal digits 0 through 9
The following graphic characters:
! " # % & ' ( ) * + , - . / :
; < = > ? [ \ ] _ { } ~
The caret (^) character
The split vertical bar (Γûî) character
The space character
The control characters representing new-line, horizontal tab, vertical
tab, and form feed, and end of string (NULL character).
where the # (number sign) character is used for preprocessing only, and the _
(underscore) character is treated as a normal letter.
In extended and compatible language levels, the compiler allows the $ (dollar
sign) character in C++ identifiers to facilitate calls between different
languages and porting code. In ansi language level, the $ (dollar sign)
character is not permitted in C++ identifiers. The default language level for
the compiler is extended. Language level is set with the #pragma langlvl or
the /S option.
For the keyboards that do not support the entire character set, you can use
trigraphs as alternative symbols to represent some characters.
Related Information
Trigraph Sequences
/Ss option
ΓòÉΓòÉΓòÉ <hidden> Trigraph Sequences ΓòÉΓòÉΓòÉ
Some characters from the C character set are not available in all environments.
You can enter these characters into a C source program using a sequence of
three characters called a trigraph. The trigraph sequences are:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé "??=" Γöé "#" Γöé pound sign Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??(" Γöé "[" Γöé left bracket Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??)" Γöé "]" Γöé right bracket Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??<" Γöé "{" Γöé left brace Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??>" Γöé "}" Γöé right brace Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??/" Γöé "\" Γöé backslash Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??'" Γöé "^" Γöé caret Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??!" Γöé "|" Γöé pipe Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "??-" Γöé "~" Γöé tilde Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The preprocessor replaces trigraph sequences with the corresponding
single-character representation.
Related Information
Source Program Character Set
ΓòÉΓòÉΓòÉ 3.3. Comments ΓòÉΓòÉΓòÉ
Comments begin with the /* characters, end with the */ characters, and can span
more than one line. You can put comments anywhere the language allows white
space.
Comments are replaced during preprocessing by a single space character.
Multibyte characters can also be included within a comment.
Note: The /* or */ characters found in a character constant or string literal
do not start or end comments.
In the following program, line 6 is a comment:
1 #include <stdio.h>
2
3 int main(void)
4 {
5 printf("This program has a comment.\n");
6 /* printf("This is a comment line and will not print.\n"); */
7 return 0;
8 }
Because the comment on line 6 is equivalent to a space, the output of this
program is:
This program has a comment.
Because the comment delimiters are inside a string literal, line 5 in the
following program is not a comment.
1 #include <stdio.h>
2
3 int main(void)
4 {
5 printf("This program does not have \
6 /* NOT A COMMENT */ a comment.\n");
7 return 0;
8 }
The output of the program is:
This program does not have /* NOT A COMMENT */ a comment.
You cannot nest comments. Each comment ends at the first occurrence of */.
Related Information
C++ Comments
/Ss option
ΓòÉΓòÉΓòÉ <hidden> C++ Comments ΓòÉΓòÉΓòÉ
If the /Ss compiler option is in effect when you compile a C program, double
slashes (//) also specify the beginning of a comment. The comment ends at the
next new line character. C++ also permits double-slash comments as part of the
language definition.
A C++ comment can span more than one physical source line if it is joined into
one logical source line with line-continuation (\) characters. The backslash
character can also be represented by a trigraph.
ΓòÉΓòÉΓòÉ 3.4. Identifiers ΓòÉΓòÉΓòÉ
Identifiers consist of an arbitrary number of letters or digits. They provide
names for the following language elements:
Functions
Data objects
Labels
Tags
Parameters
Macros
Typedefs
Structure and union members.
ΓòÉΓòÉΓòÉ 3.4.1. Significant Characters in Identifiers ΓòÉΓòÉΓòÉ
There is no limit for the number of characters in an identifier. However, only
the first several characters of identifiers may be significant. The following
table shows the number of significant characters for several kinds of
identifiers.
Identifier Maximum Number of Significant Characters
Static data objects 255 characters
Static function names 255 characters
External data objects 255 characters
External function names 255 characters
ΓòÉΓòÉΓòÉ 3.4.2. Case Sensitivity and Special Characters in Identifiers ΓòÉΓòÉΓòÉ
The compiler distinguishes between uppercase and lowercase letters in
identifiers. For example, PROFIT and profit represent different data objects.
Note: By default, the VisualAge C++ linker linker is case sensitive. To force
it to be case insensitive, use the /IGNORECASE linker option, though you
typically should note need to use this option. For complete portability, never
use different case representations to refer to the same object.
Avoid creating identifiers that begin with an underscore (_) for function names
and variable names.
The first character in an identifier must be a letter. The _ (underscore)
character is considered a letter; however, identifiers beginning with an
underscore are reserved by the compiler for identifiers at file scope.
Identifiers that begin with two underscores or an underscore followed by a
capital letter, are reserved in all contexts.
Although the names of system calls and library functions are not reserved words
if you do not include the appropriate headers, avoid using them as identifiers.
Duplication of a predefined name can lead to confusion for the maintainers of
your code and can cause errors at link time or run time. If you include a
library in a program, be aware of the function names in that library to avoid
name duplications.
You should always include the appropriate headers when using standard library
functions. At the extended and compatible language levels, C++ identifiers can
contain the $ character.
Related Information
Name Spaces
Overview of the C Language
File Inclusion (#include)
ΓòÉΓòÉΓòÉ 3.4.3. Keywords ΓòÉΓòÉΓòÉ
Keywords are identifiers reserved by the language for special use. Although you
can use them for preprocessor macro names, it is poor programming style. Only
the exact spelling of keywords is reserved. For example, auto is reserved but
AUTO is not. The following lists the keywords common to both the C and C++
languages. These keywords are also included in the ISO/ANSI C language
definition:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Table 1. Keywords Common to C and C++ Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé auto Γöé double Γöé int Γöé struct Γöé
Γöé break Γöé else Γöé long Γöé switch Γöé
Γöé case Γöé enum Γöé register Γöé typedef Γöé
Γöé char Γöé extern Γöé return Γöé union Γöé
Γöé const Γöé float Γöé short Γöé unsigned Γöé
Γöé continue Γöé for Γöé signed Γöé void Γöé
Γöé default Γöé goto Γöé sizeof Γöé volatile Γöé
Γöé do Γöé if Γöé static Γöé while Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The C++ language also reserves the following keywords:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Table 2. C++ Keywords Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé asm Γöé inline Γöé protected Γöé throw Γöé
Γöé catch Γöé new Γöé public Γöé try Γöé
Γöé class Γöé operator Γöé template Γöé virtual Γöé
Γöé delete Γöé private Γöé this Γöé wchar_t Γöé
Γöé friend Γöé Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The VisualAge C++ compiler also reserves the following keywords. They are
considered to be VisualAge C++ extensions to the existing language standards.
Except for the keywords _Far32 and _Inline, the following keywords are
supported in both C and C++.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Table 3. Additional VisualAge C++ Keywords Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé _Cdecl Γöé _Far32 Γöé _Pascal Γöé __stdcall Γöé
Γöé __cdecl Γöé _Fastcall Γöé _Packed Γöé _System Γöé
Γöé _Export Γöé _Inline Γöé _Seg16 Γöé __unaligned Γöé
Γöé _Far16 Γöé _Optlink Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The keyword _Packed is reserved only for C programs. _Packed is an extension to
the ISO/ANSI C standard, and is not supported by C++.
ΓòÉΓòÉΓòÉ 3.5. Constants ΓòÉΓòÉΓòÉ
A constant does not change its value while the program is running. The value of
any constant must be in the range of representable values for its type.
The C language contains the following types of constants (also called
literals):
Integer Constants
Floating-Point Constants
Character Constants
String Literals
Enumeration constants, which belong to the lexical class of identifiers, are
discussed in Enumerations. For more information on data types, see Type
Specifiers.
ΓòÉΓòÉΓòÉ 3.5.1. Integer Constants ΓòÉΓòÉΓòÉ
Integer constants can represent decimal, octal, or hexadecimal values. The data
type of an integer constant is determined by the form, value, and suffix of the
constant. The following lists the integer constants and shows the possible data
types for each constant. The smallest data type can represent the constant
value is used to store the constant.
Constant Data Type
unsuffixed decimal int, long int, unsigned long int
unsuffixed octal int, unsigned int, long int, unsigned long int
unsuffixed hexadecimal int, unsigned int, long int, unsigned long int
suffixed by u or U unsigned int, unsigned long int
suffixed by l or L long int, unsigned long int
suffixed by both u or U, and l or L unsigned long int
A plus (+) or minus (-) symbol can precede the constant. It is treated as a
unary operator rather than as part of the constant value.
Related Information
Decimal Constants
Octal Constants
Hexadecimal Constants
Integer Variables
ΓòÉΓòÉΓòÉ 3.5.1.1. Decimal Constants ΓòÉΓòÉΓòÉ
A decimal constant contains any of the digits 0 through 9. The first digit
cannot be 0. Integer constants beginning with the digit 0 are interpreted as an
octal constant, rather than as a decimal constant.
Related Information
Integer Constants
Octal Constants
Hexadecimal Constants
Integer Variables
ΓòÉΓòÉΓòÉ 3.5.1.2. Hexadecimal Constants ΓòÉΓòÉΓòÉ
A hexadecimal constant begins with the 0 digit followed by either an x or X,
followed by any combination of the digits 0 through 9 and the letters a through
f or A through F. The letters A (or a) through F (or f) represent the values 10
through 15, respectively.
The following are examples of hexadecimal constants:
0x3b24
0XF96
0x21
0x3AA
0X29b
0X4bD
Related Information
Integer Constants
Decimal Constants
Octal Constants
Integer Variables
ΓòÉΓòÉΓòÉ 3.5.1.3. Octal Constants ΓòÉΓòÉΓòÉ
An octal constant begins with the digit 0 and contains any of the digits 0
through 7.
The following are examples of octal constants:
0
0125
034673
03245
Related Information
Integer Constants
Decimal Constants
Hexadecimal Constants
Integer Variables
ΓòÉΓòÉΓòÉ 3.5.2. Floating-Point Constants ΓòÉΓòÉΓòÉ
A floating-point constant consists of:
An integral part
A decimal point
A fractional part
An exponent part
An optional suffix.
Both the integral and fractional parts are made up of decimal digits. You can
omit either the integral part or the fractional part, but not both. You can
omit either the decimal point or the exponent part, but not both.
The representation of a floating-point number on a system is unspecified. If a
floating-point constant is too large or too small, the result is undefined by
the language. VisualAge C++, represents floating-point numbers according to
IEEE rules. For C, if a floating-point constant is too large, it is set to the
largest value representable by the type. If it is too small, it is set to
zero. For C++, constant values that are too large or too small cause a
compile-time error.
The suffix f or F indicates a type of float, and the suffix l or L indicates a
type of long double. If a suffix is not specified, the floating-point constant
has a type double.
A plus (+) or minus (-) symbol can precede a floating-point constant. However,
it is not part of the constant; it is interpreted as a unary operator.
The following are examples of floating-point constants:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé FLOATING-POINT CON- Γöé VALUE Γöé
Γöé STANT Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "5.3876e4" Γöé "53,876" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "4e-11" Γöé "0.00000000004" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "1e+5" Γöé "100000" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "7.321E-3" Γöé "0.007321" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "3.2E+4" Γöé "32000" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "0.5e-6" Γöé "0.0000005" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "0.45" Γöé "0.45" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "6.e10" Γöé "60000000000" Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Related Information
Floating-Point Variables
ΓòÉΓòÉΓòÉ 3.5.3. Character Constants ΓòÉΓòÉΓòÉ
A character constant contains a sequence of characters or escape sequences
enclosed in single quotation mark symbols.
At least one character or escape sequence must appear in the character
constant. The characters can be any from the source program character set,
excluding the single quotation mark, backslash and new-line symbols. The prefix
L indicates a wide character constant. A character constant must appear on a
single logical source line.
The value of a character constant containing a single character is the numeric
representation of the character in the character set used at run time. The
value of a wide character constant containing a single multibyte character is
the code for that character, as defined by the mbtowc function. If the
character constant contains more than one character, the last 4 bytes represent
the character constant. In C++, a character constant can contain only one
character.
In C, a character constant has type int. In C++, a character constant hast type
char.
A wide character constant is represented by a double-byte character of type
wchar_t. Multibyte characters represent character sets that use more than one
byte in their representation. In OS/2, each multibyte character can contain up
to 2 bytes.
Restrictions
To represent the single quotation symbol, backslash, and new-line characters,
you must use the corresponding escape sequence. For more information on escape
sequences, see Escape Sequences.
The following are examples of character constants:
'a' '\''
'0' '('
'x' '\n'
'7' '\117'
'C'
Related Information
String Literals
Escape Sequences
Integer Variables
ΓòÉΓòÉΓòÉ 3.5.4. String Literals ΓòÉΓòÉΓòÉ
A string constant or literal contains a sequence of characters or escape
sequences enclosed in double quotation mark symbols.
The prefix L indicates a wide-character string literal.
A null ('\0') character is appended to each string. For a wide character string
(a string prefixed by the letter L), the value '\0' of type wchar_t is
appended. By convention, programs recognize the end of a string by finding the
null character.
Multiple spaces contained within a string constant are retained.
To continue a string on the next line, use the line continuation sequence (\
symbol immediately followed by a new-line character). A carriage return must
immediately follow the backslash. In the following example, the string literal
second causes a compile-time error.
char *first = "This string continues onto the next\
line, where it ends."; /* compiles successfully. */
char *second = "The comment makes the \ /* continuation symbol */
invisible to the compiler."; /* compilation error. */
Another way to continue a string is to have two or more consecutive strings.
Adjacent string literals are concatenated to produce a single string. You
cannot concatenate a wide string constant with a character string constant. For
example:
"hello " "there" /* is equivalent to "hello there" */
"hello " L"there" /* is not valid */
"hello" "there" /* is equivalent to "hellothere" */
Characters in concatenated strings remain distinct. For example, the strings
"\xab" and "3" are concatenated to form "\xab3". However, the characters \xab
and 3 remain distinct and are not merged to form the hexadecimal character
\xab3.
Following any concatenation, '\0' of type char is appended at the end of each
string. C++ programs find the end of a string by scanning for this value. For a
wide-character string literal, '\0' of type wchar_t is appended. For example:
char *first = "Hello "; /* stored as "Hello \0" */
char *second = "there"; /* stored as "there\0" */
char *third = "Hello " "there"; /* stored as "Hello there\0" */
A character string constant has type array of char and static storage duration.
A wide character constant has type array of wchar_t and static storage
duration.
Use the escape sequence \n to represent a new-line character as part of the
string. Use the escape sequence \\ to represent a backslash character as part
of the string. You can represent the single quotation mark symbol by itself ',
but you use the escape sequence \" to represent the double quotation mark
symbol.
For example:
/**
** This example illustrates escape sequences in string literals
**/
#include <iostream.h>
void main ()
{
char *s ="Hi there! \n";
cout << s;
char *p = "The backslash character \\.";
cout << p << endl;
char *q = "The double quotation mark \".\n";
cout << q ;
}
This program produces the following output:
Hi there!
The backslash character \.
The double quotation mark ".
You should be careful when modifying string literals because the resulting
behavior depends on whether your strings are stored in read/write static
memory. String literals are stored read/write by default. Use the #pragma
strings preprocessor directive, to specify the storage type for strings.
The following are examples of string literals:
char titles[ ] = "Handel's \"Water Music\"";
char *mail_addr = "Last Name First Name MI Street Address \
City Province Postal code ";
char *temp_string = "abc" "def" "ghi"; /* *temp_string = "abcdefghi\0" */
wchar_t *wide_string = L"longstring";
Related Information
Character Constants
Escape Sequences
Characters
Arrays
Pragma Directives (#pragma)
#pragma strings
ΓòÉΓòÉΓòÉ 3.5.5. Escape Sequences ΓòÉΓòÉΓòÉ
An escape sequence contains a backslash (\) symbol followed by one of the
escape sequence characters or an octal or hexadecimal number. A hexadecimal
escape sequence contains an x followed by one or more hexadecimal digits (0-9,
A-F, a-f). An octal escape sequence uses up to three octal digits (0-7).
Note: The line continuation sequence (\ followed by a new-line character) is
not an escape sequence. It is used in character strings to indicate that the
current line continues on the next line.
The escape sequences and the characters they represent are:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ESCAPE Γöé CHARACTER REPRES- Γöé
Γöé SEQUENCE Γöé ENTED Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\a" Γöé Alert (bell, alarm) Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\b" Γöé Backspace Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\f" Γöé Form feed (new page) Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\n" Γöé New-line Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\r" Γöé Carriage return Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\t" Γöé Horizontal tab Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\v" Γöé Vertical tab Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\'" Γöé Single quotation Γöé
Γöé Γöé mark Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\"" Γöé Double quotation Γöé
Γöé Γöé mark Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\?" Γöé Question mark Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "\\" Γöé Backslash Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The value of an escape sequence represents the member of the character set used
at run time. For example, on a system uses the ASCII character codes, the
letter V is represented by the escape sequence \x56.
Use escape sequences only in character constants or in string literals.
If an escape sequence is not recognized, the compiler uses the character
following the backslash and a message is issued. Note that this behavior is
implementation-defined.
In string and character sequences, when you want the backslash to represent
itself (rather than the beginning of an escape sequence), you must use a \\
backslash escape sequence. For example:
cout << "The escape sequence \\n." << endl;
This statement results in the following output:
The escape sequence \n.
The following program prints the character 'a' four times to standard output,
and then prints a new line:
#include <iostream.h>
void main()
{
char a,b,c,d,e;
a='a';
b=97; // ASCII integer value
c='\141'; // ASCII octal value
d='\x61'; // ASCII hexadecimal value
e='\n';
cout << a << b << c << d << e;
}
Related Information
Character Constants
String Literals
ΓòÉΓòÉΓòÉ 4. Declarations ΓòÉΓòÉΓòÉ
A declaration establishes the names and characteristics of data objects and
functions used in a program. A definition allocates storage for data objects or
specifies the body for a function. When you define a type, no storage is
allocated.
Declarations determine the following properties of data objects and their
identifiers:
Scope, which describes the visibility of an identifier in a block or
source file. For a complete description of scope, see Scope in C.
Linkage, which describes the association between two identical
identifiers. See Program Linkage for more information.
Storage duration, which describes when the system allocates and frees
storage for a data object. See Storage Duration for more information.
Type, which describes the kind of data the object is to represent.
The declaration for a data object includes one or more of:
Qualifier and declarator, described on page Storage Class Specifiers
Storage class, described on page Storage Class Specifiers
Initializer, described on page Initializers
Type specifier, described on page Type Specifiers
Function declarations are described in Functions.
Syntax of a Data Declaration
Note:
1. One of the fundamental differences between C++ and C is the placement of
variable declarations. Although variables are declared in the same way,
in C++, variable declarations can be put anywhere in the program. In C,
declarations must come before any statements in a block.
In the following C++ example, the variable d is declared in the middle of
the main() function:
#include <iostream.h>
void main()
{
int a, b;
cout << "Please enter two integers" << endl;
cin >> a >> b;
int d = a + b;
cout << "Here is the sum of your two integers:" << d << endl;
}
2. A given function, object, or type can have only one definition. It can
have more than one declaration as long as all of the declarations match.
If a function is never called and its address is never taken, then you do
not have to define it. If an object is declared but never used, or is
only used as the operand of sizeof, you do not have to define it. You can
declare a given class or enumerator more than once.
The following table shows examples of declarations and definitions.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Table 4. Examples of Declarations and Definitions Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé DECLARATIONS Γöé DECLARATIONS AND DEFINITIONS Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "extern double pi;" Γöé "double pi = 3.14159265;" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "float square(float x);" Γöé "float square(float x) { return x*x; }" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "struct payroll;" Γöé struct payroll { Γöé
Γöé Γöé char *name; Γöé
Γöé Γöé float salary; Γöé
Γöé Γöé } employee; Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Related Information
Block Scope Data Declarations
File Scope Data Declarations
Declarators
Storage Class Specifiers
Initializers
Type Specifiers
ΓòÉΓòÉΓòÉ <hidden> Data Declaration Syntax ΓòÉΓòÉΓòÉ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Γöé
>>ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇΓöÇΓöÇdeclaratorΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓö┤ΓöÇΓöÇ;ΓöÇΓöÇ><
Γö£ΓöÇstorage_class_specifierΓöÇΓöñ ΓööΓöÇinitializerΓöÇΓöÿ
Γö£ΓöÇtype_specifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓööΓöÇtype_qualifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 4.1. Block Scope Data Declarations ΓòÉΓòÉΓòÉ
A block scope data declaration can only be put at the beginning of a block. It
describes a variable and makes that variable accessible to the current block.
All block scope declarations that do not have the extern storage class
specifier are definitions and allocate storage for that object.
You can declare a data object with block scope with any of the storage class
specifiers described in Storage Class Specifiers. If you do not specify a
storage class specifier in a block-scope data declaration, the default storage
class specifier auto is used. If you specify a storage class specifier, you can
omit the type specifier. If you omit the type specifier, all variables in that
declaration receive type int.
Initialization
You cannot initialize a variable declared in a block scope data declaration
that has the extern storage class specifier.
The types of variables you can initialize and the values that uninitialized
variables receive vary for that storage class specifier. See Storage Class
Specifiers for details on the different storage classes.
Storage
The duration and type of storage varies for each storage class specifier.
Declarations with the auto or register storage class specifier result in
automatic storage duration. Declarations with the extern or static storage
class specifier result in static storage duration.
Related Information
Declarators
Storage Class Specifiers
auto Storage Class Specifier
extern Storage Class Specifier
register Storage Class Specifier
static Storage Class Specifier
Declarations
Initializers
Type Specifiers
ΓòÉΓòÉΓòÉ 4.2. File Scope Data Declarations ΓòÉΓòÉΓòÉ
A file scope data declaration appears outside any function definition. It
describes a variable and makes that variable accessible to all functions that
are in the same file and whose definitions appear after the declaration.
A file scope data definition is a data declaration at file scope that also
causes storage to be allocated for that variable. All objects whose identifiers
are declared at file scope have static storage duration.
Use a file scope data declaration to declare variables that you want to have
external linkage.
The only storage class specifiers you can put in a file scope data declaration
are static, extern, and typedef. If you specify static, all variables defined
in it have internal linkage. If you do not specify static, all variables
defined in it have external linkage. If you specify the storage class you can
omit the type specifier. If you omit the type specifier, all variables defined
in that declaration receive the type int.
Initialization
You can initialize any object with file scope. If you do not initialize a file
scope variable, its initial value is zero of the appropriate type. If you do
initialize it, the initializer must be described by a constant expression, or
it must reduce to the address of a previously declared variable at file scope,
possibly modified by a constant expression. Initialization of all variables at
file scope takes place before the main function begins running.
Storage
All objects with file scope data declarations have static storage duration.
Storage is allocated at runtime and freed when the program stops running.
Related Information
extern Storage Class Specifier
static Storage Class Specifier
Declarations
Declarators
Initializers
Type Specifiers
ΓòÉΓòÉΓòÉ 4.3. Objects ΓòÉΓòÉΓòÉ
An object is a region of storage that contains a value or group of values. Each
value can be accessed using its identifier or a more complex expression that
refers to the object. In addition, each object has a unique data type. Both the
identifier and data type of an object are established in the object
declaration.
The data type of an object determines the initial storage allocation for that
object and the interpretation of the values during subsequent access. It is
also used in any type-checking operations.
C++ has built-in, or standard, data types and user-defined data types. Standard
data types include signed and unsigned integers, floating-point numbers, and
characters. User-defined types include enumerations, structures, unions, and
classes.
In C++ code, objects are represented by variables. A variable also represents
the location in storage that contains the value of an object.
An instance of a class type is commonly called a class object. The individual
class members are also called objects. The set of all member objects comprises
a class object.
Related Information
Type Specifiers
C++ Classes
Declarations
ΓòÉΓòÉΓòÉ 4.4. Declarators ΓòÉΓòÉΓòÉ
A declarator designates a data object or function. Declarators appear in all
data definitions and declarations and in some type definitions.
In a declarator, you can specify the type of an object to be an array, a
pointer, or a reference. You can specify that the return type of a function is
a pointer or a reference. You can also perform initialization in a declarator.
Syntax of a Declarator VisualAge C++ also implements the following qualifiers:
_Packed
__unaligned
_Seg16
_Export
_Inline
In C, you cannot declare or define a volatile or const function. C++ class
member functions can be qualified with const or volatile.
A simple declarator consists of an identifier, which names a data object. For
example, the following block scope data declaration uses initial as the
declarator:
auto char initial;
The data object initial has the storage class auto and the data type char.
You can define or declare a structure, union, or array. by using a declarator
that contains an identifier, which names the data object, and some combination
of symbols and identifiers, which describes the type of data that the object
represents.
The following declaration uses compute[5] as the declarator:
extern long int compute[5];
Examples of Declarators
Related Information
volatile and const Qualifiers
_Packed Qualifier
_Seg16 Type Qualifier
_Export Qualifier
Declarations
Arrays
Enumerations
Pointers
Structures
Unions
ΓòÉΓòÉΓòÉ <hidden> Declarator Syntax ΓòÉΓòÉΓòÉ
A declarator has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓö┤ΓöÇΓöÇ>
ΓööΓöÇ*ΓöÇΓöÿ Γö£ΓöÇvolatileΓöÇΓöñ
ΓööΓöÇconstΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇidentifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓö┤ΓöÇΓöÇ><
ΓööΓöÇ(ΓöÇΓöÇdeclaratorΓöÇΓöÇ)ΓöÇΓöÿ Γö£ΓöÇsubscript_declaratorΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇ(ΓöÇΓöÇparameter_type_listΓöÇΓöÇ)ΓöÇΓöñ
ΓööΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓööΓöÇidentifierΓöÇΓöÿ
A qualifier is one of: const, volatile or _Packed. The VisualAge C++ compiler
also implements the _Seg16, _Export, and _Inline qualifiers.
C++ Note: C++ does not support the _Packed keyword.
A declarator can contain a subdeclarator. A subdeclarator has the form:
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇidentifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ>
Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé ΓööΓöÇ(ΓöÇΓöÇsubdeclaratorΓöÇΓöÇ)ΓöÇΓöÿ
Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ*ΓöÇΓö┤ΓöÇΓöÿ
Γö£ΓöÇvolatileΓöÇΓöñ
ΓööΓöÇconstΓöÇΓöÇΓöÇΓöÇΓöÿ
>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇsubscript_declaratorΓöÇΓöÿ
A subscript declarator describes the number of dimensions in an array and the
number of elements in each dimension. A subscript declarator has the form:
>>ΓöÇΓöÇ[ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ]ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇconstant_expressionΓöÇΓöÿ Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇ[ΓöÇΓöÇconstant_expressionΓöÇΓöÇ]ΓöÇΓö┤ΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 4.4.1. volatile and const Qualifiers ΓòÉΓòÉΓòÉ
The volatile qualifier maintains consistency in memory access to data objects.
Volatile objects are read from memory each time their value is needed, and
written back to memory each time they are changed. The volatile qualifier is
useful for data objects having values that may be changed in ways unknown to
your program (such as the system clock). Objects referenced by multiple threads
or by signal handlers should also be qualified as volatile. Portions of an
expression that reference volatile objects are not to be changed or removed.
The const qualifier explicitly declares a data object as a data item that
cannot be changed. Its value is set at initialization. You cannot use const
data objects in expressions requiring a modifiable lvalue. For example, a const
data object cannot appear on the left-hand side of an assignment statement.
These type qualifiers are only meaningful in expressions that are lvalues.
For a volatile or const pointer, you must put the keyword between the * and the
identifier. For example:
int * volatile x; /* x is a volatile pointer to an int */
int * const y = &z; /* y is a const pointer to the int variable z */
For a pointer to a volatile or const data object, the type specifier,
qualifier, and storage class specifier can be in any order. For example:
volatile int *x; /* x is a pointer to a volatile int */
or
int volatile *x; /* x is a pointer to a volatile int */
const int *y; /* y is a pointer to a const int */
or
int const *y; /* y is a pointer to a const int */
In the following example, the pointer to y is a constant. You can change the
value that y points to, but you cannot change the value of y:
int * const y
In the following example, the value that y points to is a constant integer and
cannot be changed. However, you can change the content of y:
const int * y
For other types of volatile and const variables, the position of the keyword
within the definition (or declaration) is less important. For example:
volatile struct omega {
int limit;
char code;
} group;
provides the same storage as:
struct omega {
int limit;
char code;
} volatile group;
In both examples, only the structure variable group receives the volatile
qualifier. Similarly, if you specified the const keyword instead of volatile,
only the structure variable group receives the const qualifier. The const and
volatile qualifiers when applied to a structure, union, or class also apply to
the members of the structure, union, or class.
Although enumeration, structure, and union variables can receive the volatile
or const qualifier, enumeration, structure, and union tags do not carry the
volatile or const qualifier. For example, the blue structure does not carry the
volatile qualifier:
volatile struct whale {
int weight;
char name[8];
} beluga;
struct whale blue;
The keywords volatile and const cannot separate the keywords enum, struct, and
union from their tags.
You can declare or define a volatile or const function only if it is a C++
member function. You can define or declare any function to return a pointer to
a volatile or const function.
You can put more than one qualifier on a declaration but you cannot specify the
same qualifier more than once on a declaration.
Related Information
Declarators
_Packed Qualifier
_Seg16 Type Qualifier
_Export Qualifier
Type Specifiers
Structures
Unions
Enumerations
Pointers
lvalues
ΓòÉΓòÉΓòÉ 4.4.2. _Packed Qualifier ΓòÉΓòÉΓòÉ
The _Packed qualifier removes padding between members of structures and unions,
whenever possible. However, the storage saved using packed structures and
unions may come at the expense of runtime performance. Most machines access
data more efficiently if it is aligned on appropriate boundaries. With packed
structures and unions, members are generally not aligned on natural boundaries,
and the result is that member-accessing operations (using the . and ->
operators) are slower.
_Packed can only be used with structures or unions. If you use _Packed with
other types, an error message is generated and the qualifier has no effect on
the declarator it qualifies. Packed and nonpacked structures and unions have
different storage layouts. Comparisons between packed and nonpacked structures
or unions of the same type are prohibited.
If you specify the _Packed qualifier on a structure or union that contains a
structure or union as a member, the qualifier is not passed on to the contained
structure or union.
The VisualAge C++ compiler also lets you pack structures using the #pragma pack
directive or the /Sp option.
C++ Note: C++ does not support the _Packed keyword. Use the #pragma pack
directive or the /Sp compiler option to control the alignment of structures and
unions.
Related Information
#pragma pack
/Sp option
Declarators
volatile and const Qualifiers
_Seg16 Type Qualifier
_Export Qualifier
Type Specifiers
Structures
Unions
ΓòÉΓòÉΓòÉ <hidden> Examples of Declarators ΓòÉΓòÉΓòÉ
The following table describes some declarators:
Example Description
int owner owner is an int data object.
int *node node is a pointer to an int data object.
int names[126] names is an array of 126 int elements.
int *action( ) action is a function returning a pointer to an int.
volatile int min min is an int that has the volatile
int * volatile volume volume is a volatile pointer to an int.
volatile int * next next is a pointer to a volatile int
volatile int * sequence[5] sequence is an array of five pointers to volatile
int objects.
extern const volatile int op_system_clock op_system_clock is an extern int
that has the volatile attribute.
ΓòÉΓòÉΓòÉ 4.4.3. _Seg16 Type Qualifier ΓòÉΓòÉΓòÉ
Because pointers are interpreted differently in 16-bit programs than in 32-bit
programs, they cannot be shared between the two types of program. Use the
_Seg16 type qualifier when calling 16-bit code to ensure correct mapping of
pointers between the different types of code. For example:
char * _Seg16 p16;
declares p16 to be a segmented pointer that can be addressed by a 16-bit
program. The pointer can also be used in a 32-bit program, because the compiler
converts it to 32-bit form when it is used in an expression. The _Seg16
qualifier can only be used with pointers. Note that _Seg16 comes after the
asterisk in the declaration, as required by ISO/ANSI C syntax rules.
All pointers shared between 32-bit and 16-bit code must be qualified with
_Seg16. These include pointers passed indirectly to 16-bit code, such as
pointers in structures and pointers that are referenced by pointers passed
directly to 16-bit code.
While it is possible to write a program where all the pointers are qualified
with _Seg16, it is not recommended. Every time a segmented pointer is used in a
32-bit program, it must be converted to a 32-bit pointer and then back to
segmented pointer to be stored. This process will cause a noticeable
performance degradation in your program. Pointers that are not shared with
16-bit code and those that are passed by value to 16-bit code (that is, as a
parameter to a function) do not need to be qualified with _Seg16.
For more information on using _Seg16 and calling 16-bit programs from 32-bit
code, see the IBM VisualAge C++ for OS/2 Programming Guide.
Related Information
Pointers
#pragma seg16
Declarators
volatile and const Qualifiers
_Packed Qualifier
_Export Qualifier
ΓòÉΓòÉΓòÉ 4.5. Storage Class Specifiers ΓòÉΓòÉΓòÉ
The storage class specifier used within the declaration determines whether:
The object has internal, external, or no linkage.
The object is to be stored in memory or in a register, if available.
The object receives the default initial value 0 or an indeterminate
default initial value.
The object can be referenced throughout a program or only within the
function, block, or source file where the variable is defined.
Storage duration for the object is static (storage is maintained
throughout program run time) or automatic (storage is maintained only
during the execution of the block where the object is defined).
For a function, the storage class specifier determines the linkage of the
function.
The VisualAge C++ compiler implements an additional storage class specifier
for functions, inline. The _Inline and inline specifiers determine whether the
function code will be inlined or called. Note that _Inline and inline are
ignored if the /Oi- compiler option is specified.
The following sections describe the storage class specifiers:
auto Storage Class Specifier
extern Storage Class Specifier
register Storage Class Specifier
static Storage Class Specifier
Inline Specifiers
Related Information
Program Linkage
C++ Inline Functions
Declarations
Inline Specifiers
/Oi option
ΓòÉΓòÉΓòÉ 4.5.1. auto Storage Class Specifier ΓòÉΓòÉΓòÉ
The auto storage class specifier lets you define a variable with automatic
storage; its use and storage is restricted to the current block. The storage
class keyword auto is optional in a data declaration. It is not permitted in a
parameter declaration. A variable having the auto storage class specifier must
be declared within a block. It cannot be used for file scope declarations.
Because automatic variables require storage only while they are actually being
used, defining variables with the auto storage class can decrease the amount of
memory required to run a program. However, having many large automatic objects
is likely to cause you to run out of stack space.
Declaring variables with the auto storage class can also make code easier to
maintain, because a change to an auto variable in one function never affects
another function (unless it is passed as an argument).
You can initialize any auto variable except parameters. If you do not
initialize an automatic object, its value is indeterminate. If you provide an
initial value, the expression representing the initial value can be any valid C
or C++ expression. For structure and union members, the initial value must be a
valid constant expression if an initializer list is used. The object is then
set to that initial value each time the program block that contains the
object's definition is entered.
Note: If you use the goto statement to jump into the middle of a block,
automatic variables within that block are not initialized.
Objects with the auto storage class specifier have automatic storage duration.
Each time a block is entered, storage for auto objects defined in that block is
made available. When the block is exited, the objects are no longer available
for use.
If an auto object is defined within a function that is recursively invoked,
memory is allocated for the object at each invocation of the block.
Examples of auto Storage Class
Related Information
Storage Class Specifiers
register Storage Class Specifier
Block Scope Data Declarations
Function Declarator
Address &
ΓòÉΓòÉΓòÉ <hidden> Examples of auto Storage Class ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program shows the scope and initialization of auto variables. The
function main defines two variables, each named auto_var. The first definition
occurs on line 8. The second definition occurs in a nested block on line 11.
While the nested block is running, only the auto_var created by the second
definition is available. During the rest of the program, only the auto_var
created by the first definition is available.
*
************************************************************************/
1 /****************************************************
2 ** Example illustrating the use of auto variables **
3 ****************************************************/
4
5 #include <stdio.h>
6
7 int main(void)
8 {
9 void call_func(int passed_var);
10 auto int auto_var = 1; /* first definition of auto_var */
11
12 {
13 int auto_var = 2; /* second definition of auto_var */
14 printf("inner auto_var = %d\n", auto_var);
15 }
16 call_func(auto_var);
17 printf("outer auto_var = %d\n", auto_var);
18 return(0);
19 }
20
21 void call_func(int passed_var)
22 {
23 printf("passed_var = %d\n", passed_var);
24 passed_var = 3;
25 printf("passed_var = %d\n", passed_var);
26 }
/************************************************************************
*
This program produces the following output:
inner auto_var = 2
passed_var = 1
passed_var = 3
outer auto_var = 1
The following example uses an array that has the storage class auto to pass a
character string to the function sort. The function sort receives the address
of the character string, rather than the contents of the array. The address
enables sort to change the values of the elements in the array.
*
************************************************************************/
/*****************************************************************
** Sorted string program -- this example passes an array name **
** to a function **
*****************************************************************/
#include <stdio.h>
#include <string.h>
int main(void)
{
void sort(char *array, int n);
char string[75];
int length;
printf("Enter letters:\n");
scanf("%74s", string);
length = strlen(string);
sort(string,length);
printf("The sorted string is: %s\n", string);
return(0);
}
void sort(char *array, int n)
{
int gap, i, j, temp;
for (gap = n / 2; gap > 0; gap /= 2)
for (i = gap; i < n; i++)
for (j = i - gap; j >= 0 && array[j] > array[j + gap];
j -= gap)
{
temp = array[j];
array[j] = array[j + gap];
array[j + gap] = temp;
}
}
/**********************************************************************************
*
When the program is run, interaction with the program could produce:
Output Enter letters:
Input zyfab
Output The sorted string is: abfyz
*
************************************************************************/
ΓòÉΓòÉΓòÉ 4.5.2. extern Storage Class Specifier ΓòÉΓòÉΓòÉ
The extern storage class specifier lets you declare objects and functions that
several source files can use. All object declarations that occur outside a
function and that do not contain a storage class specifier declare identifiers
with external linkage. All function definitions that do not specify a storage
class define functions with external linkage.
You can distinguish an extern declaration from an extern definition by the
presence of the keyword extern and the absence of an initial value. If the
keyword extern is absent or if there is an initial value, the declaration is
also a definition; otherwise, it is just a declaration. An extern definition
can appear only at file scope.
An extern variable, function definition, or declaration also makes the
described variable or function usable by the succeeding part of the current
source file. This declaration does not replace the definition. The declaration
is used to describe the variable that is externally defined.
If a declaration for an identifier already exists at file scope, any extern
declaration of the same identifier found within a block refers to that same
object. If no other declaration for the identifier exists at file scope, the
identifier has external linkage.
An extern declaration can appear outside a function or at the beginning of a
block. If the declaration describes a function or appears outside a function
and describes an object with external linkage, the keyword extern is optional.
C++ Note: In C++, an extern declaration cannot appear in class scope.
You can initialize any object with the extern storage class specifier at file
scope. You can initialize an extern object with an initializer that must
either:
Appear as part of the definition and the initial value must be described
by a constant expression.
OR
Reduce to the address of a previously declared object with static storage
duration. This object may be modified by adding or subtracting an
integral constant expression.
If you do not explicitly initialize an extern variable, its initial value is
zero of the appropriate type. Initialization of an extern object is completed
by the time the program starts running.
extern objects have static storage duration. Memory is allocated for extern
objects before the main function begins running. When the program finishes
running, the storage is freed.
Examples of extern Storage Class
Related Information
Storage Class Specifiers
File Scope Data Declarations
Function Definitions
Function Declarator
Constant Expressions
ΓòÉΓòÉΓòÉ <hidden> Examples of extern Storage Class ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program shows the linkage of extern objects and functions. The
extern object total is declared on line 12 of File 1 and on line 11 of File 2.
The definition of the external object total appears in File 3. The extern
function tally is defined in File 2. The function tally can be in the same file
as main or in a different file. Because main precedes these definitions and
main uses both total and tally, main declares tally on line 11 and total on
line 12.
File 1
************************************************************************/
1 /**************************************************************
2 ** The program receives the price of an item, adds the **
3 ** tax, and prints the total cost of the item. **
5 **************************************************************/
6
7 #include <stdio.h>
8
9 int main(void)
10 { /* begin main */
11 void tally(void); /* declaration of function tally */
12 extern float total; /* first declaration of total */
13
14 printf("Enter the purchase amount: \n");
15 tally();
16 printf("\nWith tax, the total is: %.2f\n", total);
17
18 return(0);
19 } /* end main */
/************************************************************************
*
File 2
*
************************************************************************/
1 /**************************************************************
2 ** This file defines the function tally **
3 **************************************************************/
4 #include <stdio.h>
6 #define tax_rate 0.05
7
8 void tally(void)
9 { /* begin tally */
10 float tax;
11 extern float total; /* second declaration of total */
12
13 scanf("%f", &total);
14 tax = tax_rate * total;
15 total += tax;
16 } /* end tally */
/************************************************************************
*
File 3
*
************************************************************************/
1 float total;
/************************************************************************
*
When this program is run, interaction with it could produce:
Output Enter the purchase amount:
Input 99.95
Output With tax, the total is: 104.95
The following program shows extern variables used by two functions. Because
both functions main and sort can access and change the values of the extern
variables string and length, main does not have to pass parameters to sort.
*
************************************************************************/
/*****************************************************************
** Sorted string program -- this example shows extern **
** used by two functions **
*****************************************************************/
#include <stdio.h>
#include <string.h>
char string[75];
int length;
int main(void)
{
void sort(void);
printf("Enter letters:\n");
scanf("%s", string);
length = strlen(string);
sort();
printf("The sorted string is: %s\n", string);
return(0);
}
void sort(void)
{
int gap, i, j, temp;
for (gap = length / 2; gap > 0; gap /= 2)
for (i = gap; i < length; i++)
for (j = i - gap;
j >= 0 && string[j] > string[j + gap];
j -= gap)
{
temp = string[j];
string[j] = string[j + gap];
string[j + gap] = temp;
}
}
/************************************************************************
*
When this program is run, interaction with it could produce:
Output Enter letters:
Input zyfab
Output The sorted string is: abfyz
The following program shows a static variable var1, which is defined at file
scope and then declared with the storage class specifier extern. The second
declaration refers to the first definition of var1 and so it has internal
linkage.
static int var1;
.
.
.
extern int var1;
*
************************************************************************/
ΓòÉΓòÉΓòÉ 4.5.3. register Storage Class Specifier ΓòÉΓòÉΓòÉ
The register storage class specifier indicates to the compiler that a heavily
used variable (such as a loop control variable) within a block scope data
definition or a parameter declaration should be allocated a register to
minimize access time.
It is equivalent to the auto storage class except that the compiler places the
object, if possible, into a machine register for faster access.
Note: Because the VisualAge C++ compiler optimizes register use, it ignores
the register keyword.
Most heavily used entities are generated by the compiler itself; therefore,
register variables are given no special priority for placement in machine
registers. The register storage class keyword is required in a data definition
and in a parameter declaration that describes an object having the register
storage class. An object having the register storage class specifier must be
defined within a block or declared as a parameter to a function.
You can initialize any register object except parameters. If you do not
initialize an automatic object, its value is indeterminate. If you provide an
initial value, the expression representing the initial value can be any valid C
or C++ expression. For structure and union members, the initial value must be a
valid constant expression if an initializer list is used. The object is then
set to that initial value each time the program block that contains the
object's definition is entered.
Objects with the register storage class specifier have automatic storage
duration. Each time a block is entered, storage for register objects defined in
that block are made available. When the block is exited, the objects are no
longer available for use.
If a register object is defined within a function that is recursively invoked,
the memory is allocated for the variable at each invocation of the block.
The register storage class specifier indicates that the object is heavily used
and indicates to the compiler that the value of the object should reside in a
machine register. Because of the limited size and number of registers available
on most systems, few variables can actually be put in registers.
If the compiler does not allocate a machine register for a register object, the
object is treated as having the storage class specifier auto.
Using register definitions for variables that are heavily used may make your
object files smaller and make them run faster. In object code, a reference to a
register can require less code and time than a reference to memory.
Restrictions
You cannot use the register storage class specifier in file scope data
declarations.
C++ Note:
In C programs, you cannot apply the address (&) operator to register variables.
However, C++ lets you take the address of an object with the register storage
class. For example:
register i;
int* b = &i; // valid in C++, but not in C
Related Information
Storage Class Specifiers
Block Scope Data Declarations
auto Storage Class Specifier
Address &
ΓòÉΓòÉΓòÉ 4.5.4. static Storage Class Specifier ΓòÉΓòÉΓòÉ
The static storage class specifier lets you define objects with static storage
duration and internal linkage, or to define functions with internal linkage.
An object having the static storage class specifier can be defined within a
block or at file scope. If the definition occurs within a block, the object has
no linkage. If the definition occurs at file scope, the object has internal
linkage.
You can initialize any static object with a constant expression or an
expression that reduces to the address of a previously declared extern or
static object, possibly modified by a constant expression. If you do not
provide an initial value, the object receives the value of zero of the
appropriate type.
Storage is allocated at compile time for static variables that are initialized.
Uninitialized static variables are mapped at compile time and initialized to 0
(zero) at load time. This storage is freed when the program finishes running.
Beyond this, the language does not define the order of initialization of
objects from different files.
Use static variables to declare objects that retain their value from one
execution of a block to the next execution of that block. The static storage
class specifier keeps the variable from being reinitialized each time the block
where the variable is defined runs. For example:
static float rate = 10.5;
Initialization of a static array is performed only once at compile time. The
following examples show the initialization of an array of characters and an
array of integers:
static char message[] = "startup completed";
static int integers[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
The static storage class specifier causes the variable to be visible only in
the file where it is declared. Files, therefore, cannot access file scope
static variables declared in other files.
C++ Note: If a local static variable is a class object with constructors and
destructors, the object is constructed when control passes through its
definition for the first time. If a local class object is created by a
constructor, its destructor is called immediately before or as part of the
calls of the atexit function.
You cannot declare a static function at block scope.
Examples of static Storage Class
Related Information
Storage Class Specifiers
Block Scope Data Declarations
File Scope Data Declarations
Function Definitions
Function Declarator
ΓòÉΓòÉΓòÉ <hidden> Examples of static Storage Class ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program shows the linkage of static identifiers at file scope.
This program uses two different external static identifiers named stat_var. The
first definition occurs in file 1. The second definition occurs in file 2. The
main function references the object defined in file 1. The var_print function
references the object defined in file 2:
File 1
*
************************************************************************/
/************************************************************************
** Program to illustrate file scope static variables **
************************************************************************/
#include <stdio.h>
extern void var_print(void);
static stat_var = 1;
int main(void)
{
printf("file1 stat_var = %d\n", stat_var);
var_print();
printf("FILE1 stat_var = %d\n", stat_var);
return(0);
}
/************************************************************************
*
File 2
*
************************************************************************/
/************************************************************************
** This file contains the second definition of stat_var **
************************************************************************/
#include <stdio.h>
static int stat_var = 2;
void var_print(void)
{
printf("file2 stat_var = %d\n", stat_var);
}
/************************************************************************
*
This program produces the following output:
file1 stat_var = 1
file2 stat_var = 2
FILE1 stat_var = 1
The following program shows the linkage of static identifiers with block scope.
The function test defines the static variable stat_var, which retains its
storage throughout the program, even though test is the only function that can
refer to stat_var.
*
************************************************************************/
/************************************************************************
** Program to illustrate block scope static variables **
************************************************************************/
#include <stdio.h>
int main(void)
{
void test(void);
int counter;
for (counter = 1; counter <= 4; ++counter)
test();
return(0);
}
void test(void)
{
static int stat_var = 0;
auto int auto_var = 0;
stat_var++;
auto_var++;
printf("stat_var = %d auto_var = %d\n", stat_var, auto_var);
}
/************************************************************************
*
This program produces the following output:
stat_var = 1 auto_var = 1
stat_var = 2 auto_var = 1
stat_var = 3 auto_var = 1
stat_var = 4 auto_var = 1
*
************************************************************************/
ΓòÉΓòÉΓòÉ 4.6. Function Specifiers ΓòÉΓòÉΓòÉ
The function specifiers inline and virtual are used only in function
declarations, which are described in Function Declarations.
The function specifier inline is used to make a suggestion to the compiler to
incorporate the code of a function into the code at the point of the call. For
more information, see C++ Inline Functions.
The function specifier virtual can only be used in nonstatic member function
declarations. For more information, see Virtual Functions.
ΓòÉΓòÉΓòÉ 4.7. References ΓòÉΓòÉΓòÉ
A C++ reference is an alias or an alternative name for an object. All
operations applied to a reference act on the object the reference refers to.
The address of a reference is the address of the aliased object.
A reference type is defined by placing the & after the type specifier. You must
initialize all references except function parameters when they are defined.
Because arguments of a function are passed by value, a function call does not
modify the actual values of the arguments. If a function needs to modify the
actual value of an argument, the argument must be passed by reference (as
opposed to being passed by value). Passing arguments by reference can be done
using either references or pointers. In C++, this is accomplished
transparently. Unlike C, C++ does not force you to use pointers if you want to
pass arguments by reference. For example:
int f(int&);
void main()
{
extern int i;
f(i);
}
You cannot tell from the function call f(i) that the argument is being passed
by reference.
References to NULL are not allowed.
Additional information is provided on Initializing References.
Related Information
Passing Arguments by Reference
Pointers
Declarators
Initializers
ΓòÉΓòÉΓòÉ <hidden> Initializing References ΓòÉΓòÉΓòÉ
The object that you use to initialize a reference must be of the same type as
the reference, or it must be of a type that is convertible to the reference
type. If you initialize a reference to a constant using an object that requires
conversion, a temporary object is created. In the following example, a
temporary object of type float is created:
int i;
const float& f = i; // reference to a constant float
Attempting to initialize a nonconstant reference with an object that requires a
conversion is an error.
Once a reference has been initialized, it cannot be modified to refer to
another object. For example:
int num1 = 10;
int num2 = 20;
int &RefOne = num1; // valid
int &RefOne = num2; // error, two definitions of RefOne
RefOne = num2; // assign num2 to num1
int &RefTwo; // error, uninitialized reference
int &RefTwo = num2; // valid
Note that the initialization of a reference is not the same as an assignment to
a reference. Initialization operates on the actual reference by initializing
the reference with the object it is an alias for. Assignment operates through
the reference on the object referred to.
A reference can be declared without an initializer:
When it is used in an argument declaration
In the declaration of a return type for a function call
In the declaration of class member within its class declaration
When the extern specifier is explicitly used.
You cannot have references to references, references to bit fields, arrays of
references, or pointers to references. Additional information is provided on
Initializing References.
Related Information
Passing Arguments by Reference
Pointers
Declarators
Initializers
Temporary Objects
ΓòÉΓòÉΓòÉ 4.8. Initializers ΓòÉΓòÉΓòÉ
An initializer is an optional part of a data declaration that specifies an
initial value of a data object.
Syntax of an Initializer
The initializer consists of the = symbol followed by an initial expression or a
braced list of initial expressions separated by commas. The number of
initializers must not be more than the number of elements to be initialized. An
initializer list with fewer initializers than elements, can end with a comma,
indicating that the rest of the uninitialized elements are initialized to zero.
The initial expression evaluates to the first value of the data object.
To assign a value to a scalar object, use the simple initializer: = expression.
For example, the following data definition uses the initializer = 3 to set the
initial value of group to 3:
int group = 3;
For unions, structures, and aggregate classes (classes with no constructors,
base classes, virtual functions, or private or protected members), the set of
initial expressions must be enclosed in { } (braces) unless the initializer is
a string literal.
If the initializer of a character string is a string literal, the { } are
optional. Individual expressions must be separated by commas, and groups of
expressions can be enclosed in braces and separated by commas.
In an array, structure, or union initialized using a brace-enclosed initializer
list, any members or subscripts that are not initialized are implicitly
initialized to zero of the appropriate type.
The initialization properties of each data type are described in the section
for that data type.
Note:
1. An initializer of the form (expression) can be used to initialize
fundamental types in C++. For example, the following two initializations
are identical:
int group = 3;
int group(3);
2. You can also use the (expression) form to initialize C++ classes. For
more information on initializing classes, see Initialization by
Constructor.
3. You can initialize variables at file scope with nonconstant expressions.
This is not allowed in ISO/ANSI C.
4. If your code jumps over declarations that contain initializations, the
compiler generates an error. For example, the following code is not valid
in C++:
goto skiplabel; // error - jumped over declaration
int i = 3; // and initialization of i
skiplabel: i = 4;
5. You can initialize classes in external, static, and automatic
definitions. The initializer contains an = (equal sign) followed by a
brace-enclosed, comma-separated list of values. You do not need to
initialize all members of a class.
In the following example, only the first eight elements of the array grid are
explicitly initialized. The remaining four elements that are not explicitly
initialized are initialized as if they were explicitly initialized to zero.
static short grid[3] [4] = {0, 0, 0, 1, 0, 0, 1, 1};
The initial values of grid are:
Element Value
grid[0][0] 0
grid[0][1] 0
grid[0][2] 0
grid[0][3] 1
grid[1][0] 0
grid[1][1] 0
grid[1][2] 1
grid[1][3] 1
grid[2][0] 0
grid[2][1] 0
grid[2][2] 0
grid[2][3] 0
Related Information
Block Scope Data Declarations
File Scope Data Declarations
Arrays
Characters
Enumerations
Floating-Point Variables
Integer Variables
Pointers
Structures
Unions
ΓòÉΓòÉΓòÉ <hidden> Initializer Syntax ΓòÉΓòÉΓòÉ
An initializer has the form:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇΓö¼ΓöÇ(ΓöÇΓöÇΓöÇexpressionΓöÇΓö┤ΓöÇ)ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ=ΓöÇΓöÇΓö¼ΓöÇexpressionΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ
Γöé ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇ{ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇexpressionΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ}ΓöÇΓöÿ
Γöé ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé Γöé
Γöé Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓöÇexpressionΓöÇΓö┤ΓöÇΓöÇ}ΓöÇΓö┤ΓöÇΓöÿ
The form (expression) is allowed in C++ only.
ΓòÉΓòÉΓòÉ 4.9. Type Specifiers ΓòÉΓòÉΓòÉ
Type specifiers indicate the type of the object or function being declared. The
fundamental data types are:
Characters
Floating-Point Variables
Integer Variables
Enumerations
void Type
From these types, you can derive:
Pointers
Arrays
Structures
Unions
Functions
The integral types are char, wchar_t (C++ only), and int of all sizes.
Floating-point numbers can have types float, double, or long double. Integral
and floating-point types are collectively called arithmetic types. In C++
only, you can also derive the following:
References
Classes
Pointers to Members
In C++, enumerations are not an integral type, but they can be subject to
integral promotion, as described in Integral Promotions.
You can give names to both fundamental and derived types by using the typedef
specifier.
ΓòÉΓòÉΓòÉ 4.9.1. Characters ΓòÉΓòÉΓòÉ
There are three character data types: char, signed char, and unsigned char.
These three data types are not compatible.
The character data types provide enough storage to hold any member of the
character set used at run time. The amount of storage allocated for a char is
implementation-dependent. The VisualAge C++ compiler represents a character by
8 bits, as defined in the CHAR_BIT macro in the <limits.h> header.
The default character type behaves like an unsigned char. To change this
default, use #pragma chars or the /J compiler option.
If it does not matter whether a char data object is signed or unsigned, you can
declare the object as having the data type char; otherwise, explicitly declare
signed char or unsigned char. When a char (signed or unsigned) is widened to an
int, its value is preserved.
To declare a data object having a character type, use a char type specifier.
The declarator for a simple character declaration is an identifier. You can
initialize a simple character with a character constant or with an expression
that evaluates to an integer.
Use the char specifier in variable definitions to define such variables as:
arrays of characters, pointers to characters, and arrays of pointers to
characters. Use signed char or unsigned char to declare numeric variables that
occupy a single byte.
C++ Note: For the purposes of distinguishing overloaded functions, a C++ char
is a distinct type from signed char and unsigned char.
Examples of Character Data Types
Related Information
Arrays
Pointers
Character Constants
Assignment Expressions
Declarators
Initializers
ΓòÉΓòÉΓòÉ <hidden> Examples of Character Data Types ΓòÉΓòÉΓòÉ
The following example defines the identifier end_of_string as a constant object
of type char having the initial value \0 (the null character):
const char end_of_string = '\0';
The following example defines the unsigned char variable switches as having the
initial value 3:
unsigned char switches = 3;
The following example defines string_pointer as a pointer to a character:
char *string_pointer;
The following example defines name as a pointer to a character. After
initialization, name points to the first letter in the character string
"Johnny":
char *name = "Johnny";
The following example defines a one-dimensional array of pointers to
characters. The array has three elements. Initially they are a pointer to the
string "Venus", a pointer to "Jupiter", and a pointer to "Saturn":
static char *planets[ ] = { "Venus", "Jupiter", "Saturn" };
ΓòÉΓòÉΓòÉ 4.9.2. Floating-Point Variables ΓòÉΓòÉΓòÉ
There are three types of floating-point variables: float, double, and long
double.
The amount of storage allocated for a float, a double, or a long double is
implementation-dependent. On all compilers, the storage size of a float
variable is less than or equal to the storage size of a double variable.
The VisualAge C++ compiler allocates the following storage for floating-point
types:
4 bytes for a float
8 bytes for a double
16 bytes for a long double, of which only the first 10 bytes are
significant.
For more information about compiler options and the VisualAge C++
implementation of floating-point types, see the IBM VisualAge C++ for OS/2
User's Guide and Reference.
To declare a data object having a floating-point type, use the float
specifier.
The declarator for a simple floating-point declaration is an identifier.
Initialize a simple floating-point variable with a float constant or with a
variable or expression that evaluates to an integer or floating-point number.
The storage class of a variable determines how you initialize the variable.
Examples of Floating-Point Data Types
Related Information
Floating-Point Constants
<float.h>
Assignment Expressions
Integer Variables
Declarators
Initializers
ΓòÉΓòÉΓòÉ <hidden> Examples of Floating-Point Data Types ΓòÉΓòÉΓòÉ
The following example defines the identifier pi as an object of type double:
double pi;
The following example defines the float variable real_number with the initial
value 100.55:
static float real_number = 100.55f;
The following example defines the float variable float_var with the initial
value 0.0143:
float float_var = 1.43e-2f;
The following example declares the long double variable maximum:
extern long double maximum;
The following example defines the array table with 20 elements of type double:
double table[20];
ΓòÉΓòÉΓòÉ 4.9.3. Integer Variables ΓòÉΓòÉΓòÉ
There are six categories of integer variables:
short int or short or signed short int or signed short
signed int or int
long int or long or signed long int or signed long
unsigned short int or unsigned short
unsigned or unsigned int
unsigned long int or unsigned long
The default integer type for a bit field is unsigned.
The amount of storage allocated for an int, a short, or a long is
implementation-dependent.
Two sizes of integer data types are provided. Objects having type short are 2
bytes of storage long. Objects having type long are 4 bytes of storage long.
An int represents the most efficient data storage size on the system (the
word-size of the machine) and receives 4 bytes of storage.
For more information about the VisualAge C++ implementation of integer types,
see the IBM VisualAge C++ for OS/2 User's Guide and Reference.
The unsigned prefix indicates that the object is a nonnegative integer. Each
unsigned type provides the same size storage as its signed equivalent. For
example, int reserves the same storage as unsigned int. Because a signed type
reserves a sign bit, an unsigned type can hold a larger positive integer than
the equivalent signed type.
To declare a data object having an integer data type, use an int type
specifier.
The declarator for a simple integer definition or declaration is an
identifier. You can initialize a simple integer definition with an integer
constant or with an expression that evaluates to a value that can be assigned
to an integer. The storage class of a variable determines how you can
initialize the variable.
C++ Note: When the arguments in overloaded functions and overloaded operators
are integer types, two integer types that both come from the same group are
not treated as distinct types. For example, you cannot overload an int
argument against a signed int argument. Overloading and argument matching is
described in C++ Overloading.
Examples of Integer Data Types
Related Information
Integer Constants
Decimal Constants
Octal Constants
Hexadecimal Constants
Declarators
Initializers
ΓòÉΓòÉΓòÉ <hidden> Examples of Integer Data Types ΓòÉΓòÉΓòÉ
The following example defines the short int variable flag:
short int flag;
The following example defines the int variable result:
int result;
The following example defines the unsigned long int variable ss_number as
having the initial value 438888834:
unsigned long ss_number = 438888834ul;
The following example defines the identifier sum as an object of type int. The
initial value of sum is the result of the expression a + b:
extern int a, b;
auto sum = a + b;
ΓòÉΓòÉΓòÉ 4.9.4. Enumerations ΓòÉΓòÉΓòÉ
An enumeration data type represents a set of values that you declare. You can
define an enumeration data type and all variables that have that enumeration
type in one statement, or you can declare an enumeration type separately from
the definition of variables of that type. The identifier associated with the
data type (not an object) is called an enumeration tag.
C++ Note: In C, an enumeration has an implementation-defined integral type.
This restriction does not apply to C++. In C++, an enumeration has a distinct
type that does not have to be integral.
An enumeration type declaration contains the enum keyword followed by an
optional identifier (the enumeration tag) and a brace-enclosed list of
enumerators. Commas separate each enumerator.
Syntax of an Enumeration
The keyword enum, followed by the identifier, names the data type (like the tag
on a struct data type). The list of enumerators provides the data type with a
set of values.
C++ Note: In C, each enumerator represents an integer value. In C++, each
enumerator represents a value that can be converted to an integral value.
To conserve space, enumerations may be stored in spaces smaller than that of an
int. By default, the type of the enum variable is the size of the smallest
integral type that can contain all enumerator values. You can change the
default using the /Su option, described in the IBM VisualAge C++ for OS/2
User's Guide and Reference.
When you define an enumeration data type, you specify a set of identifiers that
the data type represents. Each identifier in this set is an enumeration
constant.
The value of the constant is determined in the following way:
1. An equal sign (=) and a constant expression after the enumeration
constant gives an explicit value to the constant. The identifier
represents the value of the constant expression.
2. If no explicit value is assigned, the leftmost constant in the list
receives the value zero (0).
3. Identifiers with no explicitly assigned values receive the integer value
that is one greater than the value represented by the previous
identifier.
In C, enumeration constants have type int.
In C++, each enumeration constant has a value that can be promoted to a signed
or unsigned integer value and a distinct type that does not have to be
integral. Use an enumeration constant anywhere an integer constant is allowed,
or for C++, anywhere a value of the enumeration type is allowed.
Each enumeration constant must be unique within the scope in which the
enumeration is defined. It is possible to associate the same integer with two
different enumeration constants.
Additional information is provided on:
Defining Enumeration Variables
Defining Enumeration Types and Objects
Example Program Using Enumeration Types
Examples of Enumeration Types and Constants
Related Information
Constant Expressions
Identifiers
Declarators
Initializers
ΓòÉΓòÉΓòÉ <hidden> Enumeration Syntax ΓòÉΓòÉΓòÉ
An enumeration type declaration has the form:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇenumΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓöÇenumeratorΓöÇΓö┤ΓöÇΓöÇ}ΓöÇΓöÇ;ΓöÇΓöÇ><
ΓööΓöÇidentifierΓöÇΓöÿ
An enumerator has the form:
>>ΓöÇΓöÇidentifierΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ=ΓöÇΓöÇintegral_constant_expressionΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Examples of Enumeration Types and Constants ΓòÉΓòÉΓòÉ
In the following example, the declarations of average on line 4 and of poor on
line 5 cause compiler error messages:
1 func()
2 {
3 enum score { poor, average, good };
4 enum rating { below, average, above };
5 int poor;
6 }
The following data type declarations list oats, wheat, barley, corn, and rice
as enumeration constants. The number under each constant shows the integer
value.
enum grain { oats, wheat, barley, corn, rice };
/* 0 1 2 3 4 */
enum grain { oats=1, wheat, barley, corn, rice };
/* 1 2 3 4 5 */
enum grain { oats, wheat=10, barley, corn=20, rice };
/* 0 10 11 20 21 */
It is possible to associate the same integer with two different enumeration
constants. For example, the following definition is valid. The identifiers
suspend and hold have the same integer value.
enum status { run, clear=5, suspend, resume, hold=6 };
/* 0 5 6 7 6 */
The following example is a different declaration of the enumeration tag status:
enum status { run, create, clear=5, suspend };
/* 0 1 5 6 */
ΓòÉΓòÉΓòÉ 4.9.4.1. Defining Enumeration Variables ΓòÉΓòÉΓòÉ
An enumeration variable definition contains an optional storage class
specifier, a type specifier, a declarator, and an optional initializer. The
type specifier contains the keyword enum followed by the name of the
enumeration data type. You must declare the enumeration data type before you
can define a variable having that type.
The initializer for an enumeration variable contains the = symbol followed by
an expression.
In C, the initializer expression must evaluate to an int value. In C++, the
initializer must be have the same type as the associated enumeration type
The first line of the following example declares the enumeration tag grain. The
second line defines the variable g_food and gives g_food the initial value of
barley (2).
enum grain { oats, wheat, barley, corn, rice };
enum grain g_food = barley;
In C, the type specifier enum grain indicates that the value of g_food is a
member of the enumerated data type grain. In C++, the value of g_food has the
enumerated data type grain.
C++ also makes the enum keyword optional in an initialization expression like
the one in the second line of the preceding example. For example, both of the
following statements are valid C++ code:
enum grain g_food = barley;
grain cob_food = corn;
ΓòÉΓòÉΓòÉ 4.9.4.2. Defining Enumeration Types and Objects ΓòÉΓòÉΓòÉ
You can define a type and a variable in one statement by using a declarator and
an optional initializer after the type definition. To specify a storage class
specifier for the variable, you must put the storage class specifier at the
beginning of the declaration. For example:
register enum score { poor=1, average, good } rating = good;
C++ also lets you put the storage class immediately before the declarator. For
example:
enum score { poor=1, average, good } register rating = good;
Either of these examples is equivalent to the following two declarations:
enum score { poor=1, average, good };
register enum score rating = good;
Both examples define the enumeration data type score and the variable rating.
rating has the storage class specifier register, the data type enum score, and
the initial value good.
Combining a data type definition with the definitions of all variables having
that data type lets you leave the data type unnamed. For example:
enum { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday,
Saturday } weekday;
defines the variable weekday, which can be assigned any of the specified
enumeration constants.
ΓòÉΓòÉΓòÉ 4.9.4.3. Example Program Using Enumerations ΓòÉΓòÉΓòÉ
/**********************************************************************************
*
The following program receives an integer as input. The output is a sentence
that gives the French name for the weekday that is associated with the integer.
If the integer is not associated with a weekday, the program prints "C'est le
mauvais jour."
*
************************************************************************/
/**
** Example program using enumerations
**/
#include <stdio.h>
enum days {
Monday=1, Tuesday, Wednesday,
Thursday, Friday, Saturday, Sunday
} weekday;
void french(enum days);
int main(void)
{
int num;
printf("Enter an integer for the day of the week. "
"Mon=1,...,Sun=7\n");
scanf("%d", &num);
weekday=num;
french(weekday);
return(0);
}
void french(enum days weekday)
{
switch (weekday)
{
case Monday:
printf("Le jour de la semaine est lundi.\n");
break;
case Tuesday:
printf("Le jour de la semaine est mardi.\n");
break;
case Wednesday:
printf("Le jour de la semaine est mercredi.\n");
break;
case Thursday:
printf("Le jour de la semaine est jeudi.\n");
break;
case Friday:
printf("Le jour de la semaine est vendredi.\n");
break;
case Saturday:
printf("Le jour de la semaine est samedi.\n");
break;
case Sunday:
printf("Le jour de la semaine est dimanche.\n");
break;
default:
printf("C'est le mauvais jour.\n");
}
}
ΓòÉΓòÉΓòÉ 4.9.5. Pointers ΓòÉΓòÉΓòÉ
A pointer type variable holds the address of a data object or a function. A
pointer can refer to an object of any one data type except to a bit field or a
reference. Additionally, in C, a pointer cannot point to an object with the
register storage class. Some common uses for pointers are:
To access dynamic data structures such as linked lists, trees, and
queues.
To access elements of an array or members of a structure or C++ class.
To access an array of characters as a string.
To pass the address of a variable to a function. (In C++, you can also
use a reference to do this.) By referencing a variable through its
address, a function can change the contents of that variable. Calling
Functions and Passing Arguments describes passing arguments by reference.
The following example declares pcoat as a pointer to an object having type
long:
extern long *pcoat;
If the keyword volatile appears before the *, the declarator describes a
pointer to a volatile object. If the keyword volatile comes between the * and
the identifier, the declarator describes a volatile pointer. The keyword const
operates in the same manner as the volatile keyword.
Examples of Pointer Declarations
Additional information is provided on:
Assigning Pointers
Initializing Pointers
Restrictions on Pointers
Using Pointers
Pointer Arithmetic
Example Program Using Pointers
Related Information
Address &
Indirection *
_Seg16 Type Qualifier
References
Declarators
volatile and const Qualifiers
Initializers
ΓòÉΓòÉΓòÉ <hidden> Examples of Pointer Declarations ΓòÉΓòÉΓòÉ
In the following example, pvolt is a constant pointer to an object having type
short:
short * const pvolt;
The following example declares pnut as a pointer to an int object having the
volatile qualifier:
extern int volatile *pnut;
The following example defines psoup as a volatile pointer to an object having
type float:
float * volatile psoup;
The following example defines pfowl as a pointer to an enumeration object of
type bird:
enum bird *pfowl;
The next example declares pvish as a pointer to a function that takes no
parameters and returns a char object:
char (*pvish)(void);
ΓòÉΓòÉΓòÉ 4.9.5.1. Assigning Pointers ΓòÉΓòÉΓòÉ
When you use pointers in an assignment operation, you must ensure that the
types of the pointers in the operation are compatible.
The following example shows compatible declarations for the assignment
operation:
float subtotal;
float * sub_ptr;
.
.
.
sub_ptr = &subtotal;
printf("The subtotal is %f\n", *sub_ptr);
The next example shows incompatible declarations for the assignment operation:
double league;
int * minor;
.
.
.
minor = &league; /* error */
ΓòÉΓòÉΓòÉ 4.9.5.2. Initializing Pointers ΓòÉΓòÉΓòÉ
The initializer is an = (equal sign) followed by the expression that represents
the address that the pointer is to contain. The following example defines the
variables time and speed as having type double and amount as having type
pointer to a double. The pointer amount is initialized to point to total:
double total, speed, *amount = &total;
The compiler converts an unsubscripted array name to a pointer to the first
element in the array. You can assign the address of the first element of an
array to a pointer by specifying the name of the array. The following two sets
of definitions are equivalent. Both define the pointer student and initialize
student to the address of the first element in section:
int section[80];
int *student = section;
is equivalent to:
int section[80];
int *student = §ion[0];
You can assign the address of the first character in a string constant to a
pointer by specifying the string constant in the initializer.
The following example defines the pointer variable string and the string
constant "abcd". The pointer string is initialized to point to the character a
in the string "abcd".
char *string = "abcd";
The following example defines weekdays as an array of pointers to string
constants. Each element points to a different string. The pointer weekdays[2],
for example, points to the string "Tuesday".
static char *weekdays[ ] =
{
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
A pointer can also be initialized to NULL using any integer constant expression
that evaluates to 0, for example char * a=0;. Such a pointer is a NULL pointer.
It does not point to any object.
ΓòÉΓòÉΓòÉ 4.9.5.3. Restrictions on Pointers ΓòÉΓòÉΓòÉ
You cannot use pointers to reference bit fields or objects having the register
storage class specifier.
A pointer to a packed structure or union is incompatible with a pointer to a
corresponding nonpacked structure or union because packed and nonpacked objects
have different memory layouts. As a result, comparisons and assignments between
pointers to packed and nonpacked objects are not valid.
You can, however, perform these assignments and comparisons with type casts. In
the following example:
int main(void)
{
_Packed struct ss *ps1;
struct ss *ps2;
.
.
.
ps1 = (_Packed struct ss *)ps2;
.
.
.
}
the cast operation lets you compare the two pointers, but you must be aware
that ps1 still points to a nonpacked object. For the VisualAge C++ compiler,
all pointers that are shared between 32-bit and 16-bit code must be declared
with the _Seg16 type qualifier. This includes pointers that are indirectly
passed to 16-bit code, such as pointers in structures and pointers that are
referenced by pointers directly passed to 16-bit code.
For more information, see _Seg16 Type Qualifier and the chapter on "Calling
Between 32-Bit and 16-Bit Code" in the IBM VisualAge C++ for OS/2 Programming
Guide.
ΓòÉΓòÉΓòÉ 4.9.5.4. Using Pointers ΓòÉΓòÉΓòÉ
Two operators are commonly used in working with pointers, the address (&)
operator and the indirection (*) operator. You can use the & operator to refer
to the address of an object. For example, the following statement assigns the
address of x to the variable p_to_x. The variable p_to_x has been defined as a
pointer.
int x, *p_to_x;
p_to_x = &x;
The * (indirection) operator lets you access the value of the object a pointer
refers to. The following statement assigns to y the value of the object that
p_to_x points to:
float y, *p_to_x;
.
.
.
y = *p_to_x;
The following statement assigns the value of y to the variable that *p_to_x
references:
char y ,
*p_to_x,
.
.
.
*p_to_x = y;
ΓòÉΓòÉΓòÉ 4.9.5.5. Pointer Arithmetic ΓòÉΓòÉΓòÉ
You can perform a limited number of arithmetic operations on pointers. These
operations are:
Increment and decrement
Addition and subtraction
Comparison
Assignment.
The increment (++) operator increases the value of a pointer by the size of
the data object the pointer refers to. For example, if the pointer refers to
the second element in an array, the ++ makes the pointer refer to the third
element in the array.
The decrement (--) operator decreases the value of a pointer by the size of
the data object the pointer refers to. For example, if the pointer refers to
the second element in an array, the -- makes the pointer refer to the first
element in the array.
You can add a pointer to an integer, but you cannot add a pointer to a
pointer.
If the pointer p points to the first element in an array, the following
expression causes the pointer to point to the third element in the same array:
p = p + 2;
If you have two pointers that point to the same array, you can subtract one
pointer from the other. This operation yields the number of elements in the
array that separate the two addresses that the pointers refer to.
You can compare two pointers with the following operators: ==, !=, <, >, <=,
and >=. See Expressions and Operators for more information on these operators.
Pointer comparisons are defined only when the pointers point to elements of
the same array. Pointer comparisons using the == and != operators can be
performed even when the pointers point to elements of different arrays.
You can assign to a pointer the address of a data object, the value of another
compatible pointer or the NULL pointer.
ΓòÉΓòÉΓòÉ 4.9.5.6. Example Program Using Pointers ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program contains pointer arrays:
*
************************************************************************/
/********************************************************************
** Program to search for the first occurrence of a specified **
** character string in an array of character strings. **
********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 20
#define EXIT_FAILURE 999
int main(void)
{
static char *names[ ] = { "Jim", "Amy", "Mark", "Sue", NULL };
char * find_name(char **, char *);
char new_name[SIZE], *name_pointer;
printf("Enter name to be searched.\n");
scanf("%s", new_name);
name_pointer = find_name(names, new_name);
printf("name %s%sfound\n", new_name,
(name_pointer == NULL) ? " not " : " ");
exit(EXIT_FAILURE);
} /* End of main */
/********************************************************************
** Function find_name. This function searches an array of **
** names to see if a given name already exists in the array. **
** It returns a pointer to the name or NULL if the name is **
** not found. **
** **
** char **arry is a pointer to arrays of pointers (existing names) **
** char *strng is a pointer to character array entered (new name) **
********************************************************************/
char * find_name(char **arry, char *strng)
{
for (; *arry != NULL; arry++) /* for each name */
{
if (strcmp(*arry, strng) == 0) /* if strings match */
return(*arry); /* found it! */
}
return(*arry); /* return the pointer */
} /* End of find_name */
/************************************************************************
*
Interaction with this program could produce the following sessions:
Output Enter name to be searched.
Input Mark
Output name Mark found
OR:
Output Enter name to be searched.
Input Deborah
Output name Deborah not found
*
************************************************************************/
ΓòÉΓòÉΓòÉ 4.9.6. void Type ΓòÉΓòÉΓòÉ
The void data type always represents an empty set of values. The only object
that can be declared with the type specifier void is a pointer.
When a function does not return a value, you should use void as the type
specifier in the function definition and declaration. An argument list for a
function taking no arguments is void.
You cannot declare a variable of type void, but you can explicitly convert any
expression to type void, but the resulting expression can only be used as one
of the following:
An expression statement
The left operand of a comma expression
The second or third operand in a conditional expression.
Example of void Type
Related Information
Cast Expressions
Type Specifiers
Expressions and Operators
ΓòÉΓòÉΓòÉ <hidden> Example of void Type ΓòÉΓòÉΓòÉ
/************************************************************************
*
On line 7 of the following example, the function find_max is declared as having
type void. Lines 15 through 26 contain the complete definition of find_max.
Note: The use of the sizeof operator in line 13 is a standard method of
determining the number of elements in an array.
*
************************************************************************/
1 /**
2 ** Example of void type
3 **/
4 #include <stdio.h>
5
6 /* declaration of function find_max */
7 extern void find_max(int x[ ], int j);
8
9 int main(void)
10 {
11 static int numbers[ ] = { 99, 54, -102, 89 };
12
13 find_max(numbers, (sizeof(numbers) / sizeof(numbers[0])));
14
15 return(0);
16 }
17
18 void find_max(int x[ ], int j)
19 { /* begin definition of function find_max */
20 int i, temp = x[0];
21
22 for (i = 1; i < j; i++)
23 {
24 if (x[i] > temp)
25 temp = x[i];
26 }
27 printf("max number = %d\n", temp);
28 } /* end definition of function find_max */
ΓòÉΓòÉΓòÉ 4.9.7. Arrays ΓòÉΓòÉΓòÉ
An array is an ordered group of data objects. Each object is called an element.
All elements within an array have the same data type.
Use any type specifier in an array definition or declaration. Array elements
can be of any data type, except function or, in C++, a reference. You can,
however, declare an array of pointers to functions.
The array declarator contains an identifier followed by an optional subscript
declarator. An identifier preceded by an * (asterisk) is an array of pointers.
Syntax of a Subscript Declarator
The subscript declarator describes the number of dimensions in the array and
the number of elements in each dimension. Each bracketed expression, or
subscript, describes a different dimension and must be a constant expression.
The following example defines a one-dimensional array that contains four
elements having type char:
char list[4];
The first subscript of each dimension is 0. The array list contains the
elements:
list[0]
list[1]
list[2]
list[3]
The following example defines a two-dimensional array that contains six
elements of type int:
int roster[3][2];
Multidimensional arrays are stored in row-major order. When elements are
referred to in order of increasing storage location, the last subscript varies
the fastest. For example, the elements of array roster are stored in the order:
roster[0][0]
roster[0][1]
roster[1][0]
roster[1][1]
roster[2][0]
roster[2][1]
In storage, the elements of roster would be stored as:
Γöé Γöé Γöé
Γöö ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ ΓöÇΓöÇΓöÇΓöÇΓöÇ Γö┤ ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ ΓöÇΓöÇΓöÇΓöÇΓöÇ Γö┤ ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ ΓöÇΓöÇΓöÇΓöÇΓöÇ
Γöé Γöé Γöé
roster [ 0 ] [ 0 ] roster [ 0 ] [ 1 ] roster [ 1 ] [ 0 ]
You can leave the first (and only the first) set of subscript brackets empty in
Array definitions that contain initializations
extern declarations
Parameter declarations.
In array definitions that leave the first set of subscript brackets empty, the
initializer determines the number of elements in the first dimension. In a
one-dimensional array, the number of initialized elements becomes the total
number of elements. In a multidimensional array, the initializer is compared
to the subscript declarator to determine the number of elements in the first
dimension.
An unsubscripted array name (for example, region instead of region[4])
represents a pointer whose value is the address of the first element of the
array, provided the array has previously been declared. An unsubscripted array
name with square brackets (for example, region[]) is allowed only when
declaring arrays at file scope or in the argument list of a function
declaration. In declarations, only the first dimension can be left empty; you
must specify the sizes of additional dimensions.
Whenever an array is used in a context (such as a parameter) where it cannot
be used as an array, the identifier is treated as a pointer. The two
exceptions are when an array is used as an operand of the sizeof or the
address (&) operator.
Additional information is provided on:
Initializing Arrays
Example Programs Using Arrays
Related Information
Pointers
Array Subscript [ ]
String Literals
Declarators
Initializers
Implicit Type Conversions
Implicit Type Conversions
ΓòÉΓòÉΓòÉ <hidden> Subscript Declarator Syntax ΓòÉΓòÉΓòÉ
A subscript declarator has the form:
>>ΓöÇΓöÇ[ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ]ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇconstant_expressionΓöÇΓöÿ Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇ[ΓöÇΓöÇconstant_expressionΓöÇΓöÇ]ΓöÇΓö┤ΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 4.9.7.1. Initializing Arrays ΓòÉΓòÉΓòÉ
The initializer for an array contains the = symbol followed by a
comma-separated list of constant expressions enclosed in braces ({ }). You do
not need to initialize all elements in an array. Elements that are not
initialized (in extern and static definitions only) receive the value 0 of the
appropriate type.
Note: Array initializations can be either fully braced (with braces around
each dimension) or unbraced (with only one set of braces enclosing the entire
set of initializers). Avoid placing braces around some dimensions and not
around others.
Initializing a one-dimensional character array
Initialize a one-dimensional character array by specifying:
A brace-enclosed comma-separated list of constants, each of which can be
contained in a character
A string constant. (Braces surrounding the constant are optional.)
Initializing a string constant places the null character (\0) at the end of
the string if there is room or if the array dimensions are not specified.
Examples of Initialized Arrays
The following show four different character array initializations:
static char name1[] = { 'J', 'a', 'n' };
static char name2[] = { "Jan" };
static char name3[3] = "Jan";
static char name4[4] = "Jan";
These initializations create the following elements:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ELEMENT Γöé VALUEΓöé ELEMENT Γöé VALUEΓöé ELEMENT Γöé VALUEΓöé ELEMENT Γöé VALUΓöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "name1[0]" Γöé "J" Γöé "name2[0]" Γöé "J" Γöé "name3[0]" Γöé "J" Γöé "name4[0]" Γöé "J" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "name1[1]" Γöé "a" Γöé "name2[1]" Γöé "a" Γöé "name3[1]" Γöé "a" Γöé "name4[1]" Γöé "a" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "name1[2]" Γöé "n" Γöé "name2[2]" Γöé "n" Γöé "name3[2]" Γöé "n" Γöé "name4[2]" Γöé "n" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Γöé Γöé "name2[3]" Γöé "\0" Γöé Γöé Γöé "name4[3]" Γöé "\0"Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Note that the NULL character is lost for name1[] and name3[3]. In C, a
compiler warning is issued for name3[3]. In C++, the compiler issues a severe
error for name3[3].
Initializing a multidimensional array
Initialize a multidimensional array by:
Listing the values of all elements you want to initialize, in the order
that the compiler assigns the values. The compiler assigns values by
increasing the subscript of the last dimension fastest. This form of a
multidimensional array initialization looks like a one-dimensional array
initialization. The following definition completely initializes the
array month_days:
static month_days[2][12] =
{
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
Using braces to group the values of the elements you want initialized.
You can put braces around each element, or around any nesting level of
elements. The following definition contains two elements in the first
dimension. (You can consider these elements as rows.) The initialization
contains braces around each of these two elements:
static int month_days[2][12] =
{
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};
Using use nested braces to initialize dimensions and elements in a
dimension selectively.
You cannot have more initializers than the number of elements in the array.
ΓòÉΓòÉΓòÉ <hidden> Examples of Initialized Arrays ΓòÉΓòÉΓòÉ
The following definition shows a completely initialized one-dimensional array:
static int number[3] = { 5, 7, 2 };
The array number contains the following values:
Element Value
number[0] 5
number[1] 7
number[2] 2
The following definition shows a partially initialized one-dimensional array:
static int number1[3] = { 5, 7 };
The values of number1 are:
Element Value
number1[0] 5
number1[1] 7
number1[2] 0
Instead of an expression in the subscript declarator defining the number of
elements, the following one-dimensional array definition defines one element
for each initializer specified:
static int item[ ] = { 1, 2, 3, 4, 5 };
The compiler gives item the five initialized elements:
Element Value
item[0] 1
item[1] 2
item[2] 3
item[3] 4
item[4] 5
The following definitions show character array initializations:
static char name1[ ] = { 'J', 'a', 'n' };
static char name2[ ] = { "Jan" };
static char name3[4] = "Jan";
These definitions create the following elements:
Element Value
name1[0] J
name1[1] a
name1[2] n
name2[0] J
name2[1] a
name2[2] n
name2[3] \0
name3[0] J
name3[1] a
name3[2] n
The following definition explicitly initializes six elements in a 12-element
array:
static int matrix[3][4] =
{
{1, 2},
{3, 4},
{5, 6}
};
The initial values of matrix are:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ELEMENT Γöé VALUE Γöé ELEMENT Γöé VALUE Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "matrix[0][0]" Γöé "1" Γöé "matrix[1][2]" Γöé "0" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "matrix[0][1]" Γöé "2" Γöé "matrix[1][3]" Γöé "0" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "matrix[0][2]" Γöé "0" Γöé "matrix[2][0]" Γöé "5" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "matrix[0][3]" Γöé "0" Γöé "matrix[2][1]" Γöé "6" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "matrix[1][0]" Γöé "3" Γöé "matrix[2][2]" Γöé "0" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "matrix[1][1]" Γöé "4" Γöé "matrix[2][3]" Γöé "0" Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 4.9.7.2. Example Programs Using Arrays ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program defines a floating-point array called prices.
The first for statement prints the values of the elements of prices. The second
for statement adds five percent to the value of each element of prices, and
assigns the result to total, and prints the value of total.
*
************************************************************************/
/**
** Example of one-dimensional arrays
**/
#include <stdio.h>
#define ARR_SIZE 5
int main(void)
{
static float const prices[ARR_SIZE] = { 1.41, 1.50, 3.75, 5.00, .86 };
auto float total;
int i;
for (i = 0; i < ARR_SIZE; i++)
{
printf("price = $%.2f\n", prices[i]);
}
printf("\n");
for (i = 0; i < ARR_SIZE; i++)
{
total = prices[i] * 1.05;
printf("total = $%.2f\n", total);
}
return(0);
}
/************************************************************************
*
This program produces the following output:
price = $1.41
price = $1.50
price = $3.75
price = $5.00
price = $0.86
total = $1.48
total = $1.57
total = $3.94
total = $5.25
total = $0.90
The following program defines the multidimensional array salary_tbl. A for loop
prints the values of salary_tbl.
*
************************************************************************/
/**
** Example of a multidimensional array
**/
#include <stdio.h>
#define ROW_SIZE 3
#define COLUMN_SIZE 5
int main(void)
{
static int salary_tbl[ROW_SIZE][COLUMN_SIZE] =
{
{ 500, 550, 600, 650, 700 },
{ 600, 670, 740, 810, 880 },
{ 740, 840, 940, 1040, 1140 }
};
int grade , step;
for (grade = 0; grade < ROW_SIZE; ++grade)
for (step = 0; step < COLUMN_SIZE; ++step)
{
printf("salary_tbl[%d] [%d] = %d\n", grade, step,
salary_tbl[grade] [step]);
}
return(0);
}
/************************************************************************
*
This program produces the following output:
salary_tbl[0] [0] = 500
salary_tbl[0] [1] = 550
salary_tbl[0] [2] = 600
salary_tbl[0] [3] = 650
salary_tbl[0] [4] = 700
salary_tbl[1] [0] = 600
salary_tbl[1] [1] = 670
salary_tbl[1] [2] = 740
salary_tbl[1] [3] = 810
salary_tbl[1] [4] = 880
salary_tbl[2] [0] = 740
salary_tbl[2] [1] = 840
salary_tbl[2] [2] = 940
salary_tbl[2] [3] = 1040
salary_tbl[2] [4] = 1140
*
************************************************************************/
ΓòÉΓòÉΓòÉ 4.9.8. Structures ΓòÉΓòÉΓòÉ
A structure contains an ordered group of data objects. Unlike the elements of
an array, the data objects within a structure can have varied data types. Each
data object in a structure is a member or field.
Use structures to group logically related objects. For example, to allocate
storage for the components of one address, define a number of variables for the
street name and number, the city, and so on. To allocate storage for more than
one address, group the components of each address by defining a structure data
type and as many variables as you need to have the structure data type.
In the following example, lines 1 through 7 declare the structure tag address:
1 struct address {
2 int street_no;
3 char *street_name;
4 char *city;
5 char *prov;
6 char *postal_code;
7 };
8 struct address perm_address;
9 struct address temp_address;
10 struct address *p_perm_address = &perm_address;
The variables perm_address and temp_address are instances of the structure data
type address. Both contain the members described in the declaration of address.
The pointer p_perm_address points to a structure of address and is initialized
to point to perm_address.
Refer to a member of a structure by specifying the structure variable name with
the dot operator (.) or a pointer with the arrow operator (->) and the member
name. For example, both of the following:
perm_address.prov = "Ontario";
p_perm_address -> prov = "Ontario";
assign a pointer to the string "Ontario" to the pointer prov that is in the
structure perm_address.
All references to structures must be fully qualified. In the example, you
cannot reference the fourth field by prov alone. You must reference this field
by perm_address.prov.
Structures with identical members but different names are not compatible and
cannot be assigned to each other.
Structures are not intended to conserve storage. If you need direct control of
byte mapping, use pointers.
Structure member references are described in Dot Operator . and Arrow
Operator ->
You cannot declare a structure with members of incomplete types.
A structure type declaration describes the members that are part of the
structure. It contains the struct keyword followed by an optional identifier
(the structure tag) and a brace-enclosed list of members.
Syntax of a Structure
The keyword struct followed by the identifier (tag) names the data type. If you
do not provide a tag name to the data type, you must put all variable
definitions that refer to it within the declaration of the data type.
The list of members provides the data type with a description of the values
that can be stored in the structure.
If a : (colon) and a constant expression follow the member declarator, the
member represents a bit field. A member that does not represent a bit field can
be of any data type and can have the volatile or const qualifier.
Identifiers used as structure or member names can be redefined to represent
different objects in the same scope without conflicting. You cannot use the
name of a member more than once in a structure type, but you can use the same
member name in another structure type that is defined within the same scope.
You cannot declare a structure type that contains itself as a member, but you
can declare a structure type that contains a pointer to itself as a member.
A structure variable definition contains an optional storage class keyword, the
struct keyword, a structure tag, a declarator, and an optional identifier. The
structure tag indicates the data type of the structure variable.
C++ Note: The keyword struct is optional in C++.
You can declare structures having any storage class.
Additional information is provided on:
Initializing Structures
Declaring Structure Types and Variables
Declaring and Using Bit Fields in Structures
Declaring a Packed Structure
Example Program Using Structures
Related Information
Dot Operator .
Arrow Operator ->
_Packed Qualifier
Declarators
Initializers
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Structure ΓòÉΓòÉΓòÉ
A structure declaration has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇstructΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓöÇmemberΓöÇΓöÇ;ΓöÇΓö┤ΓöÇΓöÇ}ΓöÇΓöÇ><
ΓööΓöÇidentifierΓöÇΓöÿ
A structure member has the form:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇtype_specifierΓöÇΓöÇΓöÇΓö¼ΓöÇdeclaratorΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ><
ΓööΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ:ΓöÇΓöÇconstant_expressionΓöÇΓöÿ
ΓööΓöÇ.declaratorΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 4.9.8.1. Initializing Structures ΓòÉΓòÉΓòÉ
The initializer contains an = (equal sign) followed by a brace-enclosed
comma-separated list of values. You do not have to initialize all members of a
structure.
The following definition shows a completely initialized structure:
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
static struct address perm_address =
{ 3, "Savona Dr.", "Dundas", "Ontario", "L4B 2A1"};
The values of perm_address are:
Member Value
perm_address.street_no 3
perm_address.street_name address of string "Savona Dr."
perm_address.city address of string "Dundas"
perm_address.prov address of string "Ontario"
perm_address.postal_code address of string "L4B 2A1"
The following definition shows a partially initialized structure:
struct address {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
};
struct address temp_address =
{ 44, "Knyvet Ave.", "Hamilton", "Ontario" };
The values of temp_address are:
Member Value
temp_address.street_no 44
temp_address.street_name address of string "Knyvet Ave."
temp_address.city address of string "Hamilton"
temp_address.prov address of string "Ontario"
temp_address.postal_code value depends on the storage class.
Note: The initial value of uninitialized structure members like
temp_address.postal_code depends on the storage class associated with the
member. See Storage Class Specifiers for details on the initialization of
different storage classes.
ΓòÉΓòÉΓòÉ 4.9.8.2. Declaring Structure Types and Variables ΓòÉΓòÉΓòÉ
To define a structure type and a structure variable in one statement, put a
declarator and an optional initializer after the type definition. To specify a
storage class specifier for the variable, you must put the storage class
specifier at the beginning of the statement.
For example:
static struct {
int street_no;
char *street_name;
char *city;
char *prov;
char *postal_code;
} perm_address, temp_address;
Because this example does not name the structure data type, perm_address and
temp_address are the only structure variables that will have this data type.
Putting an identifier after struct, lets you make additional variable
definitions of this data type later in the program.
The structure type (or tag) cannot have the volatile qualifier, but a member or
a structure variable can be defined as having the volatile qualifier.
For example:
static struct class1 {
char descript[20];
volatile long code;
short complete;
} volatile file1, file2;
struct class1 subfile;
This example qualifies the structures file1 and file2, and the structure member
subfile.code as volatile.
ΓòÉΓòÉΓòÉ 4.9.8.3. Declaring and Using Bit Fields ΓòÉΓòÉΓòÉ
A structure or a C++ class can contain bit fields that allow you to access
individual bits. You can use bit fields for data that requires just a few bits
of storage. A bit field declaration contains a type specifier followed by an
optional declarator, a colon, a constant expression, and a semicolon. The
constant expression specifies how many bits the field reserves. A bit field
that is declared as having a length of 0 causes the next field to be aligned on
the next integer boundary. For a _Packed structure, a bit field of length 0
causes the next field to be aligned on the next byte boundary. Bit fields with
a length of 0 must be unnamed. Unnamed bit fields cannot be referenced or
initialized.
The maximum bit field length is implementation dependent. The maximum bit field
length for the VisualAge C++ compiler is 32 bits (4 bytes, or 1 word).
For portability, do not use bit fields greater than 32 bits in size.
The following restrictions apply to bit fields. You cannot:
Define an array of bit fields
Take the address of a bit field
Have a pointer to a bitfield
Have a reference to a bit field (C++ only)
In C, you can declare a bit field as type int, signed int, or unsigned int.
Bit fields of the type int are equivalent to those of type unsigned int.
C++ Note: Unlike ISO/ANSI C, C++ bit fields can be any integral type or
enumeration type. When you assign a value that is out of range to a bit field,
the low-order bit pattern is preserved and the appropriate bits are assigned.
If a series of bit fields does not add up to the size of an int, padding can
take place. The amount of padding is determined by the alignment
characteristics of the members of the structure. In some instances, bit fields
can cross word boundaries.
The following example declares the identifier kitchen to be of type struct
on_off:
struct on_off {
unsigned light : 1;
unsigned toaster : 1;
int count; /* 4 bytes */
unsigned ac : 4;
unsigned : 4;
unsigned clock : 1;
unsigned : 0;
unsigned flag : 1;
} kitchen ;
The structure kitchen contains eight members totalling 16 bytes. The following
table describes the storage that each member occupies:
Member Name Storage Occupied
light 1 bit
toaster 1 bit
(padding - 30 bits) To next int boundary
count The size of an int
ac 4 bits
(unnamed field) 4 bits
clock 1 bit
(padding - 23 bits) To next int boundary (unnamed field)
flag 1 bit
(padding - 31 bits) To next int boundary
All references to structure fields must be fully qualified. For instance, you
cannot reference the second field by toaster. You must reference this field by
kitchen.toaster.
The following expression sets the light field to 1:
kitchen.light = 1;
When you assign to a bit field a value that is out of its range, the bit
pattern is preserved and the appropriate bits are assigned. The following
expression sets the toaster field of the kitchen structure to 0 because only
the least significant bit is assigned to the toaster field:
kitchen.toaster = 2;
ΓòÉΓòÉΓòÉ 4.9.8.4. Declaring a Packed Structure ΓòÉΓòÉΓòÉ
Data elements of a structure are stored in memory on an address boundary
specific for that data type. For example, a double value is stored in memory on
a doubleword (8-byte) boundary. Gaps may be left in memory between elements of
a structure to align elements on their natural boundaries. You can reduce the
padding of bytes within a structure by using the _Packed qualifier on the
structure declaration.
C++ Note: C++ does not support the _Packed qualifier. To change the alignment
of structures, use the #pragma pack directive or the /Sp compiler option. Both
of these methods are also supported by C.
ΓòÉΓòÉΓòÉ 4.9.8.5. Example Program Using Structures ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program finds the sum of the integer numbers in a linked list:
*
************************************************************************/
/**
** Example program illustrating structures using linked lists
**/
#include <stdio.h>
struct record {
int number;
struct record *next_num;
};
int main(void)
{
struct record name1, name2, name3;
struct record *recd_pointer = &name1;
int sum = 0;
name1.number = 144;
name2.number = 203;
name3.number = 488;
name1.next_num = &name2;
name2.next_num = &name3;
name3.next_num = NULL;
while (recd_pointer != NULL)
{
sum += recd_pointer->number;
recd_pointer = recd_pointer->next_num;
}
printf("Sum = %d\n", sum);
return(0);
}
/************************************************************************
*
The structure type record contains two members: the integer number and
next_num, which is a pointer to a structure variable of type record.
The record type variables name1, name2, and name3 are assigned the following
values:
Member Name Value
name1.number 144
name1.next_num The address of name2
name2.number 203
name2.next_num The address of name3
name3.number 488
name3.next_num NULL (Indicating the end of the linked list.)
The variable recd_pointer is a pointer to a structure of type record. It is
initialized to the address of name1 (the beginning of the linked list).
The while loop causes the linked list to be scanned until recd_pointer equals
NULL. The statement:
recd_pointer = recd_pointer->next_num;
advances the pointer to the next object in the list.
*
***********************************************************************************/
ΓòÉΓòÉΓòÉ 4.9.9. Unions ΓòÉΓòÉΓòÉ
A union is an object that can hold any one of a set of named members. The
members of the named set can be of any data type. Members are overlaid in
storage.
The storage allocated for a union is the storage required for the largest
member of the union (plus any padding that is required so that the union will
end at a natural boundary of its strictest member).
C++ Notes:
In C++, a union can have member functions, including constructors and
destructors, but not virtual member functions. A union cannot be used as a base
class and cannot be derived from a base class.
A C++ union member cannot be a class object that has a constructor, destructor,
or overloaded copy assignment operator. In C++, a member of a union cannot be
declared with the keyword static.
A union type declaration contains the union keyword followed by an identifier
(optional) and a brace-enclosed list of members.
Syntax of a Union
The identifier is a tag given to the union specified by the member list. If you
specify a tag, any subsequent declaration of the union (in the same scope) can
be made by declaring the tag and omitting the member list. If you do not
specify a tag, you must put all variable definitions that refer to that union
within the statement that defines the data type.
The list of members provides the data type with a description of the objects
that can be stored in the union.
You can reference one of the possible members of a union the same way as
referencing a member of a structure.
For example:
union {
char birthday[9];
int age;
float weight;
} people;
people.birthday[0] = '\n';
assigns '\n' to the first element in the character array birthday, a member of
the union people.
A union can represent only one of its members at a time. In the example, the
union people contains either age, birthday, or weight but never more than one
of these. The printf statement in the following example does not give the
correct result because people.age replaces the value assigned to
people.birthday in the first line:
1 people.birthday = "03/06/56";
2 people.age = 38;
3 printf("%s\n", people.birthday);
Examples of Unions
Additional information is provided on:
Defining a Union Variable
Defining a Packed Union
Anonymous Unions in C
Anonymous Unions in C++
Related Information
Dot Operator .
Arrow Operator ->
_Packed Qualifier
Declarators
Initializers
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Union ΓòÉΓòÉΓòÉ
A union type declaration has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇunionΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓöÇmemberΓöÇΓöÇ???ΓöÇΓö┤ΓöÇΓöÇ}ΓöÇΓöÇ><
ΓööΓöÇqualifierΓöÇΓöÿ ΓööΓöÇidentifierΓöÇΓöÿ
A member has the form:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇtype_specifierΓöÇΓöÇΓöÇΓö¼ΓöÇdeclaratorΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ><
ΓööΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ???ΓöÇΓöÇconstant_expressionΓöÇΓöÿ
ΓööΓöÇdeclaratorΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 4.9.9.1. Defining a Union Variable ΓòÉΓòÉΓòÉ
A union variable definition contains an optional storage class keyword, the
union keyword, a union tag, and a declarator. The union tag indicates the data
type of the union variable.
The type specifier contains the keyword union followed by the name of the union
type. You must declare the union data type before you can define a union having
that type.
You can define a union data type and a union of that type in the same statement
by placing the variable declarator after the data type definition.
The declarator is an identifier, possibly with the volatile or const qualifier.
You can only initialize the first member of a union.
The following example shows how you would initialize the first union member
birthday of the union variable people:
union {
char birthday[9];
int age;
float weight;
} people = {"23/07/57"};
To define union type and a union variable in one statement, put a declarator
after the type definition. The storage class specifier for the variable must go
at the beginning of the statement.
ΓòÉΓòÉΓòÉ 4.9.9.2. Defining a Packed Union ΓòÉΓòÉΓòÉ
You can use _Packed to qualify a union. However, the memory layout of the union
members is not affected. Each member starts at offset zero. The _Packed
qualifier does affect the total alignment restriction of the whole union.
C++ Note: C++ does not support the _Packed qualifier. To change the alignment
of unions, use the #pragma pack directive or the /Sp compiler option. Both of
these methods are also supported by C.
In the following example, each of the elements in the nonpacked n_array is of
type union uu.
union uu {
short a;
struct {
char x;
char y;
char z;
} b;
};
union uu n_array[2];
_Packed union uu p_array[2];
Because it is not packed, each element in the array has an alignment
restriction of 2 bytes (the largest alignment requirement among the union
members is that of short a), and there is 1 byte of padding at the end of each
element to enforce this requirement.
Now consider the packed array p_array. Because each of its elements is of type
_Packed union uu, the alignment restriction of every element is the byte
boundary. Therefore, each element has a length of only 3 bytes, instead of the
4 bytes in the previous example.
ΓòÉΓòÉΓòÉ 4.9.9.3. Anonymous Unions in C ΓòÉΓòÉΓòÉ
Unions can be declared without declarators if they are members of another
structure or union. Unions without declarators are called anonymous unions.
Members of an anonymous union can be accessed as if they were declared directly
in the containing structure or union. For example, given the following
structure:
struct s {
int a;
union {
int b;
float c;
}; /* no declarator */
} kurt;
you can make the following statements:
kurt.a = 5;
kurt.b = 36;
You can also declare an anonymous union:
1. By creating a typedef and using the typedef name without a declarator:
typedef union {
int a;
int b;
} UNION_T;
struct s1 {
UNION_T;
int c;
} dave;
2. By using an existing union tag without a declarator:
union u1 {
int a;
int b;
};
struct s1 {
union u1;
int c;
} dave;
In both of the examples, the members can be accessed as dave.a, dave.b, and
dave.c.
An anonymous union must be a member of, or nested within another anonymous
union that is a member of, a named structure or union. If a union is declared
at file scope without a declarator, its members are not available to the
surrounding scope. For example, the following union only declares the union
tag tom:
union tom {
int b;
float c;
};
The variables b and c from this union cannot be used at file scope, and the
following statements will generate errors:
b = 5;
c = 2.5;
C++ Note: Anonymous unions are treated differently in C++. See Anonymous
Unions in C++ for more information.
ΓòÉΓòÉΓòÉ 4.9.9.4. Anonymous Unions in C++ ΓòÉΓòÉΓòÉ
It cannot be followed by a declarator. An anonymous union is not a type; it
defines an unnamed object and it cannot have member functions.
The member names of an anonymous union must be distinct from other names within
the scope in which the union is declared. You can use member names directly in
the union scope without any additional member access syntax.
For example, in the following code fragment, you can access the data members i
and cptr directly because they are in the scope containing the anonymous union.
Because i and cptr are union members and have the same address, you should only
use one of them at a time. The assignment to the member cptr will change the
value of the member i.
void f()
{
union { int i; char* cptr ; };
// .
// .
// .
i = 5;
cptr = "string_in_union"; // overrides i
}
An anonymous union cannot have protected or private members. A global anonymous
union must be declared with the keyword static.
ΓòÉΓòÉΓòÉ <hidden> Examples of Unions ΓòÉΓòÉΓòÉ
The following example defines a union data type (not named) and a union
variable (named length). The member of length can be a long int, a float, or a
double.
union {
float meters;
double centimeters;
long inches;
} length;
The following example defines the union type data as containing one member. The
member can be named charctr, whole, or real. The second statement defines two
data type variables: input and output.
union data {
char charctr;
int whole;
float real;
};
union data input, output;
The following statement assigns a character to input:
input.charctr = 'h';
The following statement assigns a floating-point number to member output:
output.real = 9.2;
The following example defines an array of structures named records. Each
element of records contains three members: the integer id_num, the integer
type_of_input, and the union variable input. input has the union data type
defined in the previous example.
struct {
int id_num;
int type_of_input;
union data input;
} records[10];
The following statement assigns a character to the structure member input of
the first element of records:
records[0].input.charctr = 'g';
ΓòÉΓòÉΓòÉ 4.9.10. typedef ΓòÉΓòÉΓòÉ
A typedef declaration lets you define your own identifiers that can be used in
place of type specifiers such as int, float, and double. The names you define
using typedef are not new data types. They are synonyms for the data types or
combinations of data types they represent.
Syntax of a typedef Declaration
A typedef declaration does not reserve storage.
When an object is defined using a typedef identifier, the properties of the
defined object are exactly the same as if the object were defined by explicitly
listing the data type associated with the identifier.
A C++ class defined in a typedef without being named is given a dummy name and
the typedef name for linkage. Such a class cannot have constructors or
destructors. For example:
typedef class {
Trees();
} Trees;
Here the function Trees() is an ordinary member function of a class whose type
name is unspecified. In the above example, Trees is an alias for the unnamed
class, not the class type name itself, so Trees() cannot be a constructor for
that class.
The following statements declare LENGTH as a synonym for int and then use this
typedef to declare length, width, and height as integral variables:
typedef int LENGTH;
LENGTH length, width, height;
The following declarations are equivalent to the above declaration:
int length, width, height;
Similarly, typedef can be used to define a class type (structure, union, or C++
class). For example:
typedef struct {
int scruples;
int drams;
int grains;
} WEIGHT;
The structure WEIGHT can then be used in the following declarations:
WEIGHT chicken, cow, horse, whale;
Related Information
Characters
Floating-Point Variables
Integer Variables
Enumerations
Pointers
void Type
Arrays
Structures
Unions
C++ Classes
Constructors and Destructors Overview
ΓòÉΓòÉΓòÉ <hidden> Syntax of a typedef Declaration ΓòÉΓòÉΓòÉ
The syntax of a typedef declaration is:
>>ΓöÇΓöÇtypedefΓöÇΓöÇtype_specifierΓöÇΓöÇidentifierΓöÇΓöÇ;ΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ 5. Expressions and Operators ΓòÉΓòÉΓòÉ
Expressions are sequences of operators, operands, and punctuators that specify
a computation. The evaluation of expressions is based on the operators that the
expressions contain and the context in which they are used.
This section discusses:
Primary Expressions
Unary Expressions
Binary Expressions
Conditional Expressions
Assignment Expressions
Comma Expression ,
lvalues
Constant Expressions
An expression can result in an lvalue, rvalue, or no value, and can produce
side effects in each case.
C++ Note: C++ operators can be defined to behave differently when applied to
operands of class type. This is called operator overloading. This section
describes the behavior of operators that are not overloaded. The C language
does not permit overloading.
Related Information
Declarations
Overloading Operators
ΓòÉΓòÉΓòÉ 5.1. Operator Precedence and Associativity ΓòÉΓòÉΓòÉ
Two operator characteristics determine how operands group with operators:
precedence and associativity. Precedence is the priority for grouping different
types of operators with their operands. Associativity is the left-to-right or
right-to-left order for grouping operands to operators that have the same
precedence.
For example, in the following statements, the value of 5 is assigned to both a
and b because of the right-to-left associativity of the = operator. The value
of c is assigned to b first, and then the value of b is assigned to a.
b = 9;
c = 5;
a = b = c;
Because the order of expression evaluation is not specified, you can explicitly
force the grouping of operands with operators by using parentheses.
In the expression a + b * c / d, the * and / operations are performed before +
because of precedence. b is multiplied by c before it is divided by d because
of associativity.
Table of Operator Precedence and Associativity lists the C and C++ language
operators in order of precedence and shows the direction of associativity for
each operator. In C++, the primary scope resolution operator (::) has the
highest precedence, followed by the other primary operators. In C, because
there is no scope resolution operator, the other primary operators have the
highest precedence. The comma operator has the lowest precedence. Operators
that appear in the same group have the same precedence.
The order of evaluation for function call arguments or for the operands of
binary operators is not specified. Avoid writing such ambiguous expressions as:
z = (x * ++y) / func1(y);
func2(++i, x[i]);
In the example above, the order of evaluation of ++y and func1(y) is not
defined. If y had the value of 1 before the first statement, it is not known
whether or not the value of 1 or 2 is passed to func1(). In the second
statement, if i had the value of 1, it is not known whether the first or second
array element of x[ ] is passed as the second argument to func2(). Do not write
code that depends on a particular order of evaluation of operators with the
same precedence.
The order of grouping operands with operators in an expression containing more
than one instance of an operator with both associative and commutative
properties is not specified. The operators that have the same associative and
commutative properties are: *, +, &, |, and ^ (or ╨║). The grouping of operands
can be forced by grouping the expression in parentheses.
Examples of Expressions and Precedence
Related Information
Parenthesized Expressions ( )
Expressions and Operators
Table of Operator Precedence and Associativity
ΓòÉΓòÉΓòÉ <hidden> Examples of Expressions and Precedence ΓòÉΓòÉΓòÉ
The parentheses in the following expressions explicitly show how the compiler
groups operands and operators. If parentheses did not appear in these
expressions, the operands and operators are grouped in the same manner as
indicated by the parentheses.
total = (4 + (5 * 3));
total = (((8 * 5) / 10) / 3);
total = (10 + (5/3));
Because the order of grouping operands with operators that are both associative
and commutative is not specified, the compiler can group the operands and
operators in the expression:
total = price + prov_tax + city_tax;
in the following ways (as indicated by parentheses):
total = (price + (prov_tax + city_tax));
total = ((price + prov_tax) + city_tax);
total = ((price + city_tax) + prov_tax);
If the values in this expression are integers, the grouping of operands and
operators does not affect the result. Because intermediate values are rounded,
different groupings of floating-point operators give different results.
In certain expressions, the grouping of operands and operators can affect the
result. For example, in the following expression, each function call might be
modifying the same global variables.
a = b() + c() + d();
This expression can give different results depending on the order in which the
functions are called.
If the expression contains operators that are both associative and commutative
and the order of grouping operands with operators can affect the result of the
expression, separate the expression into several expressions. For example, the
following expressions could replace the previous expression if the called
functions do not produce any side effects that affect the variable a.
a = b();
a += c();
a += d();
Integer overflows are ignored. Division by zero and floating-point exceptions
are implementation dependent.
See the IBM VisualAge C++ for OS/2 User's Guide and Reference for information
about VisualAge C++ implementation dependencies.
ΓòÉΓòÉΓòÉ <hidden> Table of Operator Precedence and Associativity ΓòÉΓòÉΓòÉ
Operator Name Operators
Primary scope resolution ::
Associativity: left to right
Primary () [ ] . ->
Associativity: left to right
Unary ++ -- - + ! ~ & * sizeof new delete
(typename) (C cast)
Associativity: right to left
C++ Cast (typename)
Associativity: left to right
C++ Pointer to Member .* ->*
Associativity: left to right
Multiplicative * / %
Associativity: left to right
Additive + -
Associativity: left to right
Bitwise Shift << >>
Associativity: left to right
Relational < > <= >=
Associativity: left to right
Equality == !=
Associativity: left to right
Bitwise Logical AND &
Associativity: left to right
Bitwise Exclusive OR ^ or ╨║
Associativity: left to right
Bitwise Inclusive OR |
Associativity: left to right
Logical AND &&
Associativity: left to right
Logical OR ||
Associativity: left to right
Conditional ? :
Associativity: right to left
Assignment = += -= *= /= <<= >>= %= &= ^= |=
Associativity: right to left
Comma ,
Associativity: left to right
ΓòÉΓòÉΓòÉ 5.2. Operands ΓòÉΓòÉΓòÉ
Most expressions can contain several different, but related, types of operands.
The following type classes describe related types of operands:
Integral Character objects and constants, objects having an enumeration
type, and objects having the type short, int, long, or unsigned
long.
Arithmetic Integral objects and objects having the type float, double, and
long double.
Scalar Arithmetic objects and pointers to objects of any type. Also C++
references.
Aggregate Arrays, structures, and unions. Also C++ classes.
Many operators cause conversions from one data type to another. Conversions
are discussed in Implicit Type Conversions.
ΓòÉΓòÉΓòÉ 5.3. lvalues ΓòÉΓòÉΓòÉ
An lvalue is an expression that represents an object. A modifiable lvalue is an
expression representing an object that can be changed. It is typically the left
operand in an assignment expression. For example, arrays and const objects are
not modifiable lvalues, but static int objects are.
All assignment operators evaluate their right operand and assign that value to
their left operand. The left operand must evaluate to a reference to an object.
The address operator (&) requires an lvalue as an operand while the increment
(++) and the decrement (--) operators require a modifiable lvalue as an
operand.
Examples of Lvalues
Related Information
Assignment Expressions
Address &
Dot Operator .
Arrow Operator ->
ΓòÉΓòÉΓòÉ <hidden> Examples of Lvalues ΓòÉΓòÉΓòÉ
Expression Lvalue
x = 42; x
*ptr = newvalue; *ptr
a++ a
ΓòÉΓòÉΓòÉ 5.4. Primary Expressions ΓòÉΓòÉΓòÉ
A primary expression can be:
Identifiers
String Literals
Scope Resolution Operator ::
Parenthesized Expressions ( )
Constant Expressions
Function Calls ( )
Array Subscript [ ]
Dot Operator .
Arrow Operator ->
All primary operators have the same precedence and have left-to-right
associativity.
Related Information
Expressions and Operators
Operator Precedence and Associativity
ΓòÉΓòÉΓòÉ 5.4.1. Scope Resolution Operator :: ΓòÉΓòÉΓòÉ
The :: (scope resolution) operator is used to qualify hidden names so that you
can still use them. You can use the unary scope operator if a file scope name
is hidden by an explicit declaration of the same name in a block or class. For
example:
int i = 10;
int f(int i)
{
return i ? i : :: i; // return global i if local i is zero
}
You can also use the class scope operator to qualify class names or class
member names. If a class member name is hidden, you can use it by qualifying it
with its class name and the class scope operator. Whenever a name is followed
by a :: operator, the name is interpreted as a class name.
In the following example, the declaration of the variable X hides the class
type X, but you can still use the static class member count by qualifying it
with the class type X and the scope resolution operator.
#include <iostream.h>
class X
{
public:
static int count;
};
int X::count = 10; // define static data member
void main ()
{
int X = 0; // hides class type X
cout << X::count << endl; // use static member of class X
}
The scope resolution operator is also discussed in Class Names and in Scope of
Class Names.
Related Information
Class Names
Scope of Class Names
Expressions and Operators
ΓòÉΓòÉΓòÉ 5.4.2. Parenthesized Expressions ( ) ΓòÉΓòÉΓòÉ
Use parentheses to explicitly force the order of expression evaluation. The
following expression does not contain any parentheses used for grouping
operands and operators. The parentheses surrounding weight, zipcode are used to
form a function call. Note how the compiler groups the operands and operators
in the expression according to the rules for operator precedence and
associativity:
-discount * item + handling(weight, zipcode) < .10 * item
| | | | | | |
'---.---' | '-----------.-----------' '----.---'
'----.-----' | |
'----------.----------' |
'-------------------------------'
The following expression is similar to the previous expression, but it contains
parentheses that change how the operands and operators are grouped:
(-discount * (item + handling(weight, zipcode) ) ) < (.10 * item)
| | | | | | |
'---.---' | '----------.------------' '-----.----'
| '--------.--------' |
'-------.---------' |
'---------------------------------------------'
In an expression that contains both associative and commutative operators, you
can use parentheses to specify the grouping of operands with operators. The
parentheses in the following expression guarantee the order of grouping
operands with the operators:
x = f + (g + h);
Related Information
Operator Precedence and Associativity
Function Calls ( )
Expressions and Operators
ΓòÉΓòÉΓòÉ 5.4.3. Constant Expressions ΓòÉΓòÉΓòÉ
A constant expression is an expression with a value that is determined during
compilation and cannot be changed at runtime, it can only be evaluated.
Constant expressions can be composed of integer constants, character constants,
floating-point constants, and enumeration constants, address constants, and
other constant expressions. Some constant expressions, such as a string literal
or an address constant, are lvalues.
The C and C++ languages require constant expressions in the following places:
In the subscript declarator, as the description of an array bound
After the keyword case in a switch statement
In an enumerator, as the numeric value of an enum constant
In a bit-field width specifier
In the preprocessor #if statement (Enumeration constants, address
constants, and sizeof cannot be specified in the preprocessor #if
statement.)
In the initializer of a file scope data definition.
In all these contexts except for an initializer of a file scope data
definition, the constant expression can contain integer, character, and
enumeration constants, casts to integral types, and sizeof expressions.
Function-scope static and extern declarations can be initialized with the
address of a previously defined static or extern.
In a file scope data definition, the initializer must evaluate to a constant
or to the address of a static storage (extern or static) object (plus or minus
an integer constant) that is defined or declared earlier in the file. The
constant expression in the initializer can contain integer, character,
enumeration, and float constants, casts to any type, sizeof expressions, and
unary address expressions.
Functions, class objects, pointers, and references are not allowed unless they
occur in sizeof expressions. Comma operators and assignment operators cannot
appear in constant expressions.
Examples of Constant Expressions
Related Information
Arrays
Initializers
File Scope Data Declarations
switch
Enumerations
Structures
Conditional Compilation Directives
sizeof (Size of an Object)
ΓòÉΓòÉΓòÉ <hidden> Examples of Constant Expressions ΓòÉΓòÉΓòÉ
The following examples show constants used in expressions.
Expression Constant
x = 42; 42
extern int cost = 1000; 1000
y = 3 * 29; 3 * 29
ΓòÉΓòÉΓòÉ 5.4.4. Function Calls ( ) ΓòÉΓòÉΓòÉ
A function call is a primary expression containing a simple type name and a
parenthesized argument list. The argument list can contain any number of
expressions separated by commas. It can also be empty.
For example:
stub()
overdue(account, date, amount)
notify(name, date + 5)
report(error, time, date, ++num)
The arguments are evaluated, and each formal parameter is assigned the value of
the corresponding argument. Assigning a value to a formal parameter within the
function body changes the value of the parameter within the function, but has
no effect on the argument.
The type of a function call expression is the return type of the function. The
return value is determined by the return statement in the function definition.
The result of a function call is an lvalue only if the function returns a
reference. A function can call itself.
If you want a function to change the value of a variable, pass a pointer to the
variable you want changed. When a pointer is passed as a parameter, the pointer
is copied; the object pointed to is not copied. (See Pointers.)
Arguments that are arrays and functions are converted to pointers before being
passed as function arguments.
Arguments passed to nonprototyped C functions undergo conversions: type short
or char parameters are converted to int, and float parameters to double. Use a
cast expression for other conversions. (See Cast Expressions.)
If the function has not been previously declared, an implicit declaration of
extern int func(); is assumed.
The compiler compares the data types provided by the calling function with the
data types that the called function expects. The compiler might also perform
type conversions if the declaration of the function is in function prototype
format and the parameters differ from the prototype or visible at the point
where the function is called.
The order in which parameters are evaluated is not specified. Avoid such calls
as:
method(samp1, bat.proc--, bat.proc);
In this example, bat.proc-- might be evaluated last, causing the last two
arguments to be passed with the same value.
Example of a Function Call
Related Information
Functions
Pointers
Cast Expressions
Primary Expressions
ΓòÉΓòÉΓòÉ <hidden> Example of a Function Call ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, main passes func two values: 5 and 7. The function
func receives copies of these values and accesses them by the identifiers: a
and b. The function func changes the value of a. When control passes back to
main, the actual values of x and y are not changed. The called function func
only receives copies of x and y, not the values themselves.
*
************************************************************************/
/**
** This example illustrates function calls
**/
#include <stdio.h>
int main(void)
{
int x = 5, y = 7;
func(x, y);
printf("In main, x = %d y = %d\n", x, y);
}
void func (int a, int b)
{
a += b;
printf("In func, a = %d b = %d\n", a, b);
}
/************************************************************************
*
This program produces the following output:
In func, a = 12 b = 7
In main, x = 5 y = 7
*
************************************************************************/
ΓòÉΓòÉΓòÉ 5.4.5. Array Subscript [ ] ΓòÉΓòÉΓòÉ
A primary expression followed by an expression in [ ] (square brackets)
specifies an element of an array. The expression within the square brackets is
referred to as a subscript.
The primary expression must have a pointer type, and the subscript must have
integral type. The result of an array subscript is an lvalue.
The first element of each array has the subscript 0. The expression
contract[35] refers to the 36th element in the array contract.
In a multidimensional array, you can reference each element (in the order of
increasing storage locations) by incrementing the rightmost subscript most
frequently.
For example, the following statement gives the value 100 to each element in the
array code[4][3][6]:
for (first = 0; first <= 3; ++first)
for (second = 0; second <= 2; ++second)
for (third = 0; third <= 5; ++third)
code[first][second][third] = 100;
By definition, the expression:
*((exp1) + (exp2))
is identical to the expression:
exp1[exp2]
which is also identical to:
exp2[exp1]
Related Information
Arrays
lvalues
Primary Expressions
ΓòÉΓòÉΓòÉ 5.4.6. Dot Operator . ΓòÉΓòÉΓòÉ
The . (dot) operator is used to access structure or C++ class members using a
structure object. The member is specified by a primary expression, followed by
a . (dot) operator, followed by a name. For example:
roster[num].name
roster[num].name[1]
The primary expression must be an object of type class, struct or union. The
name must be a member of that object.
The value of the expression is the value of the selected member. If the primary
expression and the name are lvalues, the expression value is also an lvalue.
For more information on class members, see C++ Class Members and Friends.
Related Information
Arrow Operator ->
C++ Class Members and Friends
Unions
Structures
lvalues
Primary Expressions
ΓòÉΓòÉΓòÉ 5.4.7. Arrow Operator -> ΓòÉΓòÉΓòÉ
The -> (arrow) operator is used to access structure or C++ class members using
a pointer. A primary expression, followed by an -> (arrow) operator, followed
by a name, designates a member of the object to which the pointer points. For
example:
roster -> name
The primary expression must be a pointer to an object of type class, struct or
union. The name must be a member of that object.
The value of the expression is the value of the selected member. If the name is
an lvalue, the expression value is also an lvalue.
For more information on class members, see C++ Class Members and Friends.
Related Information
Dot Operator .
C++ Class Members and Friends
Unions
Structures
lvalues
Pointers
Primary Expressions
ΓòÉΓòÉΓòÉ 5.5. Unary Expressions ΓòÉΓòÉΓòÉ
A unary expression contains one operand and a unary operator. All unary
operators have the same precedence and have right-to-left associativity.
As indicated in the following descriptions, the usual arithmetic conversions
are performed on the operands of most unary expressions. See Arithmetic
Conversions for more information.
Increment ++
Decrement - -
Unary Plus +
Unary Minus -
Logical Negation !
Bitwise Negation ~
Address &
Indirection *
Cast Expressions
sizeof (Size of an Object)
new Operator
delete Operator
throw Expressions
Related Information
Binary Expressions
Expressions and Operators
Operator Precedence and Associativity
ΓòÉΓòÉΓòÉ 5.5.1. Increment ++ ΓòÉΓòÉΓòÉ
The ++ (increment) operator adds 1 to the value of a scalar operand, or if the
operand is a pointer, increments the operand by the size of the object to which
it points. The operand receives the result of the increment operation. The
operand must be a modifiable lvalue.
You can put the ++ before or after the operand. If it appears before the
operand, the operand is incremented. Then the incremented value is used in the
expression. If you put the ++ after the operand, the value of the operand is
used in the expression before the operand is incremented. For example:
play = ++play1 + play2++;
is equivalent to the following three expressions:
play1 = play1 + 1;
play = play1 + play2;
play2 = play2 + 1;
The return type of the increment expression is the same type as that of the
operand.
Related Information
Decrement - -
Addition +
Pointer Arithmetic
Unary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.5.2. Decrement - - ΓòÉΓòÉΓòÉ
The -- (decrement) operator subtracts 1 from the value of a scalar operand, or
if the operand is a pointer, decreases the operand by the size of the object to
which it points. The operand receives the result of the decrement operation.
The operand must be a modifiable lvalue.
You can put the -- before or after the operand. If it appears before the
operand, the operand is decremented, and the decremented value is used in the
expression. If the -- appears after the operand, the current value of the
operand is used in the expression and the operand is decremented.
For example:
play = --play1 + play2--;
is equivalent to the following three expressions:
play1 = play1 - 1;
play = play1 + play2;
play2 = play2 - 1;
The return type of the decrement expression is the same type as that of the
operand.
Related Information
Increment ++
Subtraction -
Pointer Arithmetic
Unary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.5.3. Unary Plus + ΓòÉΓòÉΓòÉ
The + (unary plus) operator maintains the value of the operand. The operand
can have any arithmetic type. The result is not an lvalue.
The result of the unary plus expression has the same type as the operand after
any integral promotions (for example, char to int).
Note: Any plus sign in front of a constant is not part of the constant.
Related Information
Unary Minus -
Unary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.5.4. Unary Minus - ΓòÉΓòÉΓòÉ
The - (unary minus) operator negates the value of the operand. The operand can
have any arithmetic type. The result is not an lvalue.
For example, if quality has the value 100, -quality has the value -100.
The result of the unary minus expression has the same type as the operand after
any integral promotions (for example, char to int).
Note: Any minus sign in front of a constant is not part of the constant.
Related Information
Unary Plus +
Unary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.5.5. Logical Negation ! ΓòÉΓòÉΓòÉ
The ! (logical negation) operator determines whether the operand evaluates to 0
(false) or nonzero (true). The expression yields the value 1 (true) if the
operand evaluates to 0, and yields the value 0 (false) if the operand evaluates
to a nonzero value. The operand must have a scalar data type, but the result of
the operation has always type int and is not an lvalue.
The following two expressions are equivalent:
!right;
right == 0;
Related Information
Equality == !=
Relational < > <= >=
Unary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.5.6. Bitwise Negation ~ ΓòÉΓòÉΓòÉ
The ~ (bitwise negation) operator yields the bitwise complement of the operand.
In the binary representation of the result, every bit has the opposite value of
the same bit in the binary representation of the operand. The operand must have
an integral type. The result has the same type as the operand but is not an
lvalue.
Suppose x represents the decimal value 5. The 32-bit binary representation of x
is:
00000000000000000000000000000101
The expression ~x yields the following result, represented here as a 32-bit
binary number:
11111111111111111111111111111010
The 32-bit binary representation of ~0 is:
11111111111111111111111111111111
Related Information
Bitwise Left and Right Shift << >>
Bitwise AND &
Bitwise Exclusive OR ^
Bitwise Inclusive OR |
Unary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.5.7. Address & ΓòÉΓòÉΓòÉ
The & (address) operator yields a pointer to its operand. The operand must be
an lvalue, a function designator, or a qualified name. It cannot be a bit
field, nor can it have the storage class register.
If the operand is an lvalue or function, the resulting type is a pointer to the
expression type. For example, if the expression has type int, the result is a
pointer to an object having type int.
If the operand is a qualified name and the member is not static, the result is
a pointer to a member of class and has the same type as the member. The result
is not an lvalue.
If p_to_y is defined as a pointer to an int and y as an int, the following
expression assigns the address of the variable y to the pointer p_to_y:
p_to_y = &y;
C++ Note: You can use the & operator with overloaded functions only in an
initialization or assignment where the left side uniquely determines which
version of the overloaded function is used. For more information, see
Overloading Functions.
Related Information
Pointers
lvalues
register Storage Class Specifier
Overloading Functions
Unary Expressions
ΓòÉΓòÉΓòÉ 5.5.8. Indirection * ΓòÉΓòÉΓòÉ
The * (indirection) operator determines the value referred to by the
pointer-type operand. The operand cannot be a pointer to an incomplete type.
The operation yields an lvalue or a function designator if the operand points
to a function. The usual unary conversions are performed. Arrays and functions
are converted to pointers.
The type of the operand determines the type of the result. For example, if the
operand is a pointer to an int, the result has type int.
Do not apply the indirection operator to any pointer that contains an address
that is not valid, such as NULL. The result is not defined.
If p_to_y is defined as a pointer to an int and y as an int, the expressions:
p_to_y = &y;
*p_to_y = 3;
cause the variable y to receive the value 3.
Related Information
Pointers
lvalues
Functions
Unary Expressions
ΓòÉΓòÉΓòÉ 5.5.9. Cast Expressions ΓòÉΓòÉΓòÉ
The cast operator is used for explicit type conversions. It converts the value
of the operand to a specified data type and performs the necessary conversions
to the operand for the type.
For C, the operand must be scalar and the type must be either scalar or void.
For C++, the operand can have class type. If the operand has class type, it can
be cast to any type for which the class has a user-defined conversion function.
User-defined conversion functions are described in Conversion Functions.
The result of a cast is not an lvalue unless the cast is to a reference type.
When you cast to a reference type, no user-defined conversions are performed
and the result is an lvalue.
There are two types of casts that take one argument:
C-style casts, with the format (X)a. These are the only casts allowed in
C.
function-style casts with one argument, such as X(a). These are allowed
in C++ only.
Both types of casts convert the argument a to the type X. In C++, they can
invoke a constructor, if the target type is a class, or they can invoke a
conversion function, if the source type is a class. They can be ambiguous if
both conditions hold.
A function-style cast with no arguments, such as X(), creates a temporary
object of type X. If X is a class with constructors, the default constructor
X::X() is called.
A function-style cast with more than one argument, such as X(a,b), creates a
temporary object of type X. This object must be a class with a constructor
that takes two arguments of types compatible with the types of a and b. The
constructor is called with a and b as arguments.
Related Information
Implicit Type Conversions
Conversion by Constructor
Conversion Functions
Type Specifiers
ΓòÉΓòÉΓòÉ 5.5.10. sizeof (Size of an Object) ΓòÉΓòÉΓòÉ
The sizeof operator yields the size in bytes of the operand. Types cannot be
defined in a sizeof expression. The sizeof operation cannot be performed on
A bit field
A function
An undefined structure or class
An incomplete type (such as void)
The operand can be the parenthesized name of a type.
The compiler must be able to evaluate the size at compile time. The expression
is not evaluated; there are no side effects. For example, the value of b is 5
from initialization to the end of program runtime:
#include <stdio.h>
int main(void){
int b = 5;
sizeof(b++);
}
The size of a char object is the size of a byte. For example, if a variable x
has type char, the expression sizeof(x) always evaluates to 1.
The result of a sizeof operation has type size_t, which is an unsigned
integral type defined in the <stddef.h> header file.
The size of an object is determined on the basis of its definition. The sizeof
operator does not perform any conversions. If the operand contains operators
that perform conversions, the compiler does take these conversions into
consideration. The following expression causes the usual arithmetic
conversions to be performed. The result of the expression x + 1 has type int
(if x has type char, short, or int or any enumeration type) and is equivalent
to sizeof(int):
sizeof (x + 1)
Except in preprocessor directives, you can use a sizeof expression wherever a
constant or unsigned constant is required. One of the most common uses for the
sizeof operator is to determine the size of objects that are referred to
during storage allocation, input, and output functions.
Another use of sizeof is in porting code across platforms. You should use the
sizeof operator to determine the size that a data type represents. For
example:
sizeof(int)
C++ Notes: The result of a sizeof expression depends on the type it is
applied to:
An array The result is the total number of bytes in the array. For example,
in an array with 10 elements, the size is equal to 10 times the
size of a single element. The compiler does not convert the array
to a pointer before evaluating the expression.
A class The result is always nonzero, and is equal to the number of bytes
in an object of that class including any padding required for
placing class objects in an array.
A reference The result is the size of the referenced object.
Related Information
Constant Expressions
Implicit Type Conversions
Type Specifiers
Unary Expressions
ΓòÉΓòÉΓòÉ 5.5.11. new Operator ΓòÉΓòÉΓòÉ
The new operator provides dynamic storage allocation. The syntax for an
allocation expression containing the new operator is:
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇnewΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇ(type)ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ>
ΓööΓöÇ::ΓöÇΓöÿ ΓööΓöÇ(argument_list)ΓöÇΓöÿ ΓööΓöÇnew_typeΓöÇΓöÿ
>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÿ
ΓööΓöÇinitial_valueΓöÇΓöÿ
If you prefix new with the scope resolution operator (::), the global operator
new() is used. If you specify an argument_list, the overloaded new operator
that corresponds to that argument_list is used. The type is an existing
built-in or user-defined type. A new_type is a type that has not already been
defined and can include type specifiers and declarators.
An allocation expression containing the new operator is used to find storage in
free store for the object being created. The new expression returns a pointer
to the object created and can be used to initialize the object. If the object
is an array, a pointer to the initial element is returned.
You can use the routine set_new_handler() to change the default behavior of
new. You can also use the /Tm option to enable a debug version of new, as
described in Debug Versions of new and delete.
You cannot use the new operator to allocate function types, void, or incomplete
class types because these are not object types. However, you can allocate
pointers to functions with the new operator. You cannot create a reference with
the new operator.
When the object being created is an array, only the first dimension can be a
general expression. All subsequent dimensions must be constant integral
expressions. The first dimension can be a general expression even when an
existing type is used. You can create an array with zero bounds with the new
operator. For example:
char * c = new char[0];
In this case, a pointer to a unique object is returned.
An object created with operator new() exists until the operator delete() is
called to deallocate the object's memory, or until program ends.
If parentheses are used within a new_type, parentheses should also surround the
new_type to prevent syntax errors.
Example of Allocating Storage with new()
The type of the object being created cannot contain class declarations,
enumeration declarations, or const or volatile types. It can contain pointers
to const or volatile objects.
For example, const char* is allowed, but char* const is not.
Additional arguments can be supplied to new by using the argument_list, also
called the placement syntax. If placement arguments are used, a declaration of
operator new() with these arguments must exist. For example:
#include <stddef.h>
class X
{
public:
void* operator new(size_t,int, int){ /* ... */ }
};
void main ()
{
X* ptr = new(1,2) X;
}
Additional information is provided on:
Member Functions and the Global operator new()
Initializing Objects Created with the new Operator
Related Information
set_new_handler() - Set Behavior for new Failure
Overloaded new and delete
Debug Versions of new and delete
Constructors and Destructors Overview
Free Store
delete Operator
Unary Expressions
/Tm option
ΓòÉΓòÉΓòÉ 5.5.11.1. Member Functions and the Global operator new() and operator new[]() ΓòÉΓòÉΓòÉ
When an object of a class type is created with the new operator, the member
operator new() function is implicitly called. The first argument is the amount
of space requested.
The following rules determine which storage allocation function is used:
1. If your own operator new() exists, and the :: operator is not used, your
operator new() is used.
2. If you have not defined an operator new() function, the global ::operator
new() function defined in <new.h> is used. The allocation expression of
the form ::operator new() ensures that the global new operator is called,
rather than your class member operator.
When a nonclass object is created with the new operator, the global ::operator
new() is used.
The order of evaluation of a call to an operator new() is undefined in the
evaluation of arguments to constructors. If operator new() returns 0, the
arguments to a constructor may or may not have been evaluated.
ΓòÉΓòÉΓòÉ 5.5.11.2. Initializing Objects Created with the new Operator ΓòÉΓòÉΓòÉ
You can initialize objects created with the new operator in several ways. For
nonclass objects, or for class objects without constructors, a new initializer
expression can be provided in a new expression by specifying ( expression ) or
(). For example:
double* pi = new double(3.1415926);
int* score = new int(89);
float* unknown = new float();
If a class has a constructor, the new initializer must be provided when any
object of that class is allocated. The arguments of the new initializer must
match the arguments of a class constructor, unless the class has a default
constructor.
You cannot specify an initializer for arrays. You can initialize an array of
class objects only if the class has a default constructor. The constructor is
called to initialize each array element (class object).
Initialization using the new initializer is performed only if new successfully
allocates storage.
ΓòÉΓòÉΓòÉ <hidden> Example of Allocating Storage with new() ΓòÉΓòÉΓòÉ
/***********************************************************************
*
In the following example, storage is allocated for an array of pointers to
functions:
*
************************************************************************/
void f();
void g();
void main()
{
void (**p)(), (**q)();
// declare p and q as pointers to pointers to void functions
p = new (void (*[3])());
// p now points to an array of pointers to functions
q = new void(*[3])(); // error
// error - bound as 'q = (new void) (*[3])();'
p[0] = f; // p[0] to point to function f
q[2] = g; // q[2] to point to function g
p[0](); // call f()
q[2](); // call g()
}
/************************************************************************
*
However, the second use of new causes an erroneous binding of q = (new void)
(*[3])().
*
************************************************************************/
ΓòÉΓòÉΓòÉ 5.5.11.3. set_new_handler() - Set Behavior for new Failure ΓòÉΓòÉΓòÉ
When the new operator creates a new object, it calls the operator new()
function to obtain the needed storage.
When new cannot allocate storage, it calls a new handler function if one has
been installed by a call to set_new_handler(). The set_new_handler() function
is defined in <new.h>. Use it to call a new handler you have defined or the
default new handler.
The set_new_handler() function has the prototype:
typedef void(*PNH)();
PNH set_new_handler(PNH);
set_new_handler() takes as an argument a pointer to a function (the new
handler), which has no arguments and returns void. It returns a pointer to the
previous new handler function.
If you do not specify your own set_new_handler() function, new returns the NULL
pointer.
The _set_mt_new_handler() function behaves exactly the same way as
set_new_handler(), except that it only affects the current thread. When a new
handler function needs to be called, the code first checks for a thread new
handler. If one has been registered, it is called. If not, the new handler
registered with set_new_handler() is called.
Example of set_new_handler()
Related Information
new Operator
Member Functions and the Global operator new()
Initializing Objects Created with the new Operator
Overloaded new and delete
Constructors and Destructors Overview
Free Store
ΓòÉΓòÉΓòÉ <hidden> Example of set_new_handler() ΓòÉΓòÉΓòÉ
The following program segment shows how you could use set_new_handler() to
return a message if the new operator cannot allocate storage:
#include <iostream.h>
#include <new.h>
void no_storage()
{
cerr << "Operator new failed: no storage is available.\n";
exit(1);
}
main()
{
set_new_handler(&no_storage);
// Rest of program ...
}
If the program fails because new cannot allocate storage, the program exits
with the message:
Operator new failed: no storage is available.
ΓòÉΓòÉΓòÉ 5.5.12. delete Operator ΓòÉΓòÉΓòÉ
The delete operator destroys the object created with new by deallocating the
memory associated with the object.
The delete operator has a void return type. It has the syntax:
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇdeleteΓöÇΓöÇobject_pointerΓöÇΓöÇ><
ΓööΓöÇ::ΓöÇΓöÿ
For example: delete myobj;
The operand of delete must be a pointer returned by new, and cannot be a
pointer to constant. If an attempt to create an object with new fails, the
pointer returned by new will have a zero value, but it can still be used with
delete. Deleting a null pointer has no effect.
The delete[] operator frees storage allocated for array objects created with
new. The delete operator frees storage allocated for individual objects created
with new.
It has the syntax:
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇdeleteΓöÇΓöÇ[ΓöÇΓöÇ]ΓöÇΓöÇarrayΓöÇΓöÇ><
ΓööΓöÇ::ΓöÇΓöÿ
For example: delete [] myarray;
The result of deleting an array object with delete is undefined, as is deleting
an individual object with delete[]. The array dimensions do not need to be
specified with delete[].
The results of attempting to access a deleted object are undefined because the
deletion of an object can change its value.
If a destructor has been defined for a class, delete invokes that destructor.
Whether a destructor exists or not, delete frees the storage pointed to by
calling the function operator delete() of the class if one exists.
The global ::operator delete() is used if:
The class has no operator delete().
The object is of a nonclass type.
The object is deleted with the ::delete expression. For example:
::delete p;
The default global operator delete() only frees storage allocated by the
default global operator new(). The default global operator delete[]() only
frees storage allocated for arrays by the default global operator new().
You can also use the /Tm compiler option to enable a debug version of the
delete operator, as described in Debug Versions of new and delete.
Related Information
Overloaded new and delete
Debug Versions of new and delete
Constructors and Destructors Overview
Free Store
new Operator
Unary Expressions
/Tm option
ΓòÉΓòÉΓòÉ 5.5.13. throw Expressions ΓòÉΓòÉΓòÉ
A throw expression is used to throw exceptions to C++ exception handlers. It
causes control to be passed out of the block enclosing the throw statement to
the first C++ exception handler whose catch argument matches the throw
expression. A throw expression is a unary expression of type void.
For more information on the throw expression, see C++ Exception Handling.
Related Information
C++ Exception Handling
Unary Expressions
ΓòÉΓòÉΓòÉ 5.6. Binary Expressions ΓòÉΓòÉΓòÉ
A binary expression contains two operands separated by one operator.
Not all binary operators have the same precedence. The table in the section
Operator Precedence and Associativity shows the order of precedence among
operators. All binary operators have left-to-right associativity.
The order in which the operands of most binary operators are evaluated is not
specified. To ensure correct results, avoid creating binary expressions that
depend on the order in which the compiler evaluates the operands.
As indicated in the following descriptions, the usual arithmetic conversions
are performed on the operands of most binary expressions. See Arithmetic
Conversions for more information.
Multiplication *
Division /
Remainder %
Addition +
Subtraction -
Bitwise Left and Right Shift << >>
Relational < > <= >=
Equality == !=
Bitwise AND &
Bitwise Exclusive OR ^
Bitwise Inclusive OR |
Logical AND &&
Logical OR ||
Pointer to Member Operators .* ->*
Related Information
Unary Expressions
Expressions and Operators
Operator Precedence and Associativity
ΓòÉΓòÉΓòÉ 5.6.1. Multiplication * ΓòÉΓòÉΓòÉ
The * (multiplication) operator yields the product of its operands. The
operands must have an arithmetic type. The result is not an lvalue.
Because the multiplication operator has both associative and commutative
properties, the compiler can rearrange the operands in an expression that
contains more than one multiplication operator. For example, the expression:
sites * number * cost
can be interpreted in any of the following ways:
(sites * number) * cost
sites * (number * cost)
(cost * sites) * number
Related Information
Division /
Remainder %
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.2. Division / ΓòÉΓòÉΓòÉ
The / (division) operator yields the quotient of its operands. The operands
must have an arithmetic type. The result is not an lvalue.
If both operands are positive integers and the operation produces a remainder,
the remainder is ignored. For example, expression 7 / 4 yields the value 1
(rather than 1.75 or 2).
How the compiler treats the result when either of the operands has a negative
value is not specified. On all IBM C compilers, the result of -7 / 4 is -1 with
a remainder of -3, assuming both -7 and 4 are signed.
The result is undefined if the second operand evaluates to 0.
Related Information
Multiplication *
Remainder %
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.3. Remainder % ΓòÉΓòÉΓòÉ
The % (remainder) operator yields the remainder from the division of the left
operand by the right operand. For example, the expression 5 % 3 yields 2. The
result is not an lvalue.
Both operands must have an integral type. If the right operand evaluates to 0,
the result is undefined. If either operand has a negative value, the result is
such that the following expression always yields the value of a if b is not 0
and a/b is representable:
( a / b ) * b + a % b;
Related Information
Multiplication *
Division /
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.4. Addition + ΓòÉΓòÉΓòÉ
The + (addition) operator yields the sum of its operands. Both operands must
have an arithmetic type, or the first operand must be a pointer to an object
type and the other operand must have an integral type.
When both operands have an arithmetic type, the usual arithmetic conversions on
the operands are performed. The result has the type produced by the conversions
on the operands and is not an lvalue.
A pointer to an object in an array can be added to a value having integral
type. The result is a pointer of the same type as the pointer operand. The
result refers to another element in the array, offset from the original element
by the amount specified by the integral value. If the resulting pointer points
to storage outside the array, other than the first location outside the array,
the result is undefined. The compiler does not provide boundary checking on the
pointers.
Related Information
Pointers
Unary Plus +
Subtraction -
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.5. Subtraction - ΓòÉΓòÉΓòÉ
The - (subtraction) operator yields the difference of its operands. Both
operands must have an arithmetic type, or the left operand must have a pointer
type and the right operand must have the same pointer type or an integral type.
You cannot subtract a pointer from an integral value.
When both operands have an arithmetic type, the usual arithmetic conversions on
the operands are performed. The result has the type produced by the conversions
on the operands and is not an lvalue.
When the left operand is a pointer and the right operand has an integral type,
the compiler converts the value of the right to an address offset. The result
is a pointer of the same type as the pointer operand.
If both operands are pointers to the same type, the compiler converts the
result to an integral type that represents the number of objects separating the
two addresses. Behavior is undefined if the pointers do not refer to objects in
the same array.
Related Information
Pointers
Unary Minus -
Addition +
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.6. Bitwise Left and Right Shift << >> ΓòÉΓòÉΓòÉ
The bitwise shift operators move the bit values of a binary object. The left
operand specifies the value to be shifted. The right operand specifies the
number of positions that the bits in the value are to be shifted. The result is
not an lvalue. Both operands have the same precedence and are left-to-right
associative.
The << (bitwise left shift) operator indicates the bits are to be shifted to
the left. The >> (bitwise right shift) operator indicates the bits are to be
shifted to the right.
Each operand must have an integral type. The compiler performs integral
promotions on the operands. Then the right operand is converted to type int.
The result has the same type as the left operand (after the arithmetic
conversions).
The right operand should not have a negative value or a value that is greater
than or equal to the width in bits of the expression being shifted. The result
of bitwise shifts on such values is unpredictable.
If the right operand has the value 0, the result is the value of the left
operand (after the usual arithmetic conversions).
The << operator fills vacated bits with zeros. For example, if left_op has the
value 4019, the bit pattern (in 32-bit format) of left_op is:
00000000000000000000111110110011
The expression left_op << 3 yields:
00000000000000000111110110011000
If the left operand has an unsigned type, the >> operator fills vacated bits
with zeros. Otherwise, the compiler fills the vacated bits of a signed value
with a copy of the value's sign bit.
For example, if left_op has the value -25, the bit pattern (in 32-bit format)
of left_op is:
11111111111111111111111111100111
The expression left_op >> 3 yields:
11111111111111111111111111111100
Related Information
Bitwise Negation ~
Bitwise AND &
Bitwise Exclusive OR ^
Bitwise Inclusive OR |
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.7. Relational < > <= >= ΓòÉΓòÉΓòÉ
The relational operators compare two operands and determine the validity of a
relationship. If the relationship stated by the operator is true, the value of
the result is 1. If false, the value of the result is 0. The result is not an
lvalue.
The following table describes the four relational operators:
Operator Usage
< Indicates whether the value of the left operand is less than the
value of the right operand.
> Indicates whether the value of the left operand is greater than the
value of the right operand.
<= Indicates whether the value of the left operand is less than or
equal to the value of the right operand.
>= Indicates whether the value of the left operand is greater than or
equal to the value of the right operand.
Both operands must have arithmetic types or be pointers to the same type. The
result has type int. If the operands have arithmetic types, the usual
arithmetic conversions on the operands are performed.
When the operands are pointers, the result is determined by the locations of
the objects to which the pointers refer. If the pointers do not refer to
objects in the same array, the result is not defined.
A pointer can be compared to a constant expression that evaluates to 0. You
can also compare a pointer to a pointer of type void*. The pointer is
converted to a pointer of type void*.
If two pointers refer to the same object, they are considered equal. If two
pointers refer to nonstatic members of the same object, the pointer to the
object declared later has the higher address value. If two pointers refer to
data members of the same union, they have the same address value.
If two pointers refer to elements of the same array, or to the first element
beyond the last element of an array, the pointer to the element with the
higher subscript value has the higher address value.
You can only compare members of the same object with relational operators.
Relational operators have left-to-right associativity. For example, the
expression:
a < b <= c
is interpreted as:
(a < b) <= c
If the value of a is less than the value of b, the first relationship is true
and yields the value 1. The compiler then compares the value 1 with the value
of c.
Related Information
Equality == !=
Logical Negation !
Pointers
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.8. Equality == != ΓòÉΓòÉΓòÉ
The equality operators, like the relational operators, compare two operands for
the validity of a relationship. The equality operators, however, have a lower
precedence than the relational operators. If the relationship stated by an
equality operator is true, the value of the result is 1. Otherwise, the value
of the result is 0.
The following table describes the two equality operators:
Operator Usage
== Indicates whether the value of the left operand is equal to the
value of the right operand.
!= Indicates whether the value of the left operand is not equal to the
value of the right operand.
Both operands must have arithmetic types or be pointers to the same type, or
one operand must have a pointer type and the other operand must be a pointer
to void or NULL. The result has type int.
If the operands have arithmetic types, the usual arithmetic conversions on the
operands are performed. If the operands are pointers, the result is determined
by the locations of the objects to which the pointers refer.
If one operand is a pointer and the other operand is an integer having the
value 0, the == expression is true only if the pointer operand evaluates to
NULL. The != operator evaluates to true if the pointer operand does not
evaluate to NULL.
You can also use the equality operators to compare pointers to members that
are of the same type but do not belong to the same object.
Note: The equality operator (==) should not be confused with the assignment
(=) operator.
For example,
if(x == 3) evaluates to 1 if x is equal to three An equality tests like
this should be coded with spaces between the operator and the
operands to prevent unintentional assignments.
while
if(x = 3) is taken to be true because (x = 3) evaluates to a non-zero
value (3). The expression also assigns the value 3 to x.
Related Information
Relational < > <= >=
Logical Negation !
Pointers
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.9. Bitwise AND & ΓòÉΓòÉΓòÉ
The & (bitwise AND) operator compares each bit of its first operand to the
corresponding bit of the second operand. If both bits are 1's, the
corresponding bit of the result is set to 1. Otherwise, it sets the
corresponding result bit to 0.
Both operands must have an integral type. The usual arithmetic conversions on
each operand are performed. The result has the same type as the converted
operands.
Because the bitwise AND operator has both associative and commutative
properties, the compiler can rearrange the operands in an expression that
contains more than one bitwise AND operator.
The following example shows the values of a, b, and the result of a & b
represented as 32-bit binary numbers:
bit pattern of a 00000000000000000000000001011100
bit pattern of b 00000000000000000000000000101110
bit pattern of a & b 00000000000000000000000000001100
Note: The bitwise AND (&) should not be confused with the logical AND. (&&)
operator. For example,
1 & 4 evaluates to 0
while
1 && 4 evaluates to 1
Related Information
Bitwise Exclusive OR ^
Bitwise Inclusive OR |
Logical AND &&
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.10. Bitwise Exclusive OR ^ ΓòÉΓòÉΓòÉ
The bitwise exclusive OR operator compares each bit of its first operand to the
corresponding bit of the second operand. If both bits are 1's or both bits are
0's, the corresponding bit of the result is set to 0. Otherwise, it sets the
corresponding result bit to 1.
Both operands must have an integral type. The usual arithmetic conversions on
each operand are performed. The result has the same type as the converted
operands and is not an lvalue.
Because the bitwise exclusive OR operator has both associative and commutative
properties, the compiler can rearrange the operands in an expression that
contains more than one bitwise exclusive OR operator even when the
sub-expressions are explicitly grouped with parentheses.
The following example shows the values of a, b, and the result of a ^ b
represented as 32-bit binary numbers:
bit pattern of a 00000000000000000000000001011100
bit pattern of b 00000000000000000000000000101110
bit pattern of a ^ b 00000000000000000000000001110010
Related Information
Bitwise Inclusive OR |
Bitwise AND &
Logical OR ||
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.11. Bitwise Inclusive OR | ΓòÉΓòÉΓòÉ
The | (bitwise inclusive OR) operator compares the values (in binary format) of
each operand and yields a value whose bit pattern shows which bits in either of
the operands has the value 1. If both of the bits are 0, the result of that bit
is 0; otherwise, the result is 1.
Both operands must have an integral type. The usual arithmetic conversions on
each operand are performed. The result has the same type as the converted
operands and is not an lvalue.
Because the bitwise inclusive OR operator has both associative and commutative
properties, the compiler can rearrange the operands in an expression that
contains more than one bitwise inclusive OR operator even when the
subexpressions are explicitly grouped with parentheses.
The following example shows the values of a, b, and the result of a | b
represented as 32-bit binary numbers:
bit pattern of a 00000000000000000000000001011100
bit pattern of b 00000000000000000000000000101110
bit pattern of a | b 00000000000000000000000001111110
Note: The bitwise OR (|) should not be confused with the logical OR (||)
operator. For example,
1 | 4 evaluates to 5
while
1 || 4 evaluates to 1
Related Information
Bitwise Exclusive OR ^
Bitwise AND &
Logical OR ||
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.12. Logical AND && ΓòÉΓòÉΓòÉ
The && (logical AND) operator indicates whether both operands have a nonzero
value. If both operands have nonzero values, the result has the value 1.
Otherwise, the result has the value 0.
Both operands must have a scalar type. The usual arithmetic conversions on each
operand are performed. The result has type int and is not an lvalue.
Unlike the & (bitwise AND) operator, the && operator guarantees left-to-right
evaluation of the operands. If the left operand evaluates to 0, the right
operand is not evaluated.
The following examples show how the expressions that contain the logical AND
operator are evaluated:
Expression Result
1 && 0 0
1 && 4 1
0 && 0 0
The following example uses the logical AND operator to avoid division by zero:
(y != 0) && (x / y)
The expression x / y is not evaluated when y != 0 evaluates to 0.
Note: The logical AND (&&) should not be confused with the bitwise AND (&)
operator. For example:
1 && 4 evaluates to 1
while
1 & 4 evaluates to 0
Related Information
Logical OR ||
Bitwise AND &
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.13. Logical OR || ΓòÉΓòÉΓòÉ
The || (logical OR) operator indicates whether either operand has a nonzero
value. If either operand has a nonzero value, the result has the value 1.
Otherwise, the result has the value 0.
Both operands must have a scalar type. The usual arithmetic conversions on each
operand are performed. The result has type int and is not an lvalue.
Unlike the | (bitwise inclusive OR) operator, the The || operator guarantees
left-to-right evaluation of the operands. If the left operand has a nonzero
value, the right operand is not evaluated.
The following examples show how expressions that contain the logical OR
operator are evaluated:
Expression Result
1 || 0 1
1 || 4 1
0 || 0 0
The following example uses the logical OR operator to conditionally increment
y:
++x || ++y;
The expression ++y is not evaluated when the expression ++x evaluates to a
nonzero quantity.
Note: The logical OR (||) should not be confused with the bitwise OR (|)
operator. For example:
1 || 4 evaluates to 1
while
1 | 4 evaluates to 5
Related Information
Logical AND &&
Bitwise Inclusive OR |
Binary Expressions
Arithmetic Conversions
ΓòÉΓòÉΓòÉ 5.6.14. Pointer to Member Operators .* ->* ΓòÉΓòÉΓòÉ
The .* operator is used to dereference pointers to class members. The first
operand must be a class type. If the type of the first operand is class type T,
or is a class that has been derived from class type T, the second operand must
be a pointer to a member of a class type T.
The ->* operator is also used to dereference pointers to class members. The
first operand must be a pointer to a class type. If the type of the first
operand is a pointer to class type T, or is a pointer to a class derived from
class type T, the second operand must be a pointer to a member of class type T.
The .* and ->* operators bind the second operand to the first, resulting in an
object or function of the type specified by the second operand.
If the result of.* or ->* is a function, you can only use the result as the
operand for the ( ) (function call) operator. If the second operand is an
lvalue, the result of .* or ->* is an lvalue.
For more information on pointer to member operators, see Pointers to Members.
Related Information
Pointers to Members
Pointers
C++ Classes
Binary Expressions
ΓòÉΓòÉΓòÉ 5.7. Conditional Expressions ΓòÉΓòÉΓòÉ
A conditional expression is a compound expression that contains a condition
(operand{1}), an expression to be evaluated if the condition has a nonzero
value (operand{2}), and an expression to be evaluated if the condition has the
value 0 (operand{3}).
Conditional expressions have right-to-left associativity. The left operand is
evaluated first, and then only one of the remaining two operands is evaluated.
The conditional expression contains one two-part operator. The ? symbol
follows the condition, and the : symbol appears between the two action
expressions. All expressions that occur between the ? and : are treated as one
expression.
The first operand must have a scalar type. The type of the second and third
operands must be one of the following:
An arithmetic type
A compatible pointer, structure, or union type
void.
The second and third operands can also be a pointer or a null pointer
constant.
Two objects are compatible when they have the same type but not necessarily
the same type qualifiers (volatile, const, or _Packed). Pointer objects are
compatible if they have the same type or are pointers to void.
The first expression is evaluated first. If the first expression has a nonzero
value, the second expression is evaluated and converted to the result type. It
becomes the value of the conditional expression. The third operand is not
evaluated. If the first operand is zero, the third operand is evaluated.
If the second and third expressions evaluate to arithmetic types, the usual
arithmetic conversions are performed on the values. The types of the second
and third operands determine the type of the result as shown in the following
tables.
ΓòÉΓòÉΓòÉ 5.7.1. Type of Conditional C Expressions ΓòÉΓòÉΓòÉ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé TYPE OF ONE OPERAND Γöé TYPE OF OTHER OPERAND Γöé TYPE OF RESULT Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Arithmetic Γöé Arithmetic Γöé Arithmetic type after Γöé
Γöé Γöé Γöé usual arithmetic con- Γöé
Γöé Γöé Γöé versions Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Structure or union type Γöé Compatible structure or Γöé Structure or union type Γöé
Γöé Γöé union type Γöé with all the qualifiers Γöé
Γöé Γöé Γöé on both operands Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé void Γöé void Γöé void Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Pointer to compatible Γöé Pointer to compatible Γöé Pointer to type with Γöé
Γöé type Γöé type Γöé all the qualifiers Γöé
Γöé Γöé Γöé specified for the type Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Pointer to type Γöé "NULL" pointer (the con- Γöé Pointer to type Γöé
Γöé Γöé stant "0") Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Pointer to object or Γöé Pointer to void Γöé Pointer to void with Γöé
Γöé incomplete type Γöé Γöé all the qualifiers Γöé
Γöé Γöé Γöé specified for the type Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 5.7.2. Type of Conditional C++ Expressions ΓòÉΓòÉΓòÉ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé TYPE OF ONE OPERAND Γöé TYPE OF OTHER OPERAND Γöé TYPE OF RESULT Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Reference to type Γöé Reference to type Γöé Reference after usual Γöé
Γöé Γöé Γöé reference conversions Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Class "T" Γöé Class "T" Γöé Class "T" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Class "T" Γöé Class "X" Γöé Class type for which a Γöé
Γöé Γöé Γöé conversion exists. If Γöé
Γöé Γöé Γöé more than one possible Γöé
Γöé Γöé Γöé conversion exists, the Γöé
Γöé Γöé Γöé result is ambiguous. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé throw expression Γöé Other (type, pointer, Γöé Type of the expression Γöé
Γöé Γöé reference) Γöé that is not a throw Γöé
Γöé Γöé Γöé expression Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Examples of Conditional Expressions
Related Information
Type Specifiers
Declarators
Expressions and Operators
Arithmetic Conversions
Operator Precedence and Associativity
ΓòÉΓòÉΓòÉ <hidden> Examples of Conditional Expressions ΓòÉΓòÉΓòÉ
The following expression determines which variable has the greater value, y or
z, and assigns the greater value to the variable x:
x = (y > z) ? y : z;
The following is an equivalent statement:
if (y > z)
x = y;
else
x = z;
The following expression calls the function printf, which receives the value of
the variable c, if c evaluates to a digit. Otherwise, printf receives the
character constant 'x'.
printf(" c = %c\n", isdigit(c) ? c : 'x');
If the last operand of a conditional expression contains an assignment
operator, use parentheses to ensure the expression evaluates properly. For
example, the = operator has higher precedence than the ?: operator in the
following expression:
int i,j,k;
(i == 7) ? j ++ : k = j;
This expression generates an error because it is interpreted as if it were
parenthesized this way:
int i,j,k;
((i == 7) ? j ++ : k) = j;
That is, k is treated as the third operand, not the entire assignment
expression k = j. The error arises because a conditional expression is not an
lvalue, and the assignment is not valid.
To make the expression evaluate correctly, enclose the last operand in
parentheses:
int i,j,k;
(i == 7) ? j ++ : (k = j);
ΓòÉΓòÉΓòÉ 5.8. Assignment Expressions ΓòÉΓòÉΓòÉ
An assignment expression stores a value in the object designated by the left
operand. There are two types of assignment operators: simple assignment and
compound assignment.
The left operand in all assignment expressions must be a modifiable lvalue. The
type of the expression is the type of the left operand. The value of the
expression is the value of the left operand after the assignment has completed.
The result of an assignment expression is not an lvalue.
All assignment operators have the same precedence and have right-to-left
associativity.
Simple Assignment =
Compound Assignment
Related Information
Expressions and Operators
Operator Precedence and Associativity
ΓòÉΓòÉΓòÉ 5.8.1. Simple Assignment = ΓòÉΓòÉΓòÉ
The simple assignment operator stores the value of the right operand in the
object designated by the left operand.
Both operands must have arithmetic types, the same structure type, or the same
union type. Otherwise, both operands must be pointers to the same type, or the
left operand must be a pointer and the right operand must be the constant 0 or
NULL.
If both operands have arithmetic types, the system converts the type of the
right operand to the type of the left operand before the assignment.
If the right operand is a pointer to a type, the left operand can be a pointer
to a const of the same type. If the right operand is a pointer to a const type,
the left operand must also be a pointer to a const type.
If the right operand is a pointer to a type, the left operand can be a pointer
to a volatile of the same type. If the right operand is a pointer to a volatile
type, the left operand must also be a pointer to a volatile type.
If the left operand is a pointer to a member, the right operand must be a
pointer to a member or a constant expression that evaluates to zero. The right
operand is converted to the type of the left operand before assignment.
If the left operand is an object of reference type, the assignment is to the
object denoted by the reference.
If the left operand is a pointer and the right operand is the constant 0, the
result is NULL.
Pointers to void can appear on either side of the simple assignment operator.
A packed structure or union can be assigned to a nonpacked structure or union
of the same type, and a nonpacked structure or union can be assigned to a
packed structure or union of the same type.
If one operand is packed and the other is not, the layout of the right operand
is remapped to match the layout of the left. This remapping of structures might
degrade performance. For efficiency, when you perform assignment operations
with structures or unions, you should ensure that both operands are either
packed or nonpacked.
Note: If you assign pointers to structures or unions, the objects they point
to must both be either packed or nonpacked. See Pointers for more information
on assignments with pointers.
You can assign values to operands with the type qualifier volatile. You cannot
assign a pointer of an object with the type qualifier const to a pointer of an
object without the const type qualifier. For example:
const int *p1;
int *p2;
p2 = p1; /* this is NOT allowed */
p1 = p2; /* this IS allowed */
Examples of Simple Assignments
Note: The assignment (=) operator should not be confused with the equality
comparison (==) operator. For example:
if(x == 3) evaluates to 1 if x is equal to three
while
if(x = 3) is taken to be true because (x = 3) evaluates to a non-zero value
(3). The expression also assigns the value 3 to x.
Related Information
Compound Assignment
Equality == !=
Pointers
volatile and const Qualifiers
Type Specifiers
Arithmetic Conversions
ΓòÉΓòÉΓòÉ <hidden> Examples of Simple Assignments ΓòÉΓòÉΓòÉ
The following example assigns the value of number to the member employee of the
structure payroll:
payroll.employee = number;
The following example assigns in order the value 0 (zero) to strangeness, the
value of strangeness to charm, the value of charm to beauty, and the value of
beauty to truth:
truth = beauty = charm = strangeness = 0;
ΓòÉΓòÉΓòÉ 5.8.2. Compound Assignment ΓòÉΓòÉΓòÉ
The compound assignment operators consist of a binary operator and the simple
assignment operator. They perform the operation of the binary operator on both
operands and give the result of that operation to the left operand.
The following table shows the operand types of compound assignment expressions:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé OPERATOR Γöé LEFT OPERAND Γöé RIGHT OPERAND Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé += or -= Γöé Arithmetic Γöé Arithmetic Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé += or -= Γöé Pointer Γöé Integral type Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé *=, /=, and %= Γöé Arithmetic Γöé Arithmetic Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé <<=, >>=, &=, ^=, and Γöé Integral type Γöé Integral type Γöé
Γöé |= Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Note that the expression a *= b + c is equivalent to a = a * (b + c), and not
a = a * b + c.
The following table lists the compound assignment operators and shows an
expression using each operator:
Operator Example
+= index += 2
Equivalent expression: index = index + 2
-= *(pointer++) -= 1
Equivalent expression: *pointer = *(pointer++) - 1
*= bonus *= increase
Equivalent expression: bonus = bonus * increase
/= time /= hours
Equivalent expression: time = time / hours
%= allowance %= 1000
Equivalent expression: allowance = allowance % 1000
<<= result <<= num
Equivalent expression: result = result << num
>>= form >>= 1
Equivalent expression: form = form >> 1
&= mask &= 2
Equivalent expression: mask = mask & 2
^= test ^= pre_test
Equivalent expression: test = test ^ pre_test
|= flag |= ON
Equivalent expression: flag = flag | ON
Although the equivalent expression column shows the left operands (from the
example column) evaluated twice, the left operand is evaluated only once.
Related Information
Simple Assignment =
Binary Expressions
ΓòÉΓòÉΓòÉ 5.9. Comma Expression , ΓòÉΓòÉΓòÉ
A comma expression contains two operands separated by a comma. Although the
compiler evaluates both operands, the value of the right operand is the value
of the expression. The left operand is evaluated, possibly producing side
effects, and the value is discarded. The result of a comma expression is not an
lvalue.
Both operands of a comma expression can have any type. All comma expressions
have left-to-right associativity. The left operand is fully evaluated before
the right operand.
In the following example, if omega has the value 11, the expression increments
delta and assigns the value 3 to alpha:
alpha = (delta++, omega % 4);
Any number of expressions separated by commas can form a single expression. The
compiler evaluates the leftmost expression first. The value of the rightmost
expression becomes the value of the entire expression.
For example, the value of the expression:
intensity++, shade * increment, rotate(direction);
is the value of the expression:
rotate(direction)
The primary use of the comma operator is to produce side effects in the
following situations:
Calling a function
Entering or repeating an iteration loop
Testing a condition
Other situations where a side effect is required but the result of the
expression is not immediately needed
To use the comma operator in a context where the comma has other meanings,
such as in a list of function arguments or a list of initializers, you must
enclose the comma operator in parentheses. For example, the function
f(a, (t = 3, t + 2), c);
has only three arguments: the value of a, the value 5, and the value of c. The
value of the second argument is the result of the comma expression in
parentheses:
t = 3, t + 2
which has the value 5.
The following table gives some examples of the uses of the comma operator:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé STATEMENT Γöé EFFECTS Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé for (i=0; i<2; ++i, f() ); Γöé A for statement in which "i" is incre- Γöé
Γöé Γöé mented and "f()" is called at each iter- Γöé
Γöé Γöé ation. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé if ( f(), ++i, i>1 ) Γöé An if statement in which function "f()" Γöé
Γöé { /* ... */ } Γöé is called, variable "i" is incremented, Γöé
Γöé Γöé and variable "i" is tested against a Γöé
Γöé Γöé value. The first two expressions within Γöé
Γöé Γöé this comma expression are evaluated Γöé
Γöé Γöé before the expression "i>1". Regardless Γöé
Γöé Γöé of the results of the first two Γöé
Γöé Γöé expressions, the third is evaluated and Γöé
Γöé Γöé its result determines whether the if Γöé
Γöé Γöé statement is processed. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé func( ( ++a, f(a) ) ); Γöé A function call to "func()" in which "a" Γöé
Γöé Γöé is incremented, the resulting value is Γöé
Γöé Γöé passed to a function "f()", and the Γöé
Γöé Γöé return value of "f()" is passed to Γöé
Γöé Γöé "func()". The function "func()" is Γöé
Γöé Γöé passed only a single argument, because Γöé
Γöé Γöé the comma expression is enclosed in Γöé
Γöé Γöé parentheses within the function argument Γöé
Γöé Γöé list. Γöé
Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Related Information
Expressions and Operators
Operator Precedence and Associativity
ΓòÉΓòÉΓòÉ 6. Implicit Type Conversions ΓòÉΓòÉΓòÉ
There are two kinds of implicit type conversions: standard conversions and
user-defined conversions This chapter describes the following standard type
conversions:
integral promotions.
Implicit standard type conversions:
- Signed-integer conversions
- Unsigned-integer conversions
- Floating-point conversions
- Pointer conversions
- Reference conversions
- Pointer-to-member conversions
- Function argument conversions
- Other conversions
Arithmetic conversions.
Related Information
Expressions and Operators
Functions
Cast Expressions.
User-Defined Conversions.
The VisualAge C++ implementation of type conversions is described in the IBM
VisualAge C++ for OS/2 User's Guide and Reference.
ΓòÉΓòÉΓòÉ 6.1. Integral Promotions ΓòÉΓòÉΓòÉ
Certain fundamental types can be used wherever an integer can be used. The
fundamental types that can be converted through integral promotion are:
char
wchar_t
short int
enumerators
objects of enumeration type
integer bit-fields (both signed and unsigned)
Except for wchar_t, if the value cannot be represented by an int, the value is
converted to an unsigned int. For wchar_t, if an int can represent all the
values of the original type, the value is converted to the type that can best
represent all the values of the original type. For example, if a long can
represent all the values, the value is converted to a long.
ΓòÉΓòÉΓòÉ 6.2. Standard Type Conversions ΓòÉΓòÉΓòÉ
Many C and C++ operators cause implicit type conversions, which change the type
of a value. When you add values having different data types, both values are
first converted to the same type. For example, when a short int value and an
int value are added together, the short int value is converted to the int type.
Implicit type conversions can occur when:
An operand is prepared for an arithmetic or logical operation.
An assignment is made to an lvalue that has a different type than the
assigned value.
A prototyped function is provided a value that has a different type than
the parameter.
The value specified in the return statement of a function has a different
type from the defined return type for the function.
You can perform explicit type conversions using the cast operator or the
function style cast. For more information on explicit type conversions, see
Cast Expressions.
ΓòÉΓòÉΓòÉ 6.2.1. Signed-Integer Conversions ΓòÉΓòÉΓòÉ
The compiler converts a signed integer to a shorter integer by truncating the
high-order bits and converting the variable to a longer signed integer by
sign-extension.
Conversion of signed integers to floating-point values takes place without loss
of information, except when an int or long int value is converted to a float,
in which case some precision may be lost. When a signed integer is converted to
an unsigned integer, the signed integer is converted to the size of the
unsigned integer, and the result is interpreted as an unsigned value.
ΓòÉΓòÉΓòÉ 6.2.2. Unsigned-Integer Conversions ΓòÉΓòÉΓòÉ
An unsigned integer is converted to a shorter unsigned or signed integer by
truncating the high-order bits. An unsigned integer is converted to a longer
unsigned or signed integer by zero-extending. Zero-extending pads the leftmost
bits of the longer integer with binary zeros.
When an unsigned integer is converted to a signed integer of the same size, no
change in the bit pattern occurs. However, the value changes if the sign bit is
set.
ΓòÉΓòÉΓòÉ 6.2.3. Floating-Point Conversions ΓòÉΓòÉΓòÉ
A float value converted to a double undergoes no change in value. A double
converted to a float is represented exactly, if possible. If the compiler
cannot exactly represent the double value as a float, the value loses
precision. If the value is too large to fit into a float, the result is
undefined.
When a floating-point value is converted to an integer value, the decimal
fraction portion of the floating-point value is discarded in the conversion. If
the result is too large for the given integer type, the result of the
conversion is undefined.
ΓòÉΓòÉΓòÉ 6.2.4. Pointer Conversions ΓòÉΓòÉΓòÉ
Pointer conversions are performed when pointers are used, including pointer
assignment, initialization, and comparison.
A constant expression that evaluates to zero can be converted to a pointer.
This pointer will be a null pointer (pointer with a zero value), and is
guaranteed not to point to any object.
Any pointer to an object that is not a const or volatile object can be
converted to a void*. You can also convert any pointer to a function to a
void*, provided that a void* has sufficient bits to hold it.
You can convert an expression with type array of some type to a pointer to the
initial element of the array, except when the expression is used as the operand
of the & (address) operator or the sizeof operator.
You can convert an expression with a type of function returning T to a pointer
to a function returning T, except when the expression is used as the operand of
the & (address) operator, the () (function call) operator, or the sizeof
operator.
You can convert an integer value to an address offset.
You can convert a pointer to a class A to a pointer to an accessible base class
B of that class, as long as the conversion is not ambiguous. The conversion is
ambiguous if the expression for the accessible base class can refer to more
than one distinct class. The resulting value points to the base class subobject
of the derived class object. A null pointer (pointer with a zero value) is
converted into itself.
Note: You cannot convert a pointer to a class into a pointer to its base class
if the base class is a virtual base class of the derived class.
For more information on pointer conversions, see Pointer Arithmetic.
ΓòÉΓòÉΓòÉ 6.2.5. Reference Conversions ΓòÉΓòÉΓòÉ
A reference conversion can be performed wherever a reference initialization
occurs, including reference initialization done in argument passing and
function return values. A reference to a class can be converted to a reference
to an accessible base class of that class as long as the conversion is not
ambiguous. The result of the conversion is a reference to the base class
subobject of the derived class object.
Reference conversion is allowed if the corresponding pointer conversion is
allowed.
ΓòÉΓòÉΓòÉ 6.2.6. Pointer-to-Member Conversions ΓòÉΓòÉΓòÉ
Pointer-to-member conversion can occur when pointers to members are
initialized, assigned, or compared.
A constant expression that evaluates to zero is converted to a distinct pointer
to a member.
Note: A pointer to a member is not the same as a pointer to an object or a
pointer to a function.
A pointer to a member of a base class can be converted to a pointer to a member
of a derived class if the following conditions are true:
The conversion is not ambiguous. The conversion is ambiguous if multiple
instances of the base class are in the derived class.
A pointer to the derived class can be converted to a pointer to the base
class. If this is the case, the base class is said to be accessible. See
Derivation Access of Base Classes for more information.
For more information, see Pointers to Members and Pointer to Member
Operators .* ->*.
ΓòÉΓòÉΓòÉ 6.2.7. Function Argument Conversions ΓòÉΓòÉΓòÉ
If no function prototype declaration is visible when a function is called, the
compiler can perform default argument promotions, which consist of the
following:
Integral promotions
Arguments with type float are converted to type double.
ΓòÉΓòÉΓòÉ 6.2.8. Other Conversions ΓòÉΓòÉΓòÉ
By definition, the void type has no value. Therefore, it cannot be converted to
any other type, and no other value can be converted to void by assignment.
However, a value can be explicitly cast to void.
No conversions between structure or union types are allowed.
There are no standard conversions between class types.
In C, when you define a value using the enum type specifier, the value is
treated as an int. Conversions to and from an enum value proceed as for the int
type.
In C++, you can convert from an enum to any integral type but not from an
integral type to an enum.
ΓòÉΓòÉΓòÉ 6.3. Arithmetic Conversions ΓòÉΓòÉΓòÉ
Most C++ operators perform type conversions to bring the operands of an
expression to a common type or to extend short values to the integer size used
by the machine. The conversions depend on the specific operator and the type of
the operand or operands. However, many operators perform similar conversions on
operands of integer and floating-point types. These standard conversions are
known as the arithmetic conversions because they apply to the types of values
ordinarily used in arithmetic.
Arithmetic conversions are used for matching operands of arithmetic operators.
The VisualAge C++ implementation of type conversions is described in the IBM
VisualAge C++ for OS/2 User's Guide and Reference.
Arithmetic conversion proceeds in the following order:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé OPERAND TYPE Γöé CONVERSION Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has long double type Γöé The other operand is converted to Γöé
Γöé Γöé long double type. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has double type Γöé The other operand is converted to Γöé
Γöé Γöé double. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has float type Γöé The other operand is converted to Γöé
Γöé Γöé float. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has unsigned long int Γöé The other operand is converted to Γöé
Γöé type Γöé unsigned long int. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has unsigned int type and Γöé The operand with unsigned int type Γöé
Γöé the other operand has long int type Γöé is converted to long int. Γöé
Γöé and the value of the unsigned int can Γöé Γöé
Γöé be represented in a long int Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has unsigned int type and Γöé Both operands are converted to Γöé
Γöé the other operand has long int type Γöé unsigned long int Γöé
Γöé and the value of the unsigned int Γöé Γöé
Γöé cannot be represented in a long int Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has long int type Γöé The other operand is converted to Γöé
Γöé Γöé long int. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé One operand has unsigned int type Γöé The other operand is converted to Γöé
Γöé Γöé unsigned int. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Both operands have int type Γöé The result is type int. Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 7. Functions ΓòÉΓòÉΓòÉ
This section describes the structure and use of functions in C and C++. It
discusses the following topics:
C++ Enhancements to C Functions
Function Declarations
Function Definitions
The main() Function
Calling Functions and Passing Arguments
Default Arguments in C++ Functions
C++ Inline Functions
Related Information
Member Functions
Inline Member Functions
C++ Overloading
Special C++ Member Functions
Virtual Functions
Functions specify the logical structure of a program and define how particular
operations are to be implemented.
A function declaration consists of a return type, a name, and an argument
list. It is used to declare the format and existence of a function prior to
its use.
A function definition contains a function declaration and the body of the
function. A function can only have one definition.
Both C++ and ISO/ANSI C use the style of declaration called prototyping. A
prototype refers to the return type, name, and argument list components of a
function. It is used by the compiler for argument type checking and argument
conversions. Prototypes can appear several times in a program, provided the
declarations are compatible. They allow the compiler to check for mismatches
between the parameters of a function call and those in the function
declaration.
C++ Note: C++ functions must use prototypes. They are usually placed in
header files, while function definitions appear in source files. Nonprototype
functions are allowed in C only.
ΓòÉΓòÉΓòÉ 7.1. C++ Enhancements to C Functions ΓòÉΓòÉΓòÉ
The C++ language provides many enhancements to C functions. These are:
Reference arguments
Default arguments
Reference return types
Member functions
Overloaded functions
Operator functions
Constructor and destructor functions
Conversion functions
Virtual functions
Function templates
ΓòÉΓòÉΓòÉ 7.2. Function Declarations ΓòÉΓòÉΓòÉ
A function declaration establishes the name and the parameters of the function.
Syntax of a Function Declaration
A function is declared implicitly by its appearance in an expression if it has
not been defined or declared previously; the implicit declaration is equivalent
to a declaration of extern int func_name(). The default return type of a
function is int.
To indicate that the function does not return a value, declare it with a a
return type of void.
C++ Note: The use of the const and volatile specifiers is only supported by
C++.
ΓòÉΓòÉΓòÉ 7.2.1. C Function Declarations ΓòÉΓòÉΓòÉ
A function cannot be declared as returning a data object having a volatile or
const type but it can return a pointer to a volatile or const object. Also, a
function cannot return a value that has a type of array or function.
If the called function returns a value that has a type other than int, you must
declare the function before the function call. Even if a called function
returns a type int, explicitly declaring the function prior to its call is good
programming practice.
Some declarations do not have parameter lists; the declarations simply specify
the types of parameters and the return values, such as in the following
example:
int func(int,long);
Examples of Function Declarations and Definitions
Related Information
Function Declarator
Function Definitions
Calling Functions and Passing Arguments
extern Storage Class Specifier
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Function Declaration ΓòÉΓòÉΓòÉ
A function declaration has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇfunction_declaratorΓöÇΓöÇ(ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ>
Γö£ΓöÇexternΓöÇΓöñ ΓööΓöÇtype_specifierΓöÇΓöÿ ΓööΓöÇparameterΓöÇΓöÿ
ΓööΓöÇstaticΓöÇΓöÿ
>ΓöÇΓöÇ)ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
Γö£ΓöÇconstΓöÇΓöÇΓöÇΓöÇΓöñ
ΓööΓöÇvolatileΓöÇΓöÿ
C++ Note: The use of the const and volatile specifiers is only supported by
C++.
ΓòÉΓòÉΓòÉ 7.2.2. C++ Function Declarations ΓòÉΓòÉΓòÉ
In C++, you can specify the qualifiers volatile and const in member function
declarations. You can also specify exception specifications in function
declarations. All C++ functions must be declared before they can be called.
Types cannot be defined in return or argument types. For example, the following
declarations are not valid in C++:
void print(struct X { int i; } x); //error
enum count{one, two, three} counter(); //error
This example attempts to declare a function print() that takes an object x of
class X as its argument. However, the class definition is not allowed within
the argument list. In the attempt to declare counter(), the enumeration type
definition cannot appear in the return type of the function declaration. The
two function declarations and their corresponding type definitions can be
rewritten as follows:
struct X { int i; };
void print(X x);
enum count {one, two, three};
count counter();
Multiple Function Declarations
All function declarations for a particular function must have the same number
and type of arguments, and must have the same return type and the same linkage
keywords. These return and argument types are part of the function type,
although the default arguments are not.
For the purposes of argument matching, ellipsis and linkage keywords are
considered a part of the function type. They must be used consistently in all
declarations of a function. If the only difference between the argument types
in two declarations is in the use of typedef names or unspecified argument
array bounds, the declarations are the same. A const or volatile specifier is
also part of the function type, but can only be part of a declaration or
definition of a nonstatic member function.
Declaring two functions differing only in return type is not valid function
overloading, and is flagged as an error. For example:
void f();
int f(); // error, two definitions differ only in
// return type
int g()
{
return f();
}
Checking Function Calls
The compiler checks C++ function calls by comparing the number and type of the
actual arguments used in the function call with the number and type of the
formal arguments in the function declaration. Implicit type conversion is
performed when necessary.
Argument Names in Function Declarations
You can supply argument names in a function declaration, but the compiler
ignores them except in the following two situations:
1. If two argument names have the same name within a single declaration.
This is an error.
2. If an argument name is the same as a name outside the function. In this
case the name outside the function is hidden and cannot be used in the
argument declaration.
In the following example, the third argument intersects is meant to have
enumeration type subway_line, but this name is hidden by the name of the first
argument. The declaration of the function subway() causes a compile-time error
because subway_line is not a valid type name in the context of the argument
declarations.
enum subway_line {yonge, university, spadina, bloor};
int subway(char * subway_line, int stations,
subway_line intersects);
ΓòÉΓòÉΓòÉ <hidden> Examples of Function Declarations and Definitions ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example defines the function absolute with the return type
double. Because this is a noninteger return type, absolute is declared prior to
the function call.
*
************************************************************************/
/**
** This example shows how a function is declared and defined
**/
#include <stdio.h>
double absolute(double);
int main(void)
{
double f = -3.0;
printf("absolute number = %lf\n", absolute(f));
return (0);
}
double absolute(double number)
{
if (number < 0.0)
number = -number;
return (number);
}
/************************************************************************
*
Specifying a return type of void on a function declaration indicates that the
function does not return a value. The following example defines the function
absolute with the return type void. Within the function main, absolute is
declared with the return type void.
*
************************************************************************/
/**
** This example uses a function with a void return type
**/
#include <stdio.h>
int main(void)
{
void absolute(float);
float f = -8.7;
absolute(f);
return(0);
}
void absolute(float number)
{
if (number < 0.0)
number = -number;
printf("absolute number = %f\n", number);
}
The following code fragments show several function declarations. The first
declares a function f that takes two integer arguments and has a return type of
void:
void f(int, int);
The following code fragment declares a function f1 that takes an integer
argument, and returns a pointer to a function that takes an integer argument
and returns an integer:
int (*f1(int))(int);
Alternatively, a typedef can be used for the complicated return type of
function f1:
typedef int pf1(int);
pf1* f1(int);
The following code fragment declares a pointer p1 to a function that takes a
pointer to a constant character and returns an integer:
int (*p1) (const char*);
The following declaration is of an external function f2 that takes a constant
integer as its first argument, can have a variable number and variable types of
other arguments, and returns type int.
int extern f2(const int ...);
Function f3 takes an int argument with a default value that is the value
returned from function f2, and that has a return type of int:
const int j = 5;
int f3( int x = f2(j) );
Function f6 is a constant class member function of class X with no arguments,
and with an int return type:
class X
{
public:
int f6() const;
};
Function f4 takes no arguments, has return type void, and can throw class
objects of types X and Y.
class X;
class Y;
// .
// .
// .
void f4() throw(X,Y);
Function f5 takes no arguments, has return type void, and cannot throw an
exception.
void f5() throw();
ΓòÉΓòÉΓòÉ 7.3. Function Definitions ΓòÉΓòÉΓòÉ
A function definition contains a function declaration and the body of a
function. It specifies the function name, formal parameters, the return type,
and storage class of the function.
Syntax of a Function Definition
A function definition (either prototype or nonprototype) contains the
following:
An optional storage class specifier extern or static, which determines
the scope of the function. If a storage class specifier is not given, the
function has external linkage.
An optional linkage keyword, which determines the calling convention used
to call the function. The keywords are a VisualAge C++ extension to the
ISO/ANSI C definition. The default VisualAge C++ calling convention is
_Optlink. The _Optlink calling convention is described in the IBM
VisualAge C++ for OS/2 User's Guide and Reference.
An optional type specifier, which determines the type of value that the
function returns. If a type specifier is not given, the function has type
int.
A function declarator, which provides the function with a name, can
further describe the type of the value that the function returns, and can
list any parameters that the function expects and their types. The
parameters that the function is expecting are enclosed in parentheses.
A block statement, which contains data definitions and code.
A nonprototype function definition can also have a list of parameter
declarations, which describe the types of parameters that the function
receives. In nonprototype functions, parameters that are not declared have
type int.
A function can be called by itself or by other functions. Unless a function
definition has the storage class specifier static, the function also can be
called by functions that appear in other files. Functions with a storage class
specifier of static can only be directly invoked from within the same source
file.
If a function has the storage class specifier static or a return type other
than int, the function definition or a declaration for the function must
appear before, and in the same file as, a call to the function. If a function
definition has external linkage and a return type of int, calls to the
function can be made before it is visible because an implicit declaration of
extern int func(); is assumed.
All declarations for a given function must be compatible; that is, the return
type is the same and the parameters have the same type.
The default type for the return value and parameters of a function is int, and
the default storage class specifier is extern. If the function does not return
a value or it is not passed any parameters, use the keyword void as the type
specifier.
A function can return a pointer or reference to a function, array, or to an
object with a volatile or const type. In C, you cannot declare a function as a
struct or union member. (This restriction does not apply to C++.)
A function cannot have a return type of function or array. In C, a function
cannot return any type having the volatile or const qualifier. (This
restriction does not apply to C++.)
You cannot define an array of functions. You can, however, define an array of
pointers to functions.
Examples of Function Definitions
Related Information
Storage Class Specifiers
Linkage Keywords
Type Specifiers
Function Declarator
Block
volatile and const Qualifiers
Function Declarations
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Function Definition ΓòÉΓòÉΓòÉ
A function definition has the form:
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ>
Γö£ΓöÇexternΓöÇΓöñ ΓööΓöÇlinkage_specifierΓöÇΓöÿ ΓööΓöÇtype_specifierΓöÇΓöÿ
ΓööΓöÇstaticΓöÇΓöÿ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>ΓöÇΓöÇfunction_declaratorΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇblock_statementΓöÇΓöÇ><
ΓööΓöÇparameter_declarationΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Examples of Function Definitions ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, ary is an array of two function pointers. Type
casting is performed to the values assigned to ary for compatibility:
*
************************************************************************/
/**
** This example uses an array of pointers to functions
**/
#include <stdio.h>
int func1(void);
void func2(double a);
int main(void)
{
double num;
int retnum;
void (*ary[2]) ();
ary[0] = ((void(*)())func1);
ary[1] = ((void(*)())func2);
retnum=((int (*)())ary[0])(); /* calls func1 */
printf("number returned = %i\n", retnum);
((void (*)(double))ary[1])(num); /* calls func2 */
return(0);
}
int func1(void)
{
int number=3;
return number;
}
void func2(double a)
{
a=333.3333;
printf("result of func2 = %f\n", a);
}
/************************************************************************
*
The following example is a complete definition of the function sum:
int sum(int x,int y)
{
return(x + y);
}
The function sum has external linkage, returns an object that has type int, and
has two parameters of type int declared as x and y. The function body contains
a single statement that returns the sum of x and y.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 7.3.1. Linkage Keywords ΓòÉΓòÉΓòÉ
Use linkage keywords to set linkage conventions for function calls. Each
linkage convention has a corresponding keyword that you can use with a function
name to set the convention for that function. The keywords are:
_Optlink VisualAge C++ default linkage for C++
non-member functions
_System OS/2 system linkage
_Pascal 32-bit _Pascal linkage
_Far32 _Pascal 32-bit far _Pascal linkage
_Far16 _Cdecl 16-bit __cdecl linkage
_Far16 _Pascal 16-bit _Pascal linkage
_Far16 _Fastcall 16-bit _Fastcall linkage
Note:
1. You must specify the _Far16 keyword to use a 16-bit calling convention.
If you specify only _Far16, the _Far16 _Cdecl linkage is used.
2. Do not confuse the 32-bit _Pascal linkage with the 16-bit _Far16 _Pascal
linkage. If you specify only the _Pascal keyword, the 32-bit convention
is used.
3. _Far32 _Pascal linkage is only available when the /Gr+ option is
specified.
You can use the linkage keywords at any language level. To set the calling
convention for a function, place the linkage keyword immediately before the
function name. For example,
int _System deborah(int);
char (* _Far16 _Cdecl donna)(int);
Linkage keywords take precedence over the compiler options that set calling
conventions (/Mp and /Ms). If you specify conflicting linkage types for a
function using both a #pragma linkage directive and a keyword, an error
message is generated and your program will not compile.
Note: Using a keyword is generally quicker and easier than using a #pragma
linkage and they let you declare both the function and its linkage type
in one statement. Because the #pragma linkage directive, is obsolete in
this release of VisualAge C++, linkage keywords are the preferred
method for setting the calling conventions. Avoid using it in new code.
For your new applications, use linkage keywords to specify the calling
convention for a function.
#pragma linkage is not supported at all for C++ functions.
For more information on the different calling conventions and how they work,
see the IBM VisualAge C++ for OS/2 Programming Guide.
Related Information
#pragma linkage
/Mp and /Ms options
Function Declarations
Function Definitions
ΓòÉΓòÉΓòÉ 7.3.2. Inline Specifiers ΓòÉΓòÉΓòÉ
VisualAge C++ provides two keywords, _Inline for C programs and inline for C++
programs, that you can use to specify the user functions you want the compiler
to inline. For example:
_Inline int catherine(int a);
causes catherine to be inlined, meaning that code is generated for the
function, rather than a function call. The inline keywords also implicitly
declare the function as static.
By default, function inlining is turned off, and functions qualified with
_Inline or inline are treated simply as static functions. To turn on function
inlining, specify the /Oi+ option. If you turn optimization on (/O+), /Oi+
becomes the default.
Recursive functions (functions that call themselves) are inlined for the first
occurrence only. The call to the function from within itself will not be
inlined.
You can also use the /Oivalue option to automatically inline all functions
smaller than value abstract code units as well as those qualified with _Inline
or inline. For best performance, use the inline keywords to choose the
functions you want to inline rather than using automatic inlining.
When inlining is turned on, the following functions are also considered
candidates to be inlined:
C++ member functions that are defined in class declarations.
For C programs only, small functions of static storage class that are
called only once.
Note: If you plan to debug your code (specifying /Ti+), you should turn
inlining off. You should also be aware that profiling hooks are not generated
for inlined functions.
For more information on function inlining, see the IBM VisualAge C++ for OS/2
User's Guide and Reference.
Related Information
static Storage Class Specifier
Storage Class Specifiers
C++ Inline Functions
Inline Member Functions
/Oi option
ΓòÉΓòÉΓòÉ 7.3.3. _Export Qualifier ΓòÉΓòÉΓòÉ
Use the _Export keyword with a function name to declare that the function is to
be exported, that is, made available to other modules. For example:
int _Export anthony(float);
causes the function anthony to be exported.
You can use _Export at any language level. If you also use linkage keywords,
you can place _Export either before or after a linkage keyword. For example,
both of the following declarations are valid:
int _Export _Optlink brian(int);
int _Optlink _Export brian(int);
The _Export keyword is an alternative to the #pragma export directive. Note
that #pragma export lets you specify both a name and an ordinal number the
function can be called by. If you use _Export, other modules must call the
function using its original name.
If you use _Export to export your function, you may still need to provide an
EXPORTS entry for that function in your module definition (.DEF) file. If your
function has any of the following default characteristics
Has shared data
Has no I/O privileges
Is not resident
it does not require an EXPORTS entry. If your function has characteristics
other than the defaults, the only way you can specify them is with an EXPORTS
entry in your .DEF file.
Note: To create an import library for the DLL, you must either create it from
the DLL itself or provide a .DEF file with an EXPORTS entry for every
function, regardless of whether _Export is used.
For more information on DLLs and .DEF files, see the IBM VisualAge C++ for
OS/2 Programming Guide.
Related Information
#pragma export
Declarators
volatile and const Qualifiers
_Packed Qualifier
_Seg16 Type Qualifier
ΓòÉΓòÉΓòÉ 7.3.4. Function Declarator ΓòÉΓòÉΓòÉ
The function declarator names the function and lists the function parameters.
It contains an identifier that names the function and a list of the function
parameters. You should always use prototype function declarators because of the
parameter checking that can be performed. C++ functions must have prototype
function declarators.
Syntax of a Function Declarator
Prototype Function Declarators
Each parameter should be declared within the function declarator. Any calls to
the function must pass the same number of arguments as there are parameters in
the declaration.
Nonprototype Function Declarators
Each parameter should be declared in a parameter declaration list following the
declarator. If a parameter is not declared, it has type int.
char and short parameters are widened to int, and float to double. No type
checking between the argument type and the parameter type is done for
nonprototyped functions. As well, there are no checks to ensure that the number
of arguments matches the number of parameters.
Each value that a function receives should be declared in a parameter
declaration list for nonprototype function definitions that follows the
declarator.
A parameter declaration determines the storage class specifier and the data
type of the value.
The only storage class specifier allowed is the register storage class
specifier. Any type specifier for a parameter is allowed. If you do not specify
the register storage class specifier, the parameter will have the auto storage
class specifier. If you omit the type specifier and you are not using the
prototype form to define the function, the parameter will have type int.
int func(i,j)
{
/* i and j have type int */
}
You cannot declare a parameter in the parameter declaration list if it is not
listed within the declarator.
Related Information
Function Declarations
Function Definitions
Function Body
Functions
Declarations
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Function Declarator ΓòÉΓòÉΓòÉ
A function declarator has the form:
>>ΓöÇΓöÇdeclaratorΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇparameter_declaration_listΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
Γöé ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇidentifierΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
A parameter declaration list has the form:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé
>>ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇstorage_class_specifierΓöÇΓö¼Γö┤ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓö┤ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
Γö£ΓöÇtype_specifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ Γö£ΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇdeclaratorΓöÇΓöÇΓöÇΓöñ ΓööΓöÇ,ΓöÇΓöÇ...ΓöÇΓöÿ
ΓööΓöÇtype_qualifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ Γöé ΓööΓöÇ*ΓöÇΓöÿ Γöé
ΓööΓöÇabstract_declaratorΓöÇΓöÿ
An abstract declarator has the form:
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇΓö¼ΓöÇ(ΓöÇΓöÇabstract_declaratorΓöÇΓöÇ)ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ*ΓöÇΓöÿ ΓööΓöÇΓöñ direct_abstract_declarator Γö£ΓöÇΓöÿ
DIRECT_ABSTRACT_DECLARATOR:
Γö£ΓöÇΓöÇabstract_declaratorΓöÇΓöÇΓö¼ΓöÇ[ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ]ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöñ
Γöé ΓööΓöÇconstant_expressionΓöÇΓöÿ Γöé
ΓööΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÿ
ΓööΓöÇparameter_declaration_listΓöÇΓöÿ
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
ΓòÉΓòÉΓòÉ 7.3.5. Ellipsis and void ΓòÉΓòÉΓòÉ
An ellipsis at the end of an parameter declaration indicates that the number of
arguments is equal to, or greater than, the number of specified argument types.
At least one parameter declaration must come before the ellipsis. Where it is
permitted, an ellipsis preceded by a comma is equivalent to a simple ellipsis.
int f(int,...);
The comma before the ellipsis is optional in C++ only
Parameter promotions are performed as needed, but no type checking is done on
the variable arguments.
You can declare a function with no arguments in two ways:
int f(void); // ISO/ANSI C Standard
int f(); // C++ enhancement
// Note: In ISO/ANSI C, this declaration means that
// f may take any number or type or parameters
An empty argument declaration list or the argument declaration list of (void)
indicates a function that takes no arguments. void cannot be used as an
argument type, although types derived from void (such as pointers to void) can
be used.
In the following example, the function f() takes one integer parameter and
returns no value, while g() expects no parameters and returns an integer.
void f(int);
int g(void);
ΓòÉΓòÉΓòÉ 7.3.6. Function Body ΓòÉΓòÉΓòÉ
The body of a function is a block statement.
The following function body contains a definition for the integer variable
big_num, an if-else control statement, and a call to the function printf:
void largest(int num1, int num2)
{
int big_num;
if (num1 >= num2)
big_num = num1;
else
big_num = num2;
printf("big_num = %d\n", big_num);
}
Examples of Prototype Function Declarators
ΓòÉΓòÉΓòÉ <hidden> Examples of Prototype Function Declarators ΓòÉΓòÉΓòÉ
The following example contains a function declarator sort with table declared
as a pointer to int and length declared as type int. Note that arrays as
parameters are implicitly converted to a pointer to the type.
/**
** This example illustrates function declarators.
** Note that arrays as parameters are implicitly
** converted to a pointer to the type.
**/
#include <stdio.h>
void sort(int table[ ], int length);
int main(void)
{
int table[ ]={1,5,8,4};
int length=4;
printf("length is %d\n",length);
sort(table,length);
}
void sort(int table[ ], int length)
{
int i, j, temp;
for (i = 0; i < length -1; i++)
for (j = i + 1; j < length; j++)
if (table[i] > table[j])
{
temp = table[i];
table[i] = table[j];
table[j] = temp;
}
}
The following examples contain prototype function declarators:
double square(float x);
int area(int x,int y);
static char *search(char);
The following example illustrates how a typedef identifier can be used in a
function declarator:
typedef struct tm_fmt { int minutes;
int hours;
char am_pm;
} struct_t;
long time_seconds(struct_t arrival)
The following function set_date declares a pointer to a structure of type date
as a parameter. date_ptr has the storage class specifier register.
set_date(register struct date *date_ptr)
{
date_ptr->mon = 12;
date_ptr->day = 25;
date_ptr->year = 87;
}
Related Information
Block
Function Definitions
Function Declarations
ΓòÉΓòÉΓòÉ 7.4. The main() Function ΓòÉΓòÉΓòÉ
When a program begins running, the system automatically calls the function
main, which marks the entry point of the program. Every program must have one
function named main. No other function in the program can be called main.
Syntax of the main Function
By default, main has the storage class extern and a return type of int. It can
also be declared to return void.
In C++, you cannot use the inline or static specifiers when declaring main. You
cannot call main from within a program or take the address of main.
ΓòÉΓòÉΓòÉ 7.4.1. Arguments to main ΓòÉΓòÉΓòÉ
Syntax of the Arguments to main
The function main can be declared with or without arguments that pass program
parameters and environment settings to the program. Although any name can be
given to these parameters, they are usually referred to as argc, argv, and
envp.
argc Is the argument count. It has type int and indicates how many
arguments are entered on the command line.
argv Is the argument vector. It is an array of pointers to char
array objects. These char objects are null-terminated strings
that are the program arguments passed to the program when it
is invoked.
envp Is an optional environment pointer. It is an array of pointers
to char objects that are the environment variables available
to the program. These have the form name=value. The system
determines the value of this parameter during program
initialization (before calling main). Because you can use the
function getenv to get the value of these pointers, there is
usually no need to declare this parameter.
The value of argc indicates the number of pointers in the array argv. If a
program name is available, the first element in argv points to a character
array that contains the program name or the invocation name of the program
that is being run. If the name cannot be determined, the first element in argv
points to a null character.
This name is counted as one of the arguments to the function main. For
example, if only the program name is entered on the command line, argc has a
value of 1 and argv[0] points to the program name.
Regardless of the number of arguments entered on the command line, argv[argc]
always contains NULL.
Example of Arguments to main
Related Information
Function Definitions
Calling Functions and Passing Arguments
Type Specifiers
Identifiers
Block
ΓòÉΓòÉΓòÉ <hidden> Syntax of the main Function ΓòÉΓòÉΓòÉ
The main function has the form:
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇmainΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇblock_statementΓöÇΓöÇ><
Γö£ΓöÇvoidΓöÇΓöñ Γö£ΓöÇvoidΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓööΓöÇintΓöÇΓöÇΓöÿ ΓööΓöÇparametersΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Syntax of the Arguments to main ΓòÉΓòÉΓòÉ
The main function has the following arguments:
>>ΓöÇΓöÇintΓöÇΓöÇargcΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ, char*ΓöÇΓöÇargvΓöÇΓöÇ[]ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ
ΓööΓöÇ, char*ΓöÇΓöÇenvpΓöÇΓöÇ[]ΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Example of Arguments to main ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program backward prints the arguments entered on a command line
such that the last argument is printed first:
*
************************************************************************/
#include <stdio.h>
int main(int argc, char *argv[])
{
while (--argc > 0)
printf("%s ", argv[argc]);
}
/************************************************************************
*
Invoking this program from a command line with the following:
backward string1 string2
gives the following output:
string2 string1
The arguments argc and argv would contain the following values:
Object Value
argc 3
argv[0] pointer to string "backward"
argv[1] pointer to string "string1"
argv[2] pointer to string "string2"
argv[3] NULL
*
************************************************************************/
ΓòÉΓòÉΓòÉ 7.5. Calling Functions and Passing Arguments ΓòÉΓòÉΓòÉ
A function call specifies a function name and a list of arguments. The calling
function passes the value of each argument to the specified function. The
argument list is surrounded by parentheses, and each argument is separated by a
comma. The argument list can be empty. When a function is called, the actual
arguments are used to initialize the formal arguments.
The type of an actual argument is checked against the type of the corresponding
formal argument in the function prototype. All standard and user-defined type
conversions are applied as necessary.
For example:
#include <iostream.h>
#include <math.h>
extern double root(double, double); // declaration
double root(double value, double base) // definition
{
double temp = exp(log(value)/base);
return temp;
}
void main()
{
int value = 144;
int base = 2;
// Call function root and print return value
cout << "The root is: " << root(value,base) << endl;
}
The output is The root is: 12
In the above example, because the function root is expecting arguments of type
double, the two int arguments value and base are implicitly converted to type
double when the function is called.
The arguments to a function are evaluated before the function is called. When
an argument is passed in a function call, the function receives a copy of the
argument value. If the value of the argument is an address, the called function
can use indirection to change the contents pointed to by the address. If a
function or array is passed as an argument, the argument is converted to a
pointer that points to the function or array.
Arguments passed to parameters in prototype declarations will be converted to
the declared parameter type. For nonprototype function declarations, char and
short parameters are promoted to int, and float to double.
You can pass a packed structure argument to a function expecting a nonpacked
structure of the same type and vice versa. (The same applies to packed and
nonpacked unions.)
Note: If you do not use a function prototype and you send a packed structure
when a nonpacked structure is expected, a runtime error may occur.
The order in which arguments are evaluated and passed to the function is
implementation-defined. For example, the following sequence of statements calls
the function tester:
int x;
x = 1;
tester(x++, x);
The call to tester in the example may produce different results on different
compilers. Depending on the implementation, x++ may be evaluated first or x may
be evaluated first. To avoid the ambiguity and have x++ evaluated first,
replace the preceding sequence of statements with the following:
int x, y;
x = 1;
y = x++;
tester(y, x);
ΓòÉΓòÉΓòÉ 7.5.1. Passing Arguments in C++ ΓòÉΓòÉΓòÉ
In C++, if a nonstatic class member function is passed as an argument, the
argument is converted to a pointer to member.
Passing a class object by value is actually passed by reference if the class
has a destructor or the class has a copy constructor that does more than a
bitwise copy.
It is an error when a function argument is a class object and all of the
following properties hold:
The class needs a copy constructor.
The class does not have a user-defined copy constructor.
A copy constructor cannot be generated for that class.
Examples of Calling Functions and Passing Arguments
Related Information
Passing Arguments by Reference
Constructors and Destructors Overview
Member Functions
Function Declarator
Function Declarations
Function Definitions
Type Specifiers
ΓòÉΓòÉΓòÉ <hidden> Examples of Calling Functions ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following statement calls the function startup and passes no parameters:
startup();
The following function call causes copies of a and b to be stored in a local
area for the function sum. The function sum runs using the copies of a and b.
sum(a, b);
The following function call passes the value 2 and the value of the expression
a +b to sum:
sum(2, a + b);
The following statement calls the function printf, which receives a character
string and the return value of the function sum, which receives the values of a
and b:
printf("sum = %d\n", sum(a,b));
The following program passes the value of count to the function increment.
increment increases the value of the parameter x by 1.
*
************************************************************************/
/**
** This example shows how a parameter is passed to a function
**/
#include <stdio.h>
void increment(int);
int main(void)
{
int count = 5;
/* value of count is passed to the function */
increment(count);
printf("count = %d\n", count);
return(0);
}
void increment(int x)
{
++x;
printf("x = %d\n", x);
}
/************************************************************************
*
The output illustrates that the value of count in main remains unchanged:
x = 6
count = 5
In the following program, main passes the address of count to increment. The
function increment was changed to handle the pointer. The parameter x is
declared as a pointer. The contents to which x points are then incremented.
*
************************************************************************/
/**
** This example shows how an address is passed to a function
**/
#include <stdio.h>
int main(void)
{
void increment(int *x);
int count = 5;
/* address of count is passed to the function */
increment(&count);
printf("count = %d\n", count);
return(0);
}
void increment(int *x)
{
++*x;
printf("*x = %d\n", *x);
}
/************************************************************************
*
The output shows that the variable count is increased:
*x = 6
count = 6
*
************************************************************************/
ΓòÉΓòÉΓòÉ 7.5.2. Passing Arguments by Reference ΓòÉΓòÉΓòÉ
If you use a reference type as a formal argument, you can make a
pass-by-reference call to a function. In a pass-by-reference call, the values
of arguments in the calling function can be modified in the called function. In
pass-by-value calls, only copies of the arguments are passed to the function.
Note: The term pass by reference describes a general method of passing
arguments from a calling routine to a called routine. The term reference in the
context of C++ refers to a specific way of declaring objects and functions.
Example of Passing Arguments by Reference
Ellipsis arguments cannot be passed as references.
In addition, when the actual argument cannot be referenced directly by the
formal argument, the compiler creates a temporary variable that is referenced
by the formal argument and initialized using the value of the actual argument.
In this case, the formal argument must be a const reference.
Reference arguments declared const can be used to pass large objects
efficiently to functions without making a temporary copy of the object that is
passed to the function. Because the reference is declared const, the actual
arguments cannot be changed by the function. For example:
void printbig (const bigvar&); // Function prototype
When a function printbig is called, it cannot modify the object of type bigvar
because the object was passed by constant reference.
Related Information
Calling Functions and Passing Arguments
References
volatile and const Qualifiers
ΓòÉΓòÉΓòÉ <hidden> Example of Passing Arguments by Reference ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows how arguments are passed by reference. Note that
reference formal arguments are initialized with the actual arguments when the
function is called.
*
************************************************************************/
/**
** This example shows how arguments are passed by reference
**/
#include <iostream.h>
void swapnum(int &i, int &j)
{
int temp = i;
i = j;
j = temp;
}
// .
// .
// .
main()
{
int a = 10, // a is 10
b = 20; // b is 20
swapnum(a,b); // now a is 20 and b is 10
cout << "A is : " << a
<< " and B is : " << b << endl;
}
/************************************************************************
*
When the function swapnum() is called, the actual values of the variables a and
b are exchanged because they are passed by reference. The output is:
A is : 20 and B is : 10
You must define the formal arguments of swapnum() as references if you want the
values of the actual arguments to be modified by the function swapnum().
*
************************************************************************/
If the last argument specified in the function declaration before the ellipsis
is a reference argument, arguments passed using an ellipsis (variable
arguments) are accessible using the mechanism from the <stdarg.h> standard
header file.
ΓòÉΓòÉΓòÉ 7.6. Default Arguments in C++ Functions ΓòÉΓòÉΓòÉ
In C++, you can provide default values for function arguments. All default
argument names of a function are bound when the function is declared. All
functions have their types checked at declaration, and are evaluated at each
point of call.
For example:
/**
** This example illustrates default function arguments
**/
#include <iostream.h>
int a = 1;
int f(int a) {return a;}
int g(int x = f(a)) {return f(a);}
int h()
{
a=2;
{
int a = 3;
return g();
}
}
main()
{
cout << h() << endl;
}
This example prints 2 to standard output, because the a referred to in the
declaration of g() is the one at file scope, which has the value 2 when g() is
called. The value of a is determined after entry into function h() but before
the call to g() is resolved.
A default argument can have any type.
A pointer to a function must have the same type as the function. Attempts to
take the address of a function by reference without specifying the type of the
function produce an error. The type of a function is not affected by arguments
with default values.
The following example shows that the fact that a function has default arguments
does not change its type. The default argument allows you to call a function
without specifying all of the arguments, it does not allow you to create a
pointer to the function that does not specify the types of all the arguments.
Function f can be called without an explicit argument, but the pointer
badpointer cannot be defined without specifying the type of the argument:
int f(int = 0);
void g()
{
int a = f(1); // ok
int b = f(); // ok, default argument used
}
int (*pointer)(int) = &f; // ok, type of f() specified (int)
int (*badpointer)() = &f; // error, badpointer and f have
// different types. badpointer must
// be initialized with a pointer to
// a function taking no arguments.
For additional information about default arguments, see:
Restrictions on Default Arguments
Evaluating Default Arguments
Related Information
Calling Functions and Passing Arguments
Functions
ΓòÉΓòÉΓòÉ 7.6.1. Restrictions on Default Arguments ΓòÉΓòÉΓòÉ
Of the operators, only the function call operator and the operator new can have
default arguments when they are overloaded.
Arguments with default values must be the trailing arguments in the function
declaration argument list. For example:
void f(int a, int b = 2, int c = 3); // trailing defaults
void g(int a = 1, int b = 2, int c); // error, leading defaults
void h(int a, int b = 3, int c); // error, default in middle
Once a default argument has been given in a declaration or definition, you
cannot redefine that argument, even to the same value. However, you can add
default arguments not given in previous declarations. For example, the last
declaration below attempts to redefine the default values for a and b:
void f(int a, int b, int c=1); // valid
void f(int a, int b=1, int c); // valid, add another default
void f(int a=1, int b, int c); // valid, add another default
void f(int a=1, int b=1, int c=1); // error, redefined defaults
You can supply any default argument values in the function declaration or in
the definition. All subsequent arguments must have default arguments supplied
in this or a previous declaration of the function.
You cannot use local variables in default argument expressions. For example,
the compiler generates errors for both function g() and function h() below:
void f(int a)
{
int b=4;
void g(int c=a); // Local variable "a" inaccessible
void h(int d=b); // Local variable "b" inaccessible
}
Related Information
Function Calls ( )
new Operator
Default Arguments in C++ Functions
Evaluating Default Arguments
Calling Functions and Passing Arguments
Functions
ΓòÉΓòÉΓòÉ 7.6.2. Evaluating Default Arguments ΓòÉΓòÉΓòÉ
When a function defined with default arguments is called with trailing
arguments missing, the default expressions are evaluated. For example:
void f(int a, int b = 2, int c = 3); // declaration
// ...
int a = 1;
f(a); // same as call f(a,2,3)
f(a,10); // same as call f(a,10,3)
f(a,10,20); // no default arguments
Default arguments are checked against the function declaration and evaluated
when the function is called. The order of evaluation of default arguments is
undefined. Default argument expressions cannot use formal arguments of a
function. For example:
int f(int q = 3, int r = q); // error
The argument r cannot be initialized with the value of the argument q because
the value of q may not be known when it is assigned to r. If the above function
declaration is rewritten:
int q=5;
int f(int q = 3, int r = q); // error
the value of r in the function declaration still produces an error because the
variable q defined outside of the function is hidden by the argument q declared
for the function. Similarly:
typedef double D;
int f(int D, int z = D(5.3) ); // error
Here the type D is interpreted within the function declaration as the name of
an integer. The type D is hidden by the argument D. The cast D(5.3) is
therefore not interpreted as a cast because D is the name of the argument not a
type.
In the following example, the nonstatic member a cannot be used as an
initializer because a does not exist until an object of class X is constructed.
You can use the static member b as an initializer because b is created
independently of any objects of class X. You can declare the member b after its
use as a default argument because the default values are not analyzed until
after the final bracket } of the class declaration.
class X
{
int a;
f(int z = a) ; // error
g(int z = b) ; // valid
static int b;
};
You must put parentheses around default argument expressions that contain
template references. In the following example:
class C {
void f(int i = X<int,5>::y);
};
the compiler cannot tell that the < represents the start of a template argument
list and not the less than operator because the default argument X<int,5>::y
cannot be processed until the end of the class.
To avoid error messages, put parentheses around the expression containing the
default argumement:
class C {
void f( int i = (X<int,5>::y) );
};
Related Information
Default Arguments in C++ Functions
Restrictions on Default Arguments
Calling Functions and Passing Arguments
Functions
Constructors and Destructors Overview
ΓòÉΓòÉΓòÉ 7.7. Function Return Values ΓòÉΓòÉΓòÉ
A value must be returned from a function unless the function has a return type
of void. The return value is specified in a return statement. The following
code fragment shows a function definition, including the return statement:
int add(int i, int j)
{
return i + j; // return statement
}
The function add() can be called as shown in the following code fragment:
int a = 10,
b = 20;
int answer = add(a, b); // answer is 30
In this example, the return statement initializes a variable of the returned
type. The variable answer is initialized with the int value 30. The type of the
returned expression is checked against the returned type. All standard and
user-defined conversions are performed as necessary.
The following return statements show different ways of returning values to a
caller:
return; // Returns no value
return result; // Returns the value of result
return 1; // Returns the value 1
return (x * x); // Returns the value of x * x
Other than main(), if a function that does not have type void returns without a
value (as in the first return statement shown in the example above) the result
returned is undefined. In C++, the compiler issues an error message as well.
If main has a return type of int, and does not contain a return expression, it
returns the value zero.
Each time a function is called, new copies of its local variables are created.
Because the storage for a local variable may be reused after the function has
terminated, a pointer to a local variable or a reference to a local variable
should not be returned.
If a class object is returned, a temporary object may be created if the class
has copy constructors or a destructor.
References can also be used as return types for functions. The reference
returns the lvalue of the object to which it refers. This allows you to place
function calls on the left side of assignment statements. Referenced return
values are used when assignment operators and subscripting operators are
overloaded so that the results of the overloaded operators can be used as
actual values.
Note: Returning a reference to an automatic variable gives unpredictable
results.
Related Information
return
Calling Functions and Passing Arguments
The main() Function
Temporary Objects
Special Overloaded Operators
ΓòÉΓòÉΓòÉ 7.8. Pointers to Functions ΓòÉΓòÉΓòÉ
A pointer to a function points to the address of the function's executable
code. You can use pointers to call functions and to pass functions as arguments
to other functions. You cannot perform pointer arithmetic on pointers to
functions.
The type of a pointer to a function is based on both the return type and
argument types of the function.
A declaration of a pointer to a function must have the pointer name in
parentheses. Without them, the compiler interprets the statement as a function
that returns a pointer to a specified return type. For example:
int *f(int a); // function f returning an int*
int (*g)(int a); // pointer g to a function returning an int
In the first declaration, f is interpreted as a function that takes an int as
argument, and returns a pointer to an int. In the second declaration, g is
interpreted as a pointer to a function that takes an int argument and that
returns an int.
For more information on pointers, see Pointers and Pointer Conversions.
ΓòÉΓòÉΓòÉ 7.9. C++ Inline Functions ΓòÉΓòÉΓòÉ
Inline functions are used in C++ to reduce the overhead of a normal function
call. A function is declared inline by using the specifier inline for C++
functions or _Inline for C functions. The inline specifier is a suggestion to
the compiler that an inline expansion can be performed. Instead of transferring
control to and from the function code segment, a modified copy of the function
body may be substituted directly for the function call.
An inline function can be declared and defined simultaneously. If it is
declared with the keyword inline or _Inline, it can be declared without a
definition. The following code fragment shows an inline function definition.
Note that the definition includes both the declaration and body of the inline
function.
inline int add(int i, int j) { return i + j; }
Both member and nonmember functions can be inline, and both have internal
linkage.
The use of the inline specifier does not change the meaning of the function.
The inline expansion of a function may not preserve the order of evaluation of
the actual arguments.
For more information on inlining, see the IBM VisualAge C++ for OS/2
Programming Guide.
Related Information
Inline Specifiers
Inline Member Functions
Functions
ΓòÉΓòÉΓòÉ 7.10. Resolving Ambiguous Statements in C++ ΓòÉΓòÉΓòÉ
There are situations in C++ where a statement can be parsed as both a
declaration and as an expression. Specifically, a declaration can look like a
function call in certain cases. The compiler resolves these ambiguities by
applying the following rules to the whole statement:
If the statement can be parsed as a declaration but there are no
declaration specifiers in the declaration and the statement is inside the
body of a function, the statement is assumed to be an expression.
The following statement, for example, is a declaration at file scope of
the function f() that returns type int. There is no declaration specifier
and int is the default, but at function scope this is a call to f():
f();
In every other case, if the statement can be parsed as a declaration, it
is assumed to be a declaration. The following statement, for example, is
a declaration of x with redundant parentheses around the declarator, not
a function-style cast of x to type int:
int (x);
In some cases, C++ syntax does not disambiguate between expression statements
and declaration statements. The ambiguity arises when an expression statement
has a function-style cast as its leftmost subexpression. (Note that, because C
does not support function-style casts, this ambiguity does not occur in C
programs.) If the statement can be interpreted both as a declaration and as an
expression, the statement is interpreted as a declaration statement.
Note: The ambiguity is resolved only on a syntactic level. The disambiguation
does not use the meaning of the names, except to assess whether or not they
are type names.
The following expressions disambiguate into expression statements because the
ambiguous subexpression is followed by an assignment or an operator. type_spec
in the expressions can be any type specifier:
type_spec(i)++; // expression statement
type_spec(i,3)<<d; // expression statement
type_spec(i)->l=24; // expression statement
In the following examples, the ambiguity cannot be resolved syntactically, and
the statements are interpreted as declarations. type_spec is any type
specifier:
type_spec(*i)(int); // declaration
type_spec(j)[5]; // declaration
type_spec(m) = { 1, 2 }; // declaration
type_spec(*k) (float(3)); // declaration
The last statement above causes a compile-time error because you cannot
initialize a pointer with a float value.
Any ambiguous statement that is not resolved by the above rules is by default
a declaration statement. All of the following are declaration statements:
type_spec(a); // declaration
type_spec(*b)(); // declaration
type_spec(c)=23; // declaration
type_spec(d),e,f,g=0; // declaration
type_spec(h)(e,3); // declaration
Another C++ ambiguity between expression statements and declaration statements
is resolved by requiring an explicit return type for function declarations
within a block:
a(); // declaration of a function returning int
// and taking no arguments
void func()
{
int a(); // declaration of a function
int b; // declaration of a variable
a(); // expression-statement calling function a()
b; // expression-statement referring to a variable
}
The last statement above does not produce any action. It is semantically
equivalent to a null statement. However, it is a valid C++ statement.
Related Information
Function Declarations
Cast Expressions
ΓòÉΓòÉΓòÉ 8. Statements ΓòÉΓòÉΓòÉ
This section describes the following C and C++ language statements:
Labels
Block
break
continue
do
Expression
for
goto
if
Null Statement
return
switch
while
Related Information
Scope in C
Scope in C++
Declarations
Expressions and Operators
Functions
ΓòÉΓòÉΓòÉ 8.1. Labels ΓòÉΓòÉΓòÉ
A label is an identifier that allows your program to transfer control to other
statements within the same function. It is the only type of identifier that has
function scope. Control is transferred to the statement following the label by
means of the goto or switch statements.
Syntax of a Labelled Statement
The label is the identifier and the colon (:) character.
The case and default labels can only appear within the body of a switch
statement.
comment_complete : ; /* null statement label */
test_for_null : if (NULL == pointer)
Related Information
Scope in C
Scope in C++
goto
switch
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Labelled Statement ΓòÉΓòÉΓòÉ
A labelled statement has the form:
>>ΓöÇΓöÇidentifierΓöÇΓöÇ:ΓöÇΓöÇstatementΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ 8.2. Block ΓòÉΓòÉΓòÉ
A block statement, or compound statement, lets you group any number of data
definitions, declarations, and statements into one statement. All definitions,
declarations, and statements enclosed within a single set of braces are treated
as a single statement. You can use a block wherever a single statement is
allowed.
Syntax of a Block Statement
In C, Any definitions and declarations must come before the statements.
Redefining a data object inside a nested block hides the outer object while the
inner block runs. Defining several variables that have the same identifier can
make a program difficult to understand and maintain. You should avoid
redefining of identifiers within nested blocks. If a data object is usable
within a block and its identifier is not redefined, all nested blocks can use
that data object.
Examples of Block Statements
Related Information
Block Scope Data Declarations
Initialization within Block Statements
Function Body
auto Storage Class Specifier
register Storage Class Specifier
static Storage Class Specifier
extern Storage Class Specifier
ΓòÉΓòÉΓòÉ <hidden> Initialization within Block Statements ΓòÉΓòÉΓòÉ
Initialization of an auto or register variable occurs each time the block is
run from the beginning. If you transfer control from one block to the middle of
another block, initializations are not always performed. You cannot initialize
an extern variable within a block.
A static local object is initialized only once, when control passes through its
declaration for the first time. A static variable initialized with an
expression other than a constant expression is initialized to 0 before its
block is first entered.
C++ Note: Unlike ISO/ANSI C, in C++ it is an error to jump over a declaration
or definition containing an initializer.
The following code produces an error in C++:
goto skiplabel;
int i=3 // error, jumped over declaration of i with initializer
skiplabel: i=4;
When control exits from a block, all objects with destructors that are defined
in the block are destroyed. The destructor for a static local object is called
only if the object was constructed. The destructor must be called before or as
part of the atexit function.
Local variables declared in a block are also destroyed on exit. Automatic
variables defined in a loop are destroyed at each iteration.
Related Information
Block Scope Data Declarations
Block
auto Storage Class Specifier
register Storage Class Specifier
static Storage Class Specifier
extern Storage Class Specifier
Destructors
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Block Statement ΓòÉΓòÉΓòÉ
A block statement has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Γöé
>>ΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ}ΓöÇΓöÇ><
Γö£ΓöÇtype_definitionΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ ΓööΓöÇstatementΓöÇΓöÿ
Γö£ΓöÇfile_scope_data_declarationΓöÇΓöÇΓöñ
ΓööΓöÇblock_scope_data_declarationΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Examples of Block Statements ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program shows how the values of data objects change in nested
blocks:
*
************************************************************************/
1 /**
2 ** This example shows how data objects change in nested blocks.
3 **/
4 #include <stdio.h>
5
6 int main(void)
7 {
8 int x = 1; /* Initialize x to 1 */
9 int y = 3;
10
11 if (y > 0)
12 {
13 int x = 2; /* Initialize x to 2 */
14 printf("second x = %4d\n", x);
15 }
16 printf("first x = %4d\n", x);
17
18 return(0);
19 }
/************************************************************************
*
The program produces the following output:
second x = 2
first x = 1
Two variables named x are defined in main. The definition of x on line 8
retains storage while main is running. However, because the definition of x on
line 13 occurs within a nested block, line 14 recognizes x as the variable
defined on line 13. Because line 16 is not part of the nested block, x is
recognized as the variable defined on line 8.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 8.3. break ΓòÉΓòÉΓòÉ
A break statement lets you end an iterative (do, for, while) or switch
statement and exit from it at any point other than the logical end.
Syntax of a break Statement
In an iterative statement the break statement ends the loop and moves control
to the next statement outside the loop. Within nested statements, the break
statement ends only the smallest enclosing do, for, switch, or while statement.
In a switch body, the break passes control out of the switch body to the next
statement outside the switch body.
A break statement can only appear in the body of an iterative statement or a
switch statement.
Examples of break Statements
Related Information
do
for
switch
while
ΓòÉΓòÉΓòÉ <hidden> Syntax of a break Statement ΓòÉΓòÉΓòÉ
A break statement has the form:
>>ΓöÇΓöÇbreakΓöÇΓöÇ;ΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ <hidden> Examples of break Statements ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows a break statement in the action part of a for
statement. If the ith element of the array string is equal to '\0', the break
statement causes the for statement to end.
for (i = 0; i < 5; i++)
{
if (string[i] == '\0')
break;
length++;
}
The following is an equivalent for statement, if string does not contain any
embedded null characters:
for (i = 0; i < 5; i++)
{
if (string[i] != '\0')
length++;
}
The following example shows a break statement in a nested iterative statement.
The outer loop goes through an array of pointers to strings. The inner loop
examines each character of the string. When the break statement is processed,
the inner loop ends and control returns to the outer loop.
*
************************************************************************/
/**
** This program counts the characters in the strings that are
** part of an array of pointers to characters. The count stops
** when one of the digits 0 through 9 is encountered
** and resumes at the beginning of the next string.
**/
#include <stdio.h>
#define SIZE 3
int main(void)
{
static char *strings[SIZE] = { "ab", "c5d", "e5" };
int i;
int letter_count = 0;
char *pointer;
for (i = 0; i < SIZE; i++) /* for each string */
/* for each character */
for (pointer = strings[i]; *pointer != '\0'; ++pointer)
{ /* if a number */
if (*pointer >= '0' && *pointer <= '9')
break;
letter_count++;
}
printf("letter count = %d\n", letter_count);
return(0);
}
/************************************************************************
*
The program produces the following output:
letter count = 4
The following example is a switch statement that contains several break
statements. Each break statement indicates the end of a specific clause and
ends the switch statement.
/**
** This example shows a switch statement with break statements.
**/
#include <stdio.h>
enum {morning, afternoon, evening} timeofday = morning;
int main(void) {
switch (timeofday) {
case (morning):
printf("Good Morning\n");
break;
case (evening):
printf("Good Evening\n");
break;
default:
printf("Good Day, eh\n");
}
}
*
************************************************************************/
ΓòÉΓòÉΓòÉ 8.4. continue ΓòÉΓòÉΓòÉ
A continue statement lets you end the current iteration of a loop. Program
control is passed from the continue statement to the end of the loop body.
Syntax of a continue Statement
The continue statement ends the processing of the action part of an iterative
(do, for, or while) statement and moves control to the condition part of the
statement. If the iterative statement is a for statement, control moves to the
third expression in the condition part of the statement, then to the second
expression (the test) in the condition part of the statement.
Within nested statements, the continue statement ends only the current
iteration of the do, for, or while statement immediately enclosing it.
A continue statement can only appear within the body of an iterative statement.
Examples of continue Statements
Related Information
do
for
while
ΓòÉΓòÉΓòÉ <hidden> Syntax of a continue Statement ΓòÉΓòÉΓòÉ
A continue statement has the form:
>>ΓöÇΓöÇcontinueΓöÇΓöÇ;ΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ <hidden> Examples of continue Statements ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows a continue statement in a for statement. The
continue statement causes processing to skip over those elements of the array
rates that have values less than or equal to 1.
*
************************************************************************/
/**
** This example shows a continue statement in a for statement.
**/
#include <stdio.h>
#define SIZE 5
int main(void)
{
int i;
static float rates[SIZE] = { 1.45, 0.05, 1.88, 2.00, 0.75 };
printf("Rates over 1.00\n");
for (i = 0; i < SIZE; i++)
{
if (rates[i] <= 1.00) /* skip rates <= 1.00 */
continue;
printf("rate = %.2f\n", rates[i]);
}
return(0);
}
/************************************************************************
*
The program produces the following output:
Rates over 1.00
rate = 1.45
rate = 1.88
rate = 2.00
The following example shows a continue statement in a nested loop. When the
inner loop encounters a number in the array strings, that iteration of the loop
ends. Processing continues with the third expression of the inner loop. The
inner loop ends when the '\0' escape sequence is encountered.
*
************************************************************************/
/**
** This program counts the characters in strings that are part
** of an array of pointers to characters. The count excludes
** the digits 0 through 9.
**/
#include <stdio.h>
#define SIZE 3
int main(void)
{
static char *strings[SIZE] = { "ab", "c5d", "e5" };
int i;
int letter_count = 0;
char *pointer;
for (i = 0; i < SIZE; i++) /* for each string */
/* for each each character */
for (pointer = strings[i]; *pointer != '\0'; ++pointer)
{ /* if a number */
if (*pointer >= '0' && *pointer <= '9')
continue;
letter_count++;
}
printf("letter count = %d\n", letter_count);
return(0);
}
/************************************************************************
*
The program produces the following output:
letter count = 5
*
************************************************************************/
ΓòÉΓòÉΓòÉ 8.5. do ΓòÉΓòÉΓòÉ
A do statement repeatedly runs a statement until the test expression evaluates
to 0. Because of the order of processing, the statement is run at least once.
Syntax of a do Statement
The body of the loop is run before the controlling while clause is evaluated.
Further processing of the do statement depends on the value of the while
clause. If the while clause does not evaluate to 0, the statement runs again.
When the while clause evaluates to 0, the statement ends. The controlling
expression must be evaluate to a scalar type.
A break, return, or goto statement can cause the processing of a do statement
to end, even when the while clause does not evaluate to 0.
Example of a do Statement
Related Information
break
continue
for
goto
return
while
ΓòÉΓòÉΓòÉ <hidden> Syntax of a do Statement ΓòÉΓòÉΓòÉ
A do statement has the form:
>>ΓöÇΓöÇdoΓöÇΓöÇstatementΓöÇΓöÇwhileΓöÇΓöÇ(ΓöÇΓöÇexpressionΓöÇΓöÇ)ΓöÇΓöÇ;ΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ <hidden> Example of a do Statement ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following statement prompts the user to enter a 1. If the user enters a 1,
the statement ends. If not, it displays another prompt.
*
************************************************************************/
The example contains error-checking code to verify that the user entered an
integer value and to clear the input stream if an error occurs.
/**
** This example illustrates the do statement.
**/
#include <iostream.h>
void main()
{
int reply1;
char c;
do
{
cout << "Enter a 1: ";
cin >> reply1;
if (cin.fail())
{
cerr << "Not a valid number." << endl;
// Clear the error flag.
cin.clear(cin.rdstate() & ~ios::failbit);
// Purge incorrect input.
cin.ignore(cin.rdbuf()->in_avail());
}
}
while (reply1 != 1);
}
ΓòÉΓòÉΓòÉ 8.6. Expression ΓòÉΓòÉΓòÉ
An expression statement contains an expression. The expression can be null.
Expressions are described in Expressions and Operators.
Syntax of an Expression Statement
An expression statement evaluates the given expression. It is used to assign
the value of the expression to a variable or to call a function.
The following are examples of expressions:
printf("Account Number: \n"); /* call to the printf */
marks = dollars * exch_rate; /* assignment to marks */
(difference < 0) ? ++losses : ++gain; /* conditional increment */
switches = flags Γûî BIT_MASK; /* assignment to switches */
Resolving Ambiguous Statements in C++
Related Information
Expressions and Operators
ΓòÉΓòÉΓòÉ <hidden> Syntax of an Expression Statement ΓòÉΓòÉΓòÉ
An expression statement has the form:
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ;ΓöÇΓöÇ><
ΓööΓöÇexpressionΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 8.7. for ΓòÉΓòÉΓòÉ
A for statement lets you do the following:
Evaluate an expression before the first iteration of the statement
(initialization)
Specify an expression to determine whether or not the statement should be
processed (controlling part)
Evaluate an expression after each iteration of the statement
Syntax of a for Statement
A break, return, or goto statement can cause a for statement to end, even when
the second expression does not evaluate to 0. If you omit expression2, you
must use a break, return, or goto statement to end the for statement.
C++ Note: In C++ programs, you can also use expression1 to declare a variable
as well as initialize it. If you declare a variable in this expression, the
variable has the same scope as the for statement and is not local to the for
statement.
Examples of for Statements
Related Information
break
continue
do
return
goto
while
Expressions and Operators
ΓòÉΓòÉΓòÉ <hidden> Syntax of a for Statement ΓòÉΓòÉΓòÉ
A for statement has the form:
>>ΓöÇΓöÇforΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ;ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ;ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ>
ΓööΓöÇexpression1ΓöÇΓöÿ ΓööΓöÇexpression2ΓöÇΓöÿ ΓööΓöÇexpression3ΓöÇΓöÿ
>ΓöÇΓöÇstatementΓöÇΓöÇ><
Expression1 Is the initialization expression. It is evaluated only before
the statement is processed for the first time. You can use
this expression to initialize a variable. If you do not want
to evaluate an expression prior to the first iteration of the
statement, you can omit this expression.
Expression2 Is the controlling part. It is evaluated before each iteration
of the statement. It must evaluate to a scalar type.
If it evaluates to 0 (zero), the statement is not processed
and control moves to the next statement following the for
statement. If expression2 does not evaluate to 0, the
statement is processed. If you omit expression2, it is as if
the expression had been replaced by a nonzero constant, and
the for statement is not terminated by failure of this
condition.
Expression3 Is evaluated after each iteration of the statement. You can
use this expression to increase, decrease, or reinitialize a
variable. This expression is optional.
ΓòÉΓòÉΓòÉ <hidden> Examples of for Statements ΓòÉΓòÉΓòÉ
The following for statement prints the value of count 20 times. The for
statement initially sets the value of count to 1. After each iteration of the
statement, count is incremented.
for (count = 1; count <= 20; count++)
printf("count = %d\n", count);
The following sequence of statements accomplishes the same task. Note the use
of the while statement instead of the for statement.
count = 1;
while (count <= 20)
{
printf("count = %d\n", count);
count++;
}
The following for statement does not contain an initialization expression:
for (; index > 10; --index)
{
list[index] = var1 + var2;
printf("list[%d] = %d\n", index, list[index]);
}
The following for statement will continue running until scanf receives the
letter e:
for (;;)
{
scanf("%c", &letter);
if (letter == '\n')
continue;
if (letter == 'e')
break;
printf("You entered the letter %c\n", letter);
}
The following for statement contains multiple initializations and increments.
The comma operator makes this construction possible. The first comma in the for
expression is a punctuator for a declaration. It declares and initializes two
integers, i and j. The second comma, a comma operator, allows both i and j to
be incremented at each step through the loop.
for (int i = 0, j = 50; i < 10; ++i, j += 50)
{
cout << "i = " << i << "and j = " << j << endl;
}
The following example shows a nested for statement. It prints the values of an
array having the dimensions [5][3].
for (row = 0; row < 5; row++)
for (column = 0; column < 3; column++)
printf("%d\n", table[row][column]);
The outer statement is processed as long as the value of row is less than 5.
Each time the outer for statement is executed, the inner for statement sets the
initial value of column to zero and the statement of the inner for statement is
executed 3 times. The inner statement is executed as long as the value of
column is less than 3.
ΓòÉΓòÉΓòÉ 8.8. goto ΓòÉΓòÉΓòÉ
A goto statement causes your program to unconditionally transfer control to the
statement associated with the label specified on the goto statement.
Syntax of a goto Statement
Because the goto statement can interfere with the normal sequence of
processing, it makes a program more difficult to read and maintain. Often, a
break statement, a continue statement, or a function call can eliminate the
need for a goto statement.
If you use a goto statement to transfer control to a statement inside of a loop
or block, initializations of automatic storage for the loop do not take place
and the result is undefined. The label must appear in the same function as the
goto.
If an active block is exited using a goto statement, any local variables are
destroyed when control is transferred from that block.
Example of a goto Statement
Related Information
Labels
break
continue
Functions
ΓòÉΓòÉΓòÉ <hidden> Syntax of a goto Statement ΓòÉΓòÉΓòÉ
A goto statement has the form:
>>ΓöÇΓöÇgotoΓöÇΓöÇlabel_identifierΓöÇΓöÇ;ΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ <hidden> Example of a goto Statement ΓòÉΓòÉΓòÉ
The following example shows a goto statement that is used to jump out of a
nested loop. This function could be written without using a goto statement.
/**
** This example shows a goto statement that is used to
** jump out of a nested loop.
**/
#include <stdio.h>
void display(int matrix[3][3]);
int main(void)
{
int matrix[3][3]={1,2,3,4,5,2,8,9,10};
display(matrix);
return(0);
}
void display(int matrix[3][3])
{
int i, j;
for (i = 0; i < 3; i++)
for (j = 0; j < 3; j++)
{
if ( (matrix[i][j] < 1) ΓûîΓûî (matrix[i][j] > 6) )
goto out_of_bounds;
printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]);
}
return;
out_of_bounds: printf("number must be 1 through 6\n");
}
ΓòÉΓòÉΓòÉ 8.9. if ΓòÉΓòÉΓòÉ
An if statement lets you conditionally process a statement when the specified
test expression evaluates to a nonzero value. The expression must evaluate to a
scalar type. You can optionally specify an else clause on the if statement. If
the test expression evaluates to 0 and an else clause exists, the statement
associated with the else clause runs. If the test expression evaluates to a
nonzero value, the statement following the expression runs and the else clause
is ignored.
Syntax of an if Statement
When if statements are nested and else clauses are present, a given else is
associated with the closest preceding if statement within the same block.
Examples of if Statements
Related Information
Conditional Compilation Directives
switch
ΓòÉΓòÉΓòÉ <hidden> Syntax of an if Statement ΓòÉΓòÉΓòÉ
An if statement has the form:
>>ΓöÇΓöÇifΓöÇΓöÇ(ΓöÇΓöÇexpressionΓöÇΓöÇ)ΓöÇΓöÇstatementΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇelseΓöÇΓöÇstatementΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Examples of if Statements ΓòÉΓòÉΓòÉ
The following example causes grade to receive the value A if the value of score
is greater than or equal to 90.
if (score >= 90)
grade = 'A';
The following example displays Number is positive if the value of number is
greater than or equal to 0. If the value of number is less than 0, it displays
Number is negative.
if (number >= 0)
printf("Number is positive\n");
else
printf("Number is negative\n");
The following example shows a nested if statement:
if (paygrade == 7)
if (level >= 0 && level <= 8)
salary *= 1.05;
else
salary *= 1.04;
else
salary *= 1.06;
cout << "salary is " << salary << endl;
The following example shows a nested if statement that does not have an else
clause. Because an else clause always associates with the closest if statement,
braces might be needed to force a particular else clause to associate with the
correct if statement. In this example, omitting the braces would cause the else
clause to associate with the nested if statement.
if (kegs > 0) {
if (furlongs > kegs)
fpk = furlongs/kegs;
}
else
fpk = 0;
The following example shows an if statement nested within an else clause. This
example tests multiple conditions. The tests are made in order of their
appearance. If one test evaluates to a nonzero value, a statement runs and the
entire if statement ends.
if (value > 0)
++increase;
else if (value == 0)
++break_even;
else
++decrease;
ΓòÉΓòÉΓòÉ 8.10. Null Statement ΓòÉΓòÉΓòÉ
The null statement performs no operation.
Syntax of a Null Statement
A null statement can hold the label of a labeled statement or complete the
syntax of an iterative statement.
The following example initializes the elements of the array price. Because the
initializations occur within the for expressions, a statement is only needed to
finish the for syntax; no operations are required.
for (i = 0; i < 3; price[i++] = 0)
;
A null statement can be used when a label is needed before the end of a block
statement. For example:
void func(void) {
if (error_detected)
goto depart;
/* further processing */
depart: ; /* null statement required */
}
Related Information
Null Directive (#)
Labels
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Null Statement ΓòÉΓòÉΓòÉ
A null statement has the form:
>>ΓöÇΓöÇ;ΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ 8.11. return ΓòÉΓòÉΓòÉ
A return statement ends the processing of the current function and returns
control to the caller of the function.
Syntax of a return Statement
A return statement in a function is optional. The compiler issues a warning if
a return statement is not found in a function declared with a return type. If
the end of a function is reached without encountering a return statement,
control is passed to the caller as if a return statement without an expression
were encountered. A function can contain multiple return statements.
If an expression is present on a return statement, the value of the expression
is returned to the caller. If the data type of the expression is different from
the function return type, conversion of the return value takes place as if the
value of the expression were used to initialize an object with the same
function return type.
If an expression is not present on a return statement, the value of the return
statement is undefined. If an expression is not given on a return statement in
a function declared with a nonvoid return type, an error message is issued, and
the result of calling the function is unpredictable. For example:
int func1()
{
return;
}
int func2()
{
return (4321);
}
void main() {
int a=func1(); // result is unpredictable!
int b=func2();
}
You cannot use a return statement with an expression when the function is
declared as returning type void.
You can use the /Wret compiler option to generate diagnostic messages about the
use of return statements in your functions.
C++ Note: If a function returns a class object with constructors, a temporary
class object might be constructed. The temporary object is not in the scope of
the function returning the temporary object but is local to the caller of the
function.
When a function returns, all temporary local variables are destroyed. If local
class objects with destructors exist, destructors are called. For more details,
see Temporary Objects.
Examples of return Statements
Related Information
Functions
/W option
Temporary Objects
Expression
ΓòÉΓòÉΓòÉ <hidden> Syntax of a return Statement ΓòÉΓòÉΓòÉ
A return statement has the form:
>>ΓöÇΓöÇreturnΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ;ΓöÇΓöÇ><
ΓööΓöÇexpressionΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Examples of return Statements ΓòÉΓòÉΓòÉ
return; /* Returns no value */
return result; /* Returns the value of result */
return 1; /* Returns the value 1 */
return (x * x); /* Returns the value of x * x */
The following function searches through an array of integers to determine if a
match exists for the variable number. If a match exists, the function match
returns the value of i. If a match does not exist, the function match returns
the value -1 (negative one).
int match(int number, int array[ ], int n)
{
int i;
for (i = 0; i < n; i++)
if (number == array[i])
return (i);
return(-1);
}
ΓòÉΓòÉΓòÉ 8.12. switch ΓòÉΓòÉΓòÉ
A switch statement lets you transfer control to different statements within the
switch body depending on the value of the switch expression. The switch
expression must evaluate to an integral value. The body of the switch statement
contains case clauses that consist of
A case label
An optional default label
A case expression
A list of statements.
If the value of the switch expression equals the value of one of the case
expressions, the statements following that case expression are processed. If
not, the default label statements, if any, are processed.
A switch statement has the form:
>>ΓöÇΓöÇswitchΓöÇΓöÇ(ΓöÇΓöÇexpressionΓöÇΓöÇ)ΓöÇΓöÇswitch_bodyΓöÇΓöÇ><
Syntax of a switch Body
A case clause contains a case label followed by any number of statements.
A case label contains the word case followed by an integral constant
expression and a colon. Anywhere you can put one case label, you can put
multiple case labels.
A default clause contains a default label followed by one or more statements.
You can put a case label on either side of the default label. A switch
statement can have only one default label.
The switch statement passes control to the statement following one of the
labels or to the statement following the switch body. The value of the
expression that precedes the switch body determines which statement receives
control. This expression is called the switch expression.
The value of the switch expression is compared with the value of the
expression in each case label. If a matching value is found, control is passed
to the statement following the case label that contains the matching value. If
there is no matching value but there is a default label in the switch body,
control passes to the default labelled statement. If no matching value is
found, and there is no default label anywhere in the switch body, no part of
the switch body is processed.
When control passes to a statement in the switch body, control only leaves the
switch body when a break statement is encountered or the last statement in the
switch body is processed.
If necessary, an integral promotion is performed on the controlling
expression, and all expressions in the case statements are converted to the
same type as the controlling expression.
Restrictions
The switch expression and the case expressions must have an integral type. The
value of each case expression must represent a different value and must be a
constant expression.
Only one default label can occur in each switch statement. You cannot have
duplicate case labels in a switch statement.
You can put data definitions at the beginning of the switch body, but the
compiler does not initialize auto and register variables at the beginning of a
switch body.
C++ Note: You can have declarations in the body of the switch statement. In
C++, you cannot transfer control over a declaration containing an initializer
unless the declaration is located in an inner block that is completely
bypassed by the transfer of control. All declarations within the body of a
switch statement that contain initializers must be contained in an inner
block.
Examples of switch Statements
Related Information
break
if
Labels
Expression
Type Specifiers
Storage Class Specifiers
ΓòÉΓòÉΓòÉ <hidden> Syntax of a switch Body ΓòÉΓòÉΓòÉ
The switch body is enclosed in braces and can contain definitions,
declarations, case clauses, and a default clause. Each case clause and default
clause can contain statements.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Γöé
>>ΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ>
Γö£ΓöÇtype_definitionΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ ΓööΓöÇcase_clauseΓöÇΓöÿ
Γö£ΓöÇfile_scope_data_declarationΓöÇΓöÇΓöñ
ΓööΓöÇblock_scope_data_declarationΓöÇΓöÿ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ}ΓöÇΓöÇ><
ΓööΓöÇdefault_clauseΓöÇΓöÿ ΓööΓöÇcase_clauseΓöÇΓöÿ
A case clause has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇcase_labelΓöÇΓöÇΓöÇΓöÇstatementΓöÇΓö┤ΓöÇΓöÇ><
A case label has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇΓöÇΓöÇcaseΓöÇΓöÇintegral_constant_expressionΓöÇΓöÇ:ΓöÇΓö┤ΓöÇΓöÇ><
A default clause has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇdefaultΓöÇΓöÇ:ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇstatementΓöÇΓö┤ΓöÇΓöÇ><
ΓööΓöÇcase_labelΓöÇΓöÿ ΓööΓöÇcase_labelΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Examples of switch Statements ΓòÉΓòÉΓòÉ
The following switch statement contains several case clauses and one default
clause. Each clause contains a function call and a break statement. The break
statements prevent control from passing down through each statement in the
switch body.
If the switch expression evaluated to '/', the switch statement would call the
function divide. Control would then pass to the statement following the switch
body.
char key;
cout << "Enter an arithmetic operator\n");
cin >> key;
switch (key)
{
case '+':
add();
break;
case '-':
subtract();
break;
case '*':
multiply();
break;
case '/':
divide();
break;
default:
cout << "The key you pressed is not valid\n";
break;
}
If the switch expression matches a case expression, the statements following
the case expression are processed until a break statement is encountered or the
end of the switch body is reached. In the following example, break statements
are not present. If the value of text[i] is equal to 'A', all three counters
are incremented. If the value of text[i] is equal to 'a', lettera and total are
increased. Only total is increased if text[i] is not equal to 'A' or 'a'.
char text[100];
int capa, lettera, total;
for (i=0; i<sizeof(text); i++) {
switch (text[i])
{
case 'A':
capa++;
case 'a':
lettera++;
default:
total++;
}
}
The following switch statement performs the same statements for more than one
case label:
/**
** This example contains a switch statement that performs
** the same statement for more than one case label.
**/
#include <stdio.h>
int main(void)
{
int month;
/* Read in a month value */
printf("Enter month: ");
scanf("%d", &month);
/* Tell what season it falls into */
switch (month)
{
case 12:
case 1:
case 2:
printf("month %d is a winter month\n", month);
break;
case 3:
case 4:
case 5:
printf("month %d is a spring month\n", month);
break;
case 6:
case 7:
case 8:
printf("month %d is a summer month\n", month);
break;
case 9:
case 10:
case 11:
printf("month %d is a fall month\n", month);
break;
case 66:
case 99:
default:
printf("month %d is not a valid month\n", month);
}
return(0);
}
If the expression month has the value 3, control passes to the statement:
printf("month %d is a spring month\n", month);
The break statement passes control to the statement following the switch body.
ΓòÉΓòÉΓòÉ 8.13. while ΓòÉΓòÉΓòÉ
A while statement repeatedly runs the body of a loop until the controlling
expression evaluates to 0.
Syntax of a while Statement
The expression is evaluated to determine whether or not to process the body of
the loop. The expression must be convertible to a scalar type. If the
expression evaluates to 0, the body of the loop never runs. If the expression
does not evaluate to 0, the loop body is processed. After the body has run,
control passes back to the expression. Further processing depends on the value
of the condition.
A break, return, or goto statement can cause a while statement to end, even
when the condition does not evaluate to 0.
Example of a while Statement
Related Information
break
continue
do
for
goto
return
ΓòÉΓòÉΓòÉ <hidden> Syntax of a while Statement ΓòÉΓòÉΓòÉ
A while statement has the form:
>>ΓöÇΓöÇwhileΓöÇΓöÇ(ΓöÇΓöÇexpressionΓöÇΓöÇ)ΓöÇΓöÇstatementΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ <hidden> Example of a while Statement ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following program, item[index] triples each time the value of the
expression ++index is less than MAX_INDEX. When ++index evaluates to MAX_INDEX,
the while statement ends.
*
************************************************************************/
/**
** This example illustrates the while statement.
**/
#define MAX_INDEX (sizeof(item) / sizeof(item[0]))
#include <stdio.h>
int main(void)
{
static int item[ ] = { 12, 55, 62, 85, 102 };
int index = 0;
while (index < MAX_INDEX)
{
item[index] *= 3;
printf("item[%d] = %d\n", index, item[index]);
++index;
}
return(0);
}
ΓòÉΓòÉΓòÉ 9. Preprocessor Directives ΓòÉΓòÉΓòÉ
This section describes the VisualAge C++ preprocessor directives. Preprocessing
is a step that takes place before compilation that lets you:
Replace tokens in the current file with specified replacement tokens.
Imbed files within the current file
Conditionally compile sections of the current file
Generate diagnostic messages
Change the line number of the next line of source and change the file
name of the current file.
A token is a series of characters delimited by white space. The only white
space allowed on a preprocessor directive is the space, horizontal tab, and
comments.
The preprocessed source program file must be a valid C or C++ program.
The preprocessor is controlled by the following directives:
Macro Definition and Expansion (#define)
Scope of Macro Names (#undef)
Preprocessor Error Directive (#error)
File Inclusion (#include)
#if, #elif
#ifdef
#ifndef
#else
#endif
Line Control (#line)
Pragma Directives (#pragma)
This section also describes:
The # operator
Macro concatenation with the ## operator
The null directive (#)
Predefined macros.
Related Information
The format of a preprocessor directive
/D option
ΓòÉΓòÉΓòÉ 9.1. Preprocessor Directive Format ΓòÉΓòÉΓòÉ
Preprocessor directives begin with the # token followed by a preprocessor
keyword. The # token must appear as the first character that is not white space
on a line. The # is not part of the directive name and can be separated from
the name with white spaces.
A preprocessor directive ends at the new-line character unless the last
character of the line is the \ (backslash) character. If the \ character
appears as the last character in the preprocessor line, the preprocessor
interprets the \ and the new-line character as a continuation marker. The
preprocessor deletes the \ (and the following new-line character) and splices
the physical source lines into continuous logical lines.
Except for some #pragma directives, preprocessor directives can appear anywhere
in a program.
Related Information
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.2. Phases of Preprocessing ΓòÉΓòÉΓòÉ
Preprocessing appears as if it occurs in several phases.
1. New-line characters are introduced as needed to replace system-dependent
end-of-line indicators, and any other system-dependent character-set
translations are done. Equivalent single characters replace trigraph
sequences.
2. Each \ (backslash) followed by a new-line character pair is deleted. The
next source line is appended to the line that contained the sequence.
3. The source text is decomposed into preprocessing tokens and sequences of
white space. A single white space replaces each comment. A source file
cannot end with a partial token or comment.
4. Preprocessing directives are executed, and macros are expanded.
5. Escape sequences in character constants and string literals are replaced
by their equivalent values.
6. Adjacent string literals are concatenated.
The rest of the compilation process operates on the preprocessor output, which
is syntactically and semantically analyzed and translated, and then linked as
necessary with other programs and libraries.
ΓòÉΓòÉΓòÉ 9.3. Macro Definition and Expansion (#define) ΓòÉΓòÉΓòÉ
A preprocessor define directive directs the preprocessor to replace all
subsequent occurrences of a macro with specified replacement tokens.
A preprocessor #define directive has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇdefineΓöÇΓöÇidentifierΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ><
Γöé ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé Γö£ΓöÇidentifierΓöÇΓöñ
Γöé Γöé Γöé Γöé Γöé
ΓööΓöÇ(ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ)ΓöÇΓöÿ ΓööΓöÇcharacterΓöÇΓöÇΓöÿ
ΓööΓöÇidentifierΓöÇΓöÿ
The #define directive can contain an object-like definition or a function-like
definition
Arguments of the # and ## operators are converted before replacement of
parameters in a function-like macro.
The number of arguments in a macro invocation must be the same as the number of
parameters in the corresponding macro definition.
Commas in the macro invocation argument list do not act as argument separators
when they are:
in character constants
in string literals
surrounded by parentheses.
The scope of a macro definition begins at the definition and does not end
until a corresponding #undef directive is encountered. If there is no
corresponding #undef directive, the scope of the macro definition lasts until
the end of the compilation is reached.
A recursive macro is not fully expanded. For example, the definition
#define x(a,b) x(a+1,b+1) + 4
would expand x(20,10) to x(20+1,10+1) + 4 rather than trying to expand
the macro x over and over within itself.
A definition is not required to specify replacement tokens. The following
definition removes all instances of the token debug from subsequent lines in
the current file:
#define debug
You can change the definition of a defined identifier or macro with a second
preprocessor #define directive only if the second preprocessor #define
directive is preceded by a preprocessor #undef directive, described in Scope
of Macro Names (#undef). The #undef directive nullifies the first definition
so that the same identifier can be used in a redefinition.
You can also use the /D compiler option to define macros on the command line.
Macros defined on the command line override macros defined in the source code.
The /D option is described in the IBM VisualAge C++ for OS/2 User's Guide and
Reference.
Within the text of the program, the preprocessor does not scan character
constants or string constants for macro invocations.
Examples of #define Directives
Related Information
Object-Like Macros
Function-Like Macros
Scope of Macro Names (#undef)
/D option
# Operator
Macro Concatenation with the ## Operator
ΓòÉΓòÉΓòÉ 9.3.1. Object-Like Macros ΓòÉΓòÉΓòÉ
An object-like macro definition replaces a single identifier with the specified
replacement tokens. The following object-like definition causes the
preprocessor to replace all subsequent instances of the identifier COUNT with
the constant 1000:
#define COUNT 1000
If the statement
int arry[COUNT];
appears after this definition and in the same file as the definition, the
preprocessor would change the statement to
int arry[1000];
in the output of the preprocessor.
Other definitions can make reference to the identifier COUNT:
#define MAX_COUNT COUNT + 100
The preprocessor replaces each subsequent occurrence of MAX_COUNT with
COUNT + 100, which the preprocessor then replaces with 1000 + 100.
If a number that is partially built by a macro expansion is produced, the
preprocessor does not consider the result to be a single value. For example,
the following will not result in the value 10.2 but in a syntax error.
#define a 10
a.2
Using the following also results in a syntax error:
#define a 10
#define b a.11
Identifiers that are partially built from a macro expansion may not be
produced. Therefore, the following example contains two identifiers and results
in a syntax error:
#define d efg
abcd
ΓòÉΓòÉΓòÉ 9.3.2. Function-Like Macros ΓòÉΓòÉΓòÉ
Function-like macro definition:
An identifier followed by a parenthesized parameter list in parenthesis
and the replacement tokens. The parameters are imbedded in the
replacement code. White space cannot separate the identifier (which is
the name of the macro) and the left parenthesis of the parameter list. A
comma must separate each parameter. For portability, you should not have
more than 31 parameters for a macro.
Function-like macro invocation:
An identifier followed by a list of arguments in parentheses. A comma
must separate each argument. Once the preprocessor identifies a
function-like macro invocation, argument substitution takes place. A
parameter in the replacement code is replaced by the corresponding
argument. Any macro invocations contained in the argument itself are
completely replaced before the argument replaces its corresponding
parameter in the replacement code.
The following line defines the macro SUM as having two parameters a and b and
the replacement tokens (a + b):
#define SUM(a,b) (a + b)
This definition would cause the preprocessor to change the following
statements (if the statements appear after the previous definition):
c = SUM(x,y);
c = d * SUM(x,y);
In the output of the preprocessor, these statements would appear as:
c = (x + y);
c = d * (x + y);
Use parentheses to ensure correct evaluation of replacement text. For example,
the definition:
#define SQR(c) ((c) * (c))
requires parentheses around each parameter c in the definition in order to
correctly evaluate an expression like:
y = SQR(a + b);
The preprocessor expands this statement to:
y = ((a + b) * (a + b));
Without parentheses in the definition, the correct order of evaluation is not
preserved, and the preprocessor output is:
y = (a + b * a + b);
See Operator Precedence and Associativity, and Parenthesized Expressions ( )
for more information about using parentheses.
ΓòÉΓòÉΓòÉ <hidden> Examples of #define Directives ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program contains two macro definitions and a macro invocation
that refers to both of the defined macros:
*
************************************************************************/
/**
** This example illustrates #define directives.
**/
#include <stdio.h>
#define SQR(s) ((s) * (s))
#define PRNT(a,b) \
printf("value 1 = %d\n", a); \
printf("value 2 = %d\n", b) ;
int main(void)
{
int x = 2;
int y = 3;
PRNT(SQR(x),y);
return(0);
}
/************************************************************************
*
After being interpreted by the preprocessor, this program is replaced by code
equivalent to the following:
*
************************************************************************/
#include <stdio.h>
int main(void)
{
int x = 2;
int y = 3;
printf("value 1 = %d\n", ( (x) * (x) ) );
printf("value 2 = %d\n", y);
return(0);
}
/************************************************************************
*
This program produces the following output:
value 1 = 4
value 2 = 3
*
************************************************************************/
ΓòÉΓòÉΓòÉ 9.4. Scope of Macro Names (#undef) ΓòÉΓòÉΓòÉ
A preprocessor undef directive causes the preprocessor to end the scope of a
preprocessor definition.
A preprocessor #undef directive has the form:
>>ΓöÇΓöÇ#ΓöÇΓöÇundefΓöÇΓöÇidentifierΓöÇΓöÇ><
If the identifier is not currently defined as a macro, #undef is ignored
Once defined, a preprocessor identifier remains defined and in scope
(independent of the scoping rules of the language) until the end of a
translation unit or until it is undefined by an #undef preprocessor directive.
You can also use the /U compiler option to undefine macros. The /U option does
not undefine macros defined in source code.
Examples of #undef Directives
Related Information
Macro Definition and Expansion (#define)
Predefined Macro Names
/U option
Preprocessor Directives
ΓòÉΓòÉΓòÉ <hidden> Examples of #undef Directives ΓòÉΓòÉΓòÉ
The following directives define BUFFER and SQR:
#define BUFFER 512
#define SQR(x) ((x) * (x))
The following directives nullify these definitions:
#undef BUFFER
#undef SQR
Any occurrences of the identifiers BUFFER and SQR that follow these #undef
directives are not replaced with any replacement tokens. Once the definition of
a macro has been removed by an #undef directive, the identifier can be used in
a new #define directive.
ΓòÉΓòÉΓòÉ 9.5. # Operator ΓòÉΓòÉΓòÉ
The # (single number sign) operator converts a parameter of a function-like
macro into a character string literal. For example, if macro ABC is defined
using the following directive:
#define ABC(x) #x
all subsequent invocations of the macro ABC would be expanded into a character
string literal containing the argument passed to ABC. For example:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé INVOCATION Γöé RESULT OF MACRO EXPANSION Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "ABC(1)" Γöé "1" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "ABC(Hello there)" Γöé "Hello there" Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The # operator should not be confused with the null directive.
Use the # operator in a function-like macro definition according to the
following rules:
A parameter following # operator in a function-like macro is converted
into a character string literal containing the argument passed to the
macro.
White-space characters that appear before or after the argument passed to
the macro are deleted.
Multiple white-space characters imbedded within the argument passed to
the macro is replaced by a single space character.
If the argument passed to the macro contains a string literal and if a \
(backslash) character appears within the literal, a second \ character is
inserted before the original \ when the macro is expanded.
If the argument passed to the macro contains a " (double quotation mark)
character, a \ character is inserted before the " when the macro is
expanded.
If the argument passed to the macro contains a ' (single quotation mark)
character, a \ character is inserted before the ' when the macro is
expanded.
The conversion of an argument into a string literal occurs before macro
expansion on that argument.
If more than one ## operator or # operator appears in the replacement
list of a macro definition, the order of evaluation of the operators is
not defined.
If the result of the macro expansion is not a valid character string
literal, the behavior is undefined.
See Function-Like Macros for more information about function-like macros.
Examples of the # Operator
Related Information
Macro Definition and Expansion (#define)
Scope of Macro Names (#undef)
Macro Concatenation with the ## Operator
Function-Like Macros
Preprocessor Directives
ΓòÉΓòÉΓòÉ <hidden> Examples of the # Operator ΓòÉΓòÉΓòÉ
The following examples demonstrate the use of the # operator:
#define STR(x) #x
#define XSTR(x) STR(x)
#define ONE 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé INVOCATION Γöé RESULT OF MACRO EXPANSION Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "STR(\n "\n" '\n')" Γöé ""\n \"\\n\" '\\n'"" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "STR(ONE)" Γöé "ONE" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "XSTR(ONE)" Γöé "1" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "XSTR("hello")" Γöé "\"hello\"" Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 9.6. Macro Concatenation with the ## Operator ΓòÉΓòÉΓòÉ
The ## (double number sign) operator concatenates two tokens in a macro
invocation (text and/or arguments) given in a macro definition.
If a macro XY was defined using the following directive:
#define XY(x,y) x##y
the last token of the argument for x is concatenated with the first token of
the argument for y.
For example,
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé INVOCATION Γöé RESULT OF MACRO EXPANSION Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "XY(1, 2)" Γöé "12" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "XY(Green, Γöé "Greenhouse" Γöé
Γöé house)" Γöé Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Use the ## operator according to the following rules:
The ## operator cannot be the very first or very last item in the
replacement list of a macro definition.
The last token of the item in front of the ## operator is concatenated
with first token of the item following the ## operator.
Concatenation takes place before any macros in arguments are expanded.
If the result of a concatenation is a valid macro name, it is available
for further replacement even if it appears in a context in which it would
not normally be available.
If more than one ## operator and/or # operator appears in the replacement
list of a macro definition, the order of evaluation of the operators is
not defined.
Examples of the ## Operator
Related Information
Macro Definition and Expansion (#define)
Scope of Macro Names (#undef)
# Operator
Preprocessor Directives
ΓòÉΓòÉΓòÉ <hidden> Examples of the ## Operator ΓòÉΓòÉΓòÉ
The following examples demonstrate the use of the ## operator:
#define ArgArg(x, y) x##y
#define ArgText(x) x##TEXT
#define TextArg(x) TEXT##x
#define TextText TEXT##text
#define Jitter 1
#define bug 2
#define Jitterbug 3
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé INVOCATION Γöé RESULT OF MACRO EXPANSION Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "ArgArg(lady, bug)" Γöé ""ladybug"" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "ArgText(con)" Γöé ""conTEXT"" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "TextArg(book)" Γöé ""TEXTbook"" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "TextText" Γöé ""TEXTtext"" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "ArgArg(Jitter, Γöé "3" Γöé
Γöé bug)" Γöé Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 9.7. Preprocessor Error Directive (#error) ΓòÉΓòÉΓòÉ
A preprocessor error directive causes the preprocessor to generate an error
message and causes the compilation to fail.
The #error directive has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇerrorΓöÇΓöÇΓöÇΓöÇcharacterΓöÇΓö┤ΓöÇΓöÇ><
The directive
#error Error in TESTPGM1 - This section should not be compiled
generates the following error message:
Error in TESTPGM1 - This section should not be compiled
Use the #error directive as a safety check during compilation. For example, if
your program uses preprocessor conditional compilation directives, put #error
directives in the source file to prevent code generation if a section of the
program is reached that should be bypassed.
Related Information
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.8. File Inclusion (#include) ΓòÉΓòÉΓòÉ
A preprocessor include directive causes the preprocessor to replace the
directive with the contents of the specified file.
A preprocessor #include directive has the form:
>>ΓöÇΓöÇ#ΓöÇΓöÇincludeΓöÇΓöÇΓö¼ΓöÇ"file_name"ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
Γö£ΓöÇΓöÉfile_name>ΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇΓöÉheader_name>ΓöÇΓöñ
ΓööΓöÇidentifiersΓöÇΓöÇΓöÇΓöÿ
The preprocessor resolves macros contained in a #include directive. After macro
replacement, the resulting token sequence must consist of a file name enclosed
in either double quotation marks or the characters < and >.
For example:
#define MONTH <july.h>
#include MONTH
If the file name is enclosed in double quotation marks, for example: #include
"payroll.h" the preprocessor treats it as a user-defined file, and searches
for the file in:
1. The directory where the original .c source file resides.
2. Any directories specified using the /I compiler option (that have not
been removed by the /Xc option). Directories specified in the ICC
environment variable are searched before those specified on the command
line.
3. Any directories specified using the INCLUDE environment variable,
provided the /Xi option is not in effect.
If the file name is enclosed in angle brackets, for example: #include
<stdio.h> it is treated as a system-defined file, and the preprocessor
searches the following places in the order given:
1. Any directories specified using the /I compiler option (that have not
been removed by the /Xc option). Directories specified in the ICC
environment variable are searched before those specified on the command
line.
2. Any directories specified using the INCLUDE environment variable,
provided the /Xi option is not in effect.
Note: If the file name is fully qualified, the preprocessor searches only the
directory specified by the name.
The new-line and > characters cannot appear in a file name delimited by < and
>. The new-line and " (double quotation marks) character cannot appear in a
file name delimited by " and ", although > can.
Declarations that are used by several files can be placed in one file and
included with #include in each file that uses them. For example, the following
file defs.h contains several definitions and an inclusion of an additional
file of declarations:
/* defs.h */
#define TRUE 1
#define FALSE 0
#define BUFFERSIZE 512
#define MAX_ROW 66
#define MAX_COLUMN 80
int hour;
int min;
int sec;
#include "mydefs.h"
You can embed the definitions that appear in defs.h with the following
directive:
#include "defs.h"
In the following example, a #define combines several preprocessor macros to
define a macro that represents the name of the C standard I/O header file. A
#include makes the header file available to the program.
#define IO_HEADER <stdio.h>
.
.
.
#include IO_HEADER /* equivalent to specifying #include <stdio.h> */
.
.
.
Related Information
Macro Definition and Expansion (#define)
Preprocessor Directives
Include Files
/I option
ΓòÉΓòÉΓòÉ 9.9. Predefined Macro Names ΓòÉΓòÉΓòÉ
VisualAge C++ provides the following predefined macro names.
ISO/ANSI Standard Predefined Macro Names
VisualAge C++ Predefined Macro Names
Additional VisualAge C++ Predefined Macros
Examples of Predefined Macros
Related Information
#pragma langlvl
Macro Definition and Expansion (#define)
Scope of Macro Names (#undef)
/S2 option
ΓòÉΓòÉΓòÉ 9.9.1. ISO/ANSI Standard Predefined Macro Names ΓòÉΓòÉΓòÉ
Both C and C++ provide the following predefined macro names as specified in the
ISO/ANSI C language standard:
__LINE__
An integer representing the current source line number.
The value of __LINE__ changes during compilation as the compiler processes
subsequent lines of your source program. It can be set with the #line
directive, described in Line Control (#line).
__FILE__
A character string literal containing the name of the source file.
The value of __FILE__ changes as the compiler processes include files that
are part of your source program. It can be set with the #line directive,
described in Line Control (#line).
__DATE__
A character string literal containing the date when the source file was
compiled.
The value of __DATE__ changes as the compiler processes any include files
that are part of your source program. The date is in the form:
"Mmm dd yyyy"
where:
Mmm represents the month in an abbreviated form (Jan, Feb, Mar,
Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, or Dec).
dd represents the day. If the day is less than 10, the first d
is a blank character.
yyyy represents the year.
The date is always set to the system date.
__STDC__
For C programs, the compiler sets this macro to the integer 1 (one) to
indicate that the C compiler conforms to the ISO/ANSI standard. For C++
programs, this macro is set to the integer 0, indicating that the C++
language is not a proper superset of C, and that the compiler does not
conform to ISO/ANSI C. For more information on how C++ differs from
ISO/ANSI C, see C and C++ Compatibility.
__TIME__
A character string literal containing the time when the source file was
compiled.
The value of __TIME__ changes as the compiler processes any include files
that are part of your source program. The time is in the form:
"hh:mm:ss"
where:
hh represents the hour.
mm represents the minutes.
ss represents the seconds.
The time is always set to the system time.
__cplusplus
For C++ programs, this macro is set to the integer 1, indicating that the
compiler is a C++ compiler. Note that this macro name has no trailing
underscores.
ΓòÉΓòÉΓòÉ 9.9.2. VisualAge C++ Predefined Macro Names ΓòÉΓòÉΓòÉ
VisualAge C++ provides the following predefined macros. The value of all these
macros is defined when the corresponding #pragma directive or compiler option
is used. They cannot be the subject of a #define or #undef preprocessor
directive. However, except for the __DATE__, __FUNCTION__, __LINE__, __TIME__,
and __TIMESTAMP__ macros, they can be undefined on the command line using the
/U option.
__ANSI__
Allows only language constructs that conform to ISO/ANSI C standard.
Defined using the #pragma langlvl directive or /Sa option.
__EXTENDED__
Allows additional language constructs provided by the VisualAge C++
implementation. Defined using the #pragma langlvl directive or /S2 option.
__SAA__
Allows only language constructs that conform to the most recent level of
SAA C standards. Defined using the #pragma langlvl directive or /S2
option. This macro is not defined for C++.
__SAAL2__
Allows only language constructs that conform to SAA Level 2 C standards.
Defined using the #pragma langlvldirective or /S2 option. This macro is
not defined for C++.
__COMPAT__
Macro defined when the compat language level is specified for C++ language
files. This macro is not defined for C.
__FUNCTION__
Indicates the name of the function currently being compiled. For C++
programs, expands to the actual function prototype.
__SOM_ENABLED__
Macro defined when the SOM compiler options are used. Indicates that
native SOM is supported. This option turns on implicit SOM mode, and also
causes the file som.h to be included. SOM support for VisualAge C++ and
the SOM options are described in the IBM VisualAge C++ for OS/2
Programming Guide.
__TIMESTAMP__
A character string literal containing the date and time when the source
file was last modified.
The value of __TIMESTAMP__ changes as the compiler processes any include
files that are part of your source program. The date and time are in the
form:
"Day Mmm dd hh:mm:ss yyyy"
where:
Day represents the day of the week (Mon, Tue, Wed, Thu, Fri,
Sat, or Sun).
Mmm represents the month in an abbreviated form (Jan, Feb, Mar,
Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, or Dec).
dd represents the day. If the day is less than 10, the first d
is a blank character.
hh represents the hour. mm represents the minutes.
ss represents the seconds.
yyyy represents the year.
The date and time are always set to the system date and time.
ΓòÉΓòÉΓòÉ 9.9.3. Additional VisualAge C++ Predefined Macros ΓòÉΓòÉΓòÉ
The macros identified in this section are provided to allow customers to write
programs that use VisualAge C++ services. Only those macros identified in this
section should be used to request or receive VisualAge C++ services.
The additional macros offered by the VisualAge C++ compiler are:
_CHAR_UNSIGNED
Indicates default character type is unsigned. Defined when the #pragma
chars(unsigned) directive is in effect, or when the /J+ compiler option is
set.
_CHAR_SIGNED
Indicates default character type is signed. Defined when the #pragma
chars(signed) directive is in effect, or when the /J- compiler option is
set.
__COMPAT__
Indicates language constructs compatible with earlier versions of the C++
language are allowed. Defined using the #pragma langlvl(compat) directive
or /Sc compiler option. This macro is defined for C++ programs only.
__DBCS__
Indicates DBCS support is enabled. Defined using the /Sn compiler option.
__DDNAMES__
Indicates ddnames are supported. Defined using the /Sh compiler option.
__DEBUG_ALLOC__
Maps memory management functions to their debug versions. Defined using
the /Tmcompiler option.
__DLL__
Indicates code for a DLL is being compiled. Defined using the /Ge-
compiler option.
_FP_INLINE_
Inlines the trigonometric functions (cos, sin, and so on).
__HHW_INTEL__
Indicates that the host hardware is an Intel** processor.
__HOS_OS2__
Indicates that the host operating system is OS/2.
__IBMC__
Indicates the version number of the VisualAge C compiler.
__IBMCPP__
Indicates the version number of the VisualAge C++ compiler.
__IMPORTLIB__
Indicates that dynamic linking is used. Defined using the /Gd option.
_M_I386
Indicates code is being compiled for a 386 chip or higher.
__MULTI__
Indicates multithread code is being generated. Defined using the /Gm
compiler option.
__NO_DEFAULT_LIBS__
Indicates that information about default libraries will not be included in
object files. Defined using the /Gd option.
__OS2__
Set to the integer 1. Indicates the product is an OS/2 compiler.
__SPC__
Indicates the subsystem libraries are being used. Defined using the /Rn
compiler option.
__TEMPINC__
Indicates the template-implementation file method of resolving template
functions is being used. Defined using the /Ft compiler option.
__THW_INTEL__
Indicates that the target hardware is an Intel processor.
__TOS_OS2__
Indicates that the target operating system is OS/2.
__TILED__
Indicates tiled memory is being used. Defined using the /Gt compiler
option.
__32BIT__
Set to the integer 1. Indicates the product is a 32-bit compiler.
The value of the __IBMC__ and __IBMCPP__ macros is 300. One of these two
macros is always defined: when you compile C++ code, __IBMCPP__ is defined;
when you compile C code, __IBMC__ is defined. The macros __OS2__, _M_I386, and
__32BIT__ are always defined also. The remaining macros, with the exception of
__FUNCTION__, are defined when the corresponding #pragma directive or compiler
option is used.
ΓòÉΓòÉΓòÉ <hidden> Example of Predefined Macros ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following printf statements display the values of the predefined macros
__LINE__, __FILE__, __TIME__, and __DATE__ and print a message indicating the
program's conformance to ISO/ANSI standards based on __STDC__:
*
************************************************************************/
/**
** This example illustrates some predefined macros.
**/
#pragma langlvl(ANSI)
#include <stdio.h>
#ifdef __STDC__
# define CONFORM "conforms"
#else
# define CONFORM "does not conform"
#endif
int main(void)
{
printf("Line %d of file %s has been executed\n", __LINE__, __FILE__);
printf("This file was compiled at %s on %s\n", __TIME__, __DATE__);
printf("This program %s to ISO/ANSI standards\n", CONFORM);
}
ΓòÉΓòÉΓòÉ 9.10. Conditional Compilation Directives ΓòÉΓòÉΓòÉ
A preprocessor conditional compilation directive causes the preprocessor to
conditionally suppress the compilation of portions of source code. These
directives test a constant expression or an identifier to determine which
tokens the preprocessor should pass on to the compiler and which tokens should
be bypassed during preprocessing. The directives are:
#if
#ifdef
#ifndef
#else
#elif
#endif
The preprocessor conditional compilation directive spans several lines:
The condition specification line
Lines containing code that the preprocessor passes on to the compiler if
the condition evaluates to a nonzero value (optional)
The #else line (optional)
Lines containing code that the preprocessor passes on to the compiler if
the condition evaluates to zero (optional)
The preprocessor #endif directive
For each #if, #ifdef, and #ifndef directive, there are zero or more #elif
directives, zero or one #else directive, and one matching #endif directive.
All the matching directives are considered to be at the same nesting level.
You can nest conditional compilation directives. In the following directives,
the first #else is matched with the #if directive.
#ifdef MACNAME
/* tokens added if MACNAME is defined */
# if TEST <=10
/* tokens added if MACNAME is defined and TEST <= 10 */
# else
/* tokens added if MACNAME is defined and TEST > 10 */
# endif
#else
/* tokens added if MACNAME is not defined */
#endif
Each directive controls the block immediately following it. A block consists
of all the tokens starting on the line following the directive and ending at
the next conditional compilation directive at the same nesting level.
Each directive is processed in the order in which it is encountered. If an
expression evaluates to zero, the block following the directive is ignored.
When a block following a preprocessor directive is to be ignored, the tokens
are examined only to identify preprocessor directives within that block so
that the conditional nesting level can be determined. All tokens other than
the name of the directive are ignored.
Only the first block whose expression is nonzero is processed. The remaining
blocks at that nesting level are ignored. If none of the blocks at that
nesting level has been processed and there is a #else directive, the block
following the #else directive is processed. If none of the blocks at that
nesting level has been processed and there is no #else directive, the entire
nesting level is ignored.
Examples of Conditional Compilation
Related Information
#if
#ifdef
#ifndef
#elif
#else
#endif
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.10.1. #if, #elif ΓòÉΓòÉΓòÉ
The #if and #elif directives compare the value of the expression to zero.
If the constant expression evaluates to a nonzero value, the tokens that
immediately follow the condition are passed on to the compiler.
If the expression evaluates to zero and the conditional compilation directive
contains a preprocessor #elif directive, the source text located between the
#elif and the next #elif or preprocessor #else directive is selected by the
preprocessor to be passed on to the compiler. The #elif directive cannot appear
after the preprocessor #else directive.
All macros are expanded, any defined() expressions are processed and all
remaining identifiers are replaced with the token 0.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇΓö¼ΓöÇifΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇconstant_expressionΓöÇΓöÇΓöÇΓöÇtoken_sequenceΓöÇΓö┤ΓöÇΓöÇ><
ΓööΓöÇelifΓöÇΓöÿ
The expressions that are tested must be integer constant expressions with the
following properties:
No casts are performed.
Arithmetic is performed using long int values.
The expression can contain defined macros. No other identifiers can
appear in the expression.
The constant expression can contain the unary operator defined. This
operator can be used only with the preprocessor keyword #if. The
following expressions evaluate to 1 if the identifier is defined in the
preprocessor, otherwise to 0:
defined identifier
defined(identifier)
For example:
#if defined(TEST1) || defined(TEST2)
Note: If a macro is not defined, a value of 0 (zero) is assigned to it. In
the following example, TEST must be a macro identifier:
#if TEST >= 1
printf("i = %d\n", i);
printf("array[i] = %d\n", array[i]);
#elif TEST < 0
printf("array subscript out of bounds \n");
#endif
Example of #if and #elif Directives
Related Information
Conditional Compilation Directives
#ifdef
#ifndef
#endif
Preprocessor Directives
if
ΓòÉΓòÉΓòÉ <hidden> Examples of #if and #elif Directives ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example uses the #if and #elif directives to assign values to an
array.
*
************************************************************************/
#include <stdio.h>
int main(void)
{
int i;
char *arrayname = "realarray";
int realarray[] = { 1, 2, 3 };
int array1[] = { 4, 5, 6 };
int array2[] = { 7, 8, 9 };
#if ( (defined(LEVEL1)) && (TEST > 1) )
for (i = 0; i < 3; i++)
realarray[i] = array1[i];
arrayname = "array1";
#elif (defined(LEVEL2))
for (i = 0; i < 3; i++)
realarray[i] = array2[i];
arrayname = "array2";
#endif
printf("realarray[] now has the contents of %s[]\n", arrayname);
/* Assuming only LEVEL2 is defined, the expected output is:
realarray[] now has the contents of array2[] */
}
ΓòÉΓòÉΓòÉ 9.10.2. #ifdef ΓòÉΓòÉΓòÉ
The #ifdef directive checks for the existence of macro definitions.
If the identifier specified is defined as a macro, the tokens that immediately
follow the condition are passed on to the compiler.
The preprocessor #ifdef directive has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇifdefΓöÇΓöÇidentifierΓöÇΓöÇΓöÇΓöÇtoken_sequenceΓöÇΓö┤ΓöÇΓöÇ><
The following example defines MAX_LEN to be 75 if EXTENDED is defined for the
preprocessor. Otherwise, MAX_LEN is defined to be 50.
#ifdef EXTENDED
# define MAX_LEN 75
#else
# define MAX_LEN 50
#endif
Related Information
Conditional Compilation Directives
#ifndef
#if, #elif
#else
#endif
Macro Definition and Expansion (#define)
Scope of Macro Names (#undef)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.10.3. #ifndef ΓòÉΓòÉΓòÉ
The #ifndef directive checks for the existence of macro definitions.
If the identifier specified is not defined as a macro, the tokens that
immediately follow the condition are passed on to the compiler.
The preprocessor #ifndef directive has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇifndefΓöÇΓöÇidentifierΓöÇΓöÇΓöÇΓöÇtoken_sequenceΓöÇΓö┤ΓöÇΓöÇ><
An identifier must follow the #ifndef keyword. The following example defines
MAX_LEN to be 50 if EXTENDED is not defined for the preprocessor. Otherwise,
MAX_LEN is defined to be 75.
#ifndef EXTENDED
# define MAX_LEN 50
#else
# define MAX_LEN 75
#endif
Related Information
Conditional Compilation Directives
#ifdef
#if, #elif
#else
#endif
Macro Definition and Expansion (#define)
Scope of Macro Names (#undef)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.10.4. #else ΓòÉΓòÉΓòÉ
If the condition specified in the #if, #ifdef, or #ifndef directive evaluates
to 0, and the conditional compilation directive contains a preprocessor #else
directive, the source text located between the preprocessor #else directive and
the preprocessor #endif directive is selected by the preprocessor to be passed
on to the compiler.
The preprocessor #else directive has the form:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇelseΓöÇΓöÇΓöÇΓöÇtoken_sequenceΓöÇΓö┤ΓöÇΓöÇ><
The following example defines MAX_LEN to be 50 if EXTENDED is not defined for
the preprocessor. Otherwise, MAX_LEN is defined to be 75.
#ifndef EXTENDED
# define MAX_LEN 50
#else
# define MAX_LEN 75
#endif
Related Information
Conditional Compilation Directives
#if, #elif
#ifdef
#ifndef
#endif
Macro Definition and Expansion (#define)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.10.5. #endif ΓòÉΓòÉΓòÉ
The preprocessor #endif directive ends the conditional compilation directive.
It has the form:
>>ΓöÇΓöÇ#ΓöÇΓöÇendifΓöÇΓöÇ><
The following example shows preprocessor conditional compilation directives
ended by the #endif directive.
#if defined(LEVEL1)
# define SIZEOF_INT 16
# ifdef PHASE2
# define MAX_PHASE 2
# else
# define MAX_PHASE 8
# endif
#elif defined(LEVEL2)
# define SIZEOF_INT 32
# define MAX_PHASE 16
#endif
Related Information
Conditional Compilation Directives
#if, #elif
#ifdef
#ifndef
#else
Preprocessor Directives
ΓòÉΓòÉΓòÉ <hidden> Examples of Conditional Compilation Directives ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows how you can nest preprocessor conditional
compilation directives:
#if defined(TARGET1)
# define SIZEOF_INT 16
# ifdef PHASE2
# define MAX_PHASE 2
# else
# define MAX_PHASE 8
# endif
#elif defined(TARGET2)
# define SIZEOF_INT 32
# define MAX_PHASE 16
#else
# define SIZEOF_INT 32
# define MAX_PHASE 32
#endif
The following program contains preprocessor conditional compilation directives:
*
************************************************************************/
/**
** This example contains preprocessor
** conditional compilation directives.
**/
#include <stdio.h>
int main(void)
{
static int array[ ] = { 1, 2, 3, 4, 5 };
int i;
for (i = 0; i <= 4; i++)
{
array[i] *= 2;
#if TEST >= 1
printf("i = %d\n", i);
printf("array[i] = %d\n", array[i]);
#endif
}
return(0);
}
ΓòÉΓòÉΓòÉ 9.11. Line Control (#line) ΓòÉΓòÉΓòÉ
A preprocessor line control directive supplies line numbers for compiler
messages. It causes the compiler to view the line number of the next source
line as the specified number.
A preprocessor #line directive has the form:
>>ΓöÇΓöÇ#ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇdecimal_constantΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇlineΓöÇΓöÿ Γöé ΓööΓöÇ"file_name"ΓöÇΓöÿ Γöé
ΓööΓöÇcharactersΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
In order for the compiler to produce meaningful references to line numbers in
preprocessed source, the preprocessor inserts #line directives where necessary
(for example, at the beginning and after the end of included text).
A file name specification enclosed in double quotation marks can follow the
line number. If you specify a file name, the compiler views the next line as
part of the specified file. If you do not specify a file name, the compiler
views the next line as part of the current source file.
The token sequence on a #line directive is subject to macro replacement. After
macro replacement, the resulting character sequence must consist of a decimal
constant, optionally followed by a file name enclosed in double quotation
marks.
You can use #line control directives to make the compiler provide more
meaningful error messages.
Example of #line Directives
Related Information
Preprocessor Directives
Decimal Constants
ΓòÉΓòÉΓòÉ <hidden> Example of #line Directives ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following program uses #line control directives to give each function an
easily recognizable line number:
*
************************************************************************/
/**
** This example illustrates #line directives.
**/
#include <stdio.h>
#define LINE200 200
int main(void)
{
func_1();
func_2();
}
#line 100
func_1()
{
printf("Func_1 - the current line number is %d\n",__LINE__);
}
#line LINE200
func_2()
{
printf("Func_2 - the current line number is %d\n",__LINE__);
}
/************************************************************************
*
This program produces the following output:
Func_1 - the current line number is 102
Func_2 - the current line number is 202
*
************************************************************************/
ΓòÉΓòÉΓòÉ 9.12. Null Directive (#) ΓòÉΓòÉΓòÉ
The null directive performs no action. It consists of a single # on a line of
its own.
The null directive should not be confused with the # operator or the character
that starts a preprocessor directive.
In the following example, if MINVAL is a defined macro name, no action is
performed. If MINVAL is not a defined identifier, it is defined 1.
#ifdef MINVAL
#
#else
#define MINVAL 1
#endif
Related Information
Preprocessor Directives
Null Statement
ΓòÉΓòÉΓòÉ 9.13. Pragma Directives (#pragma) ΓòÉΓòÉΓòÉ
A pragma is an implementation-defined instruction to the compiler. It has the
general form given below, where character_sequence is a series of characters
that giving a specific compiler instruction and arguments, if any.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇΓöÇΓöÇcharacter_sequenceΓöÇΓö┤ΓöÇΓöÇ><
The character_sequence on a pragma is not subject to macro substitutions,
unless otherwise stated. More than one pragma construct can be specified on a
single #pragma directive. The compiler ignores unrecognized pragmas.
The following pragmas are available:
alloc_text Groups functions into separate 32-bit code segments.
chars Sets the sign type of character data.
checkout Controls the diagnostic messages generated by the /Kn
compiler options. This directive is valid for C programs
only.
comment Places a comment into the object file.
data_seg Places static and external variables in different 32-bit
data segments.
define Forces the definition of a template class without actually
defining an object of the class. This directive is valid for
C++ programs only.
entry Specifies the function to be used as the entry point for the
application being built.
export Declares that a DLL function is to be exported and specifies
the name of the function outside the DLL.
handler Registers an exception handler for a function.
hdrfile Specifies the filename of the precompiled header to be
generated and/or used.
hdrstop Manually terminates the initial sequence of #include
directives being considered for precompilation.
implementation Tells the compiler the name of the file that contains the
function template definitions corresponding to the template
declarations in the include file containing the pragma. This
directive is valid for C++ programs only.
import Lets you import a function or a variable from a DLL using
either an ordinal number or a name different from the one
that it has in the DLL.
info Controls the diagnostic messages generated by the /Wgroup
compiler options.
langlvl Selects the C or C++ language level for compilation.
library This tells the linker to pull in the appropriate libraries
at link time.
linkage Identifies the linkage or calling convention used on a
function call. This directive is valid for C programs only.
map Tells the compiler that all references to an identifier are
to be converted to a new name.
margins Specifies the columns in the input line that are to be
scanned for input to the compiler. This directive is valid
for C programs only.
pack Specifies the alignment rules to use for the structures,
unions, and classes that follow it.
page Skips pages of the generated source listing.
pagesize Sets the number of lines per page for the generated source
listing.
priority Specifies the order in which static objects are to be
initialized at run time. This directive is valid for C++
programs only.
seg16 Specifies that a data object will be shared between 16-bit
and 32-bit processes.
sequence Defines the section of the input line that is to contain
sequence numbers.
skip Skips lines of the generated source listing.
sourcedir Defines a new path to the directory containing the original
source of an include file. This directive is valid for C++
programs only.
stack16 Sets the size of the stack to be allocated for calls to
16-bit routines.
strings Sets storage type for strings.
subtitle Places text on generated source listings.
title Places text on generated source listings.
undeclared Used by the compiler to mark template functions that were
called but never declared. This directive is valid for C++
programs only.
weak Adds an alternate function name with weak binding for the
specified function.
Related Information
#pragma Restrictions
Preprocessor Directives
VisualAge C++ also provides several pragmas to support the IBM System Object
Model (SOM), which provides a common programming interface for building and
using software objects.
SOM support for VisualAge C++ and the SOM pragmas are described in the IBM
VisualAge C++ for OS/2 User's Guide and Reference.
ΓòÉΓòÉΓòÉ 9.13.1. #pragma Restrictions ΓòÉΓòÉΓòÉ
Some #pragma directives must appear in a certain place in your source, or can
appear a limited number of times. The following table lists the restrictions
for #pragma directives.
#PRAGMA Restrictions
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Table 5. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé #PRAGMA Γöé RESTRICTION OF PLACE- Γöé RESTRICTION ON NUMBER OF OCCURRENCES Γöé
Γöé Γöé MENT Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "chars" Γöé On the first #pragma Γöé Once Γöé
Γöé Γöé directive, and before Γöé Γöé
Γöé Γöé any code or directive Γöé Γöé
Γöé Γöé (except "#line") Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "comment"Γöé "copyright" directive Γöé "compiler" and "timestamp" directives Γöé
Γöé Γöé must appear before any Γöé can appear only once Γöé
Γöé Γöé code Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "entry" Γöé No restriction Γöé One per ".exe" or ".dll" If there is Γöé
Γöé Γöé Γöé more than one, the result is undefined. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "langlvl"Γöé On the first #pragma Γöé Once Γöé
Γöé Γöé directive, and before Γöé Γöé
Γöé Γöé any code or directive Γöé Γöé
Γöé Γöé (except "#line") Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "linkage"Γöé Before any declaration Γöé Once for each function Γöé
Γöé Γöé of or call to the Γöé Γöé
Γöé Γöé function Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé "strings"Γöé Before any code Γöé Once Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 9.13.2. alloc_text ΓòÉΓòÉΓòÉ
The #pragma alloc_text directive lets you group functions into different 32-bit
code segments.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇalloc_textΓöÇΓöÇ(ΓöÇΓöÇcode_segmentΓöÇΓöÇΓöÇΓöÇ,ΓöÇΓöÇfunctionΓöÇΓö┤ΓöÇΓöÇ)ΓöÇΓöÇ><
The code_segment is the name of the code segment where you want to place
function. You can specify any number of functions to be included in the named
code_segment.
Functions that are not grouped in a code segment by #pragma alloc_text are
placed in the default 32-bit code segment CODE32, or whatever was specified on
the /NT option. You can also use #pragma alloc_text to explicitly place
functions in CODE32 by specifying it as the code_segment.
Defining your own code segments allows you to organize functions in memory so
that the working set requires fewer pages of memory. You can also specify
attributes for each segment, such as execute-only or preload. You specify
attributes for code segments in a module definition (.DEF) file.
For example, to create two code segments, one load on call, the other preload:
#pragma alloc_text(pl_seg, func1)
#pragma alloc_text(loc_seg, func2)
and use the following statement in the .DEF file for the program:
SEGMENTS
pl_seg PRELOAD
For more information on attributes and how to specify them in a .DEF file, see
the Toolkit documentation for the ILINK program.
Related Information
#pragma data_seg
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.3. chars ΓòÉΓòÉΓòÉ
The #pragma chars directive specifies that the compiler is to treat all char
objects as signed or unsigned.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇcharsΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇunsignedΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇsignedΓöÇΓöÇΓöÇΓöÿ
This pragma must appear before any statements in a file. Once specified, it
applies to the rest of the file and cannot be turned off. If a source file
contains any functions that you want to be compiled without #pragma chars,
place these functions in a different file.
The default character type behaves like an unsigned char.
Related Information
Characters
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.4. checkout ΓòÉΓòÉΓòÉ
The #pragma checkout directive controls the diagnostic messages generated by
the /Kn compiler options.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇcheckoutΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇresumeΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇsuspendΓöÇΓöÿ
Use #pragma checkout to suspend the diagnostics performed by the /Kn options
during specific portions of your program, and then resume the same level of
diagnostics at some later point in the file.
Note:
1. This directive is valid for C programs only.
2. The #pragma info directive replaces the #pragma checkout directive for
controlling diagnostics. It is obsolete in this release of VisualAge C++.
The compiler issues a message for each #pragma checkout directive it
encounters. You should not use it in new code; for your new applications,
use the #pragma info.
3. The /Wgroup options have been added to provide greater control over
diagnostic messages. The VisualAge C compiler maps the /Kn options to the
appropriate /Wgroup option. It also maps #pragma checkout to #pragma
info. The VisualAge C++ compiler maps the /Kn options, but ignores
#pragma checkout directives.
The /Kn options are obsolete in this release of VisualAge C++. You should
not use them in new code. For your new applications, use the /Wgroup
options.
Nested #pragma checkout directives are allowed and behave as follows:
/* Assume /Kpx has been specified */
#pragma checkout(suspend) /* No diagnostics are performed */
.
.
.
#pragma checkout(suspend) /* No effect */
.
.
.
#pragma checkout(resume) /* No effect */
.
.
.
#pragma checkout(resume) /* Diagnostics continue */
The #pragma checkout directive affects all /Kn options specified.
Related Information
/Kn options
/Wgroup options
#pragma info
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.5. comment ΓòÉΓòÉΓòÉ
The #pragma comment directive places a comment into the object file.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇcommentΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇcompilerΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
Γö£ΓöÇdateΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇtimestampΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓööΓöÇΓö¼ΓöÇcopyrightΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ
ΓööΓöÇuserΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ ΓööΓöÇ,"token_sequence"ΓöÇΓöÿ
The comment type can be:
compiler the name and version of the compiler is appended to the end of
the generated object module.
date the date and time of compilation is appended to the end of the
generated object module.
timestamp the date and time of the last modification of the source is
appended to the end of the generated object module.
copyright the text specified by the token_sequence is placed by the
compiler into the generated object module and is loaded into
memory when the program is run.
user the text specified by the token_sequence is placed by the
compiler into the generated object but is not loaded into
memory when the program is run.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.6. data_seg ΓòÉΓòÉΓòÉ
The #pragma data_seg directive lets you place static and external variables in
different 32-bit data segments.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇdata_segΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇdata_segmentΓöÇΓöÿ
All static and external variables defined after the #pragma data_seg directive
are placed in the named data_segment. The pragma is in effect until the next
#pragma data_seg directive or the end of the compilation unit.
Restrictions:
Writable string literals used to initialize pointers are not placed in
the named data_segment, but in the default 32-bit data segment (DATA32).
To place a string in a particular data segment, use an array to
initialize the string instead of a pointer. For example:
char a[ ] = "mystring";
instead of
char *a = "mystring";
#pragma data_seg applies only to 32-bit data segments. Data placed in
16-bit segments because of the /Gt option or #pragma seg16 are not
affected by #pragma data_seg, and are placed in 16-bit data segments.
Static and external variables defined before the first #pragma data_seg
directive are placed in the default DATA32 segment, with the exception of
uninitialized variables and variables explicitly initialized to zero, which
are placed in the BSS32 segment. You can also use #pragma data_seg to
explicitly place variables in the DATA32 segment by specifying no
data_segment, for example, #pragma data_seg(). However, you cannot use the
CONST32_RO or BSS32 segments in a #pragma data_seg directive.
Note: Because the variables in the BSS32 data segment are initialized at load
time and loaded separately from the rest of your program, they take less space
in your executable file. If you place these variables in a different data
segment, this optimization does not take place, and the size of your
executable module increases. For this reason, if the size of your executable
file is critical to you, you should define all variables initialized to zero
(either explicitly or by default) before the first occurrence of #pragma
data_seg.
Defining your own data segments allows you to group data depending on how it
is used and to specify different attributes for different groups of data, such
as when the data should be loaded. You specify attributes for data segments in
a module definition (.DEF) file.
For example, when you build a DLL, you might have one set of data that you
want to make global for all processes that use the DLL, and a different set of
data that you want to copy for each process. You could use #pragma data_seg to
place the data in two different segments as follows:
#pragma data_seg(globdata)
static int counter1 = 0;
static int counter2 = 0;
.
.
.
#pragma data_seg(instdata)
static int instcount1 = 0;
static int instcount2 = 0;
.
.
.
You could then place the following statements in the program's .DEF file:
SEGMENTS
globdata CLASS 'DATA' SHARED
instdata CLASS 'DATA' NONSHARED
SHARED specifies that the data in the globdata segment is global or shared by
all processes that use the DLL. NONSHARED means that each process gets its own
copy of the data in the instdata segment.
For more information on attributes and how to specify them in a .DEF file, see
the Toolkit documentation for the ILINK program.
Related Information
/Gt option
#pragma alloc_text
#pragma seg16
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.7. define ΓòÉΓòÉΓòÉ
The #pragma define directive forces the definition of a template class without
actually defining an object of the class.
Note: This directive is valid for C++ programs only.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇdefineΓöÇΓöÇ(ΓöÇΓöÇtemplate_class_nameΓöÇΓöÇ)ΓöÇΓöÇ><
The pragma can appear anywhere that a declaration is allowed. It is used when
organizing your program for the efficient or automatic generation of template
functions. "Using Templates in C++ Programs" in the IBM VisualAge C++ for OS/2
Programming Guide gives more information about using #pragma define.
Related Information
C++ Templates
Pragma Directives (#pragma)
Preprocessor Directives
"Using Templates in C++ Programs" in the IBM VisualAge C++ for OS/2
Programming Guide
ΓòÉΓòÉΓòÉ 9.13.8. entry ΓòÉΓòÉΓòÉ
The #pragma entry directive specifies the function to be used as the entry
point for the application being built.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇentryΓöÇΓöÇ(ΓöÇΓöÇfunction_nameΓöÇΓöÇ)ΓöÇΓöÇ><
The function_name function must be in the same compilation unit as the #pragma
entry directive, and must be a defined external function.
Normally when an application is started, the OS/2 system calls the C library
entry point. When you specify a different entry point using #pragma entry, the
system calls that entry point and does not perform any C library initialization
or termination. If you use #pragma entry, you must ensure that your executable
file does not require library initialization or termination, or you must
provide your own initialization and termination functions.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.9. export ΓòÉΓòÉΓòÉ
The #pragma export directive declares that a DLL function or variable is to be
exported and specifies the name of the function or variable outside the DLL.
>>ΓöÇΓöÇ#-pragma-exportΓöÇΓöÇ(ΓöÇΓöÇidentifierΓöÇΓöÇ,ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ,ΓöÇΓöÇordinalΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇ"ΓöÇΓöÇexport_nameΓöÇΓöÇ"ΓöÇΓöÿ
The identifier is the name of the function or variable in the DLL. The
export_name is the name for identifier outside of the DLL. If no export_name is
specified, identifier is used.
The ordinal is the number of the identifier within the DLL. Another module can
import the identifier using either the export_name or the ordinal number.
Ordinal numbers are described in more detail in the Toolkit documentation.
For example, the statements:
int deborah(int);
#pragma export(deborah, "catherine", 4)
declare that the function deborah is to be exported, and can be imported by
another module using the name catherine or the ordinal number 4. See #pragma
import for information on importing functions and variables.
You can also use the _Export keyword to export a function. If you use the
keyword, you cannot specify a different name or an ordinal for the exported
function.
If you use #pragma export to export your function, you may still need to
provide an EXPORTS entry for that function in your module definition (.DEF)
file. If your function has any of the following default characteristics
Has shared data
Has no I/O privileges
Is not resident
it does not require an EXPORTS entry. If your function has characteristics
other than the defaults, the only way you can specify them is with an EXPORTS
entry in your .DEF file.
Note: To create an import library for the DLL, you must either create it from
the DLL itself or provide a .DEF file with an EXPORTS entry for every
function, regardless of whether #pragma export is used.
For more information on DLLs and .DEF files, see the IBM VisualAge C++ for
OS/2 Programming Guide.
Related Information
#pragma import
_Export Qualifier
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.10. handler ΓòÉΓòÉΓòÉ
The #pragma handler directive registers an exception handler for a function.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇhandlerΓöÇΓöÇ(ΓöÇΓöÇfunctionΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇ,ΓöÇΓöÇexception_handlerΓöÇΓöÿ
The function is the name of the function for which the exception handler is to
be registered. You should declare it before you use it in this directive.
The #pragma handler directive generates the code at compile time to install the
VisualAge C++ exception handler _Exception before starting execution of the
function. It also generates code to remove the exception handler at function
exit.
You must use this directive whenever you change library environments or enter a
user-created DLL.
You can remap the _Exception exception handler to another name with
exception_handler, where exception_handler is the name of the function you
provide that will be the exception handler for the named function.
Note: If you are using the subsystem libraries, the _Exception function is not
provided. To use the #pragma handler directive in a subsystem, you must provide
your own exception handler named _Exception. Otherwise, you must register and
remove your own exception handlers using the OS/2 exception handler APIs
described in the IBM OS/2 2.0 Programming Reference.
For more information on exception handling and _Exception, see the IBM
VisualAge C++ for OS/2 Programming Guide.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.11. hdrfile ΓòÉΓòÉΓòÉ
The #pragma hdrfile directive specifies the filename of the precompiled header
to be generated and/or used.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇhdrfileΓöÇΓöÇ"file_name"ΓöÇΓöÇ><
It must appear before the first #include directive and either the /Fi option or
the /Si option is specified. The /Si and /Fi options allow more than one
precompiled header to be use for a single application.
If a file name is specified both on the command line and on #pragma hdrfile,
the name specified on the pragma takes precedence. If the name specified is a
directory, then the the compiler searches for or generates a file with the
default name in that directory.
In order to maximize the reuse of precompiled headers, the use #pragma hdrfile
in combination with #pragma hdrstop to manually limit the initial sequence of
#include directives.
Use precompiled header files to decrease compile time. Using precompiled
headers will not improve compile time performance in most applications without
some organization of the headers included by each source file.
The IBM VisualAge C++ for OS/2 Programming Guide describes how to structure
your files so the compiler can take full advantage of the precompiled headers.
Examples of #pragma hdrfile Directives
Related Information
/Fi option
/Si option
Precompiled header files
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ <hidden> Examples of #pragma hdrfile Directives ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, the headers h1.h and h2.h are precompiled using the
file fred.pch (provided /Si or /Fi are specified). If /Fidave.pch is specified
alone, the compiler looks for the precompiled headers in fred.pch but will not
generate new headers.
*
************************************************************************/
#pragma hdrfile "fred.pch"
#include "h1.h"
#include "h2.h"
main () {}
/************************************************************************
*
In the following example, only the header h1.h will be precompiled using the
file fred.pch (provided /Si or /Fi are specified). If /Sidave.pch is specified
alone, the compiler looks for the precompiled headers in fred.pch but will not
generate new headers.
*
************************************************************************/
#pragma hdrfile "fred.pch"
#include "h1.h"
#pragma hdrstop
#include "h2.h"
main () {}
ΓòÉΓòÉΓòÉ 9.13.12. hdrstop ΓòÉΓòÉΓòÉ
The #pragma hdrstop directive manually terminates the initial sequence of
#include directives being considered for precompilation.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇhdrstopΓöÇΓöÇ><
It has no effect if:
The initial sequence of #include directives has already ended
Neither the /Fi option nor the /Si option is specified
It does not appear in the primary source file
Use precompiled header files to decrease compile time. Using precompiled
headers will not improve compile time performance in most applications without
some organization of the headers included by each source file.
The IBM VisualAge C++ for OS/2 User's Guide and Reference describes how to
structure your files so the compiler can take full advantage of the
precompiled headers.
Examples of #pragma hdrstop Directives
Related Information
/Fi option
/Si option
Precompiled header files
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ <hidden> Examples of #pragma hdrstop Directives ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, only the header h1.h will be precompiled using the
file default.pch (provided /Si or /Fi are specified). If /Sidave.pch
/Fijohn.pch are specified, the compiler will look for the precompiled headers
in john.pch and will regenerate them if they are not found or not usable.
*
************************************************************************/
#include "h1.h"
#pragma hdrstop
#include "h2.h"
main () {}
/************************************************************************
*
In the following example, no precompiled headers will be generated or used for
the compilation, even if /Fi or /Si are specified.
*
************************************************************************/
#pragma hdrstop
#include "h1.h"
#include "h2.h"
main () {}
ΓòÉΓòÉΓòÉ 9.13.13. implementation ΓòÉΓòÉΓòÉ
The #pragma implementation directive tells the compiler the name of the file
containing the function-template definitions that correspond to the template
declarations in the include file which contains the pragma.
Note: This directive is valid for C++ programs only.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇimplementationΓöÇΓöÇ(ΓöÇΓöÇstring_literalΓöÇΓöÇ)ΓöÇΓöÇ><
This pragma can appear anywhere that a declaration is allowed. It is used when
organizing your program for the efficient or automatic generation of template
functions. "Using Templates in C++ Programs" in the IBM VisualAge C++ for OS/2
Programming Guide gives more information about using #pragma implementation.
Related Information
C++ Templates
Pragma Directives (#pragma)
Preprocessor Directives
"Using Templates in C++ Programs" in the IBM VisualAge C++ for OS/2
Programming Guide
ΓòÉΓòÉΓòÉ 9.13.14. import ΓòÉΓòÉΓòÉ
The #pragma import directive lets you import a function or a variable from a
DLL using either an ordinal number or a name different from the one that it has
in the DLL.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇimportΓöÇΓöÇ(ΓöÇΓöÇidentifierΓöÇΓöÇ,ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ,ΓöÇΓöÇ>
ΓööΓöÇ"ΓöÇΓöÇexternal_nameΓöÇΓöÇ"ΓöÇΓöÿ
>ΓöÇΓöÇ"ΓöÇΓöÇmod_nameΓöÇΓöÇ"ΓöÇΓöÇ,ΓöÇΓöÇordinalΓöÇΓöÇ)ΓöÇΓöÇ><
The identifier is the name you use in your source to refer to the function or
variable. The external_name is the name of the function or variable in the DLL.
For C++ files, external_name can also be a function prototype. If external_name
is not specified, it is assumed to be the same as identifier.
Note: Both identifier and external_name must be defined only once in each
compilation unit.
The mod_name is the name of the DLL containing the identifier, and ordinal
indicates the position of the function or variable within the DLL. Ordinal
numbers are described in more detail in the Toolkit documentation.
The information provided by #pragma import is used at load time to locate the
imported identifier. If ordinal is 0, the external_name is used to find the
identifier. If ordinal is any other number, external_name is ignored and the
identifier is located by number. It is usually faster to locate the identifier
by number than by name.
Using #pragma import decreases your link time because the linker does not
require an import library to resolve external names. This directive is also
useful for C++ programming because you do not have to use the fully qualified
name to refer to an imported function or variable.
Note: You cannot use the ordinals provided in the Toolkit header files with
#pragma import. These ordinals are provided as C macros that cannot be used in
#pragma directives.
Related Information
#pragma export
_Export Qualifier
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.15. info ΓòÉΓòÉΓòÉ
The #pragma info directive controls the diagnostic messages generated by the
/Wgroup compiler options.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇinfoΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇallΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
Γö£ΓöÇnoneΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇrestoreΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇΓöÇΓö¼ΓöÇgroupΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÿ
ΓööΓöÇnogroupΓöÇΓöÿ
The #pragma info directive replaces the #pragma checkout directive for
controlling diagnostics. You can use this pragma directive in place of the
/Wgroup option. Specifying #pragma info(group) causes all messages associated
with that diagnostic group to be generated. Specifying #pragma info(nogroup)
suppresses all messages associated with that group.
For example, to generate messages for missing function prototypes and
statements with no effect, but not for uninitialized variables, specify:
#pragma info(pro, eff, nouni)
The #pragma directive overrides any /Wgroup options stated on the command line.
Use #pragma info(all) to turn on all diagnostic checking. Use #pragma
info(none) to turn off all diagnostic suboptions for specific portions of your
program. Specifying #pragma info(restore) restores the options that were in
effect before the previous #pragma info directive.
Because #pragma info operates like a stack, the options restored may not be
those given on the command line. If no options were previously in effect,
#pragma info(restore) does nothing.
The following list explains the groups of diagnostic messages controlled by
each group option:
Group Diagnostics
cmp Possible redundancies in unsigned comparisons.
cnd Possible redundancies or problems in conditional expressions.
cns Operations involving constants.
cnv Conversions.
dcl Consistency of declarations.
eff Statements with no effect.
enu Consistency of enum variables.
ext Unused external definitions.
gen General diagnostics.
got Usage of goto statements.
ini Possible problems with initialization.
lan Effects of the language level.
obs Features that are obsolete.
ord Unspecified order of evaluation.
par Unused parameters.
por Nonportable language constructs.
ppc Possible problems with using the preprocessor.
ppt Trace of preprocessor actions.
pro Missing function prototypes.
rea Code that cannot be reached.
ret Consistency of return statements.
trd Possible truncation or loss of data or precision.
tru Variable names truncated by the compiler.
uni Uninitialized variables.
use Unused auto and static variables.
The /Wgroup options are described in the IBM VisualAge C++ for OS/2 User's
Guide and Reference.
Related Information
#pragma checkout
/Wgroup option
/Kn options
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.16. langlvl ΓòÉΓòÉΓòÉ
The #pragma langlvl directive lets you select elements of VisualAge C++
implementation.
ΓöîΓöÇextendedΓöÇΓöÉ
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇlanglvlΓöÇΓöÇ(ΓöÇΓöÇΓö╝ΓöÇansiΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇ)ΓöÇΓöÇ><
Γö£ΓöÇcompatΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇsaaΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓööΓöÇsaal2ΓöÇΓöÇΓöÇΓöÇΓöÿ
This directive can be specified only once in your source file, and must appear
before any C code.
The compiler defines preprocessor variables that are used in header files to
define the language level. The options are:
extended Defines the preprocessor variable __EXTENDED__. Allows ANSI and
SAA C constructs and all VisualAge C++ extensions.
ansi Defines the preprocessor variables __ANSI__ and __STDC__. Allows
only language constructs that conform to ANSI C standards. Note
that for C++, the __STDC__ macro is set to 0, while for C it is
set to 1.
compat Defines the preprocessor variable __COMPAT__. Allows constructs
supported by earlier versions of the C++ language, as well as ANSI
constructs and VisualAge C++ extensions. This language level is
valid for C++ programs only.
saa Defines the preprocessor variables __SAA__ and __SAAL2__. Allows
only language constructs that conform to the most recent level of
SAA C standards (currently Level 2). These include ANSI C
constructs. This language level is valid for C programs only.
saal2 Defines the preprocessor variable __SAAL2__. Allows only language
constructs that conform to SAA Level 2 C standards. These include
ANSI C constructs. This language level is valid for C programs
only.
The default language level is extended.
You can also set the language level using the /Sa, /Sc, /S2, and /Se compiler
options. These options are described in /S options
Related Information
/Sa, /Sc, /S2, /Se options
Predefined Macro Names
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.17. library ΓòÉΓòÉΓòÉ
The #pragma library directive causes the compiler to insert an INCLUDELIB
library search record into the object file. This tells the linker to pull in
the appropriate libraries at link time.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇlibraryΓöÇΓöÇ(ΓöÇΓöÇ"library_name"ΓöÇΓöÇ)ΓöÇΓöÇ><
where library_name is the default library to be made available for the program.
The library names specified by #pragma library are imbedded in INCLUDELIB
library search records in the order that they are encountered in the source.
The library search records are inserted into the object file before the default
library search records, so that the behaviour at link time is the same as if
the library name were specified at link time.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.18. linkage ΓòÉΓòÉΓòÉ
The #pragma linkage directive identifies the linkage or calling convention used
on a function call.
Note:
1. This directive is valid for C programs only.
2. It is obsolete in this release of VisualAge C++. Avoid using it in new
code. For your new applications, use linkage keywords to specify the
calling convention for a function. Linkage keywords are easier to use
than is the #pragma linkage directive, and they let you declare both the
function and its linkage type in one statement. See Linkage Keywords for
more information on these keywords.
ΓöîΓöÇoptlinkΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇpragma-linkage-(ΓöÇΓöÇidentifier-,ΓöÇΓöÇΓö╝ΓöÇsystemΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇ)ΓöÇΓöÇ><
Γö£ΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇpascalΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé ΓööΓöÇfar32ΓöÇΓöÿ Γöé
ΓööΓöÇfar16ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ
Γö£ΓöÇcdeclΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇ_cdeclΓöÇΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇpascalΓöÇΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇ_pascalΓöÇΓöÇΓöÇΓöñ
Γö£ΓöÇfastcallΓöÇΓöÇΓöñ
ΓööΓöÇ_fastcallΓöÇΓöÿ
The identifier identifies either the name of the function that will be given
the particular linkage type or the name of a typedef that resolves to a
function type. If identifier is a typedef, any function declared using
identifier will be given the particular linkage type.
The following example shows how to use a typedef to declare functions and
pointers to functions of _System linkage:
#pragma linkage(functype, system)
typedef int functype(int);
functype f; /* f is a function with _System linkage */
functype *fp; /* fp is a pointer to a function with _System linkage */
The VisualAge C++ default linkage is _Optlink, which is a convention specific
to the VisualAge C++ product. If your program calls OS/2 APIs, you must use
the _System calling convention, which is standard for all OS/2 applications to
call those APIs. If you include the system header files, the OS/2 APIs are
automatically given _System linkage.
If you are developing device drivers, you should use the _Pascal convention.
You should use the _Far32 version of _Pascal linkage if your calls will cross
code segments. Note that _Far32 _Pascal linkage is only available when you
specify the /Gr+ option to generate code that runs at ring zero.
The _Far16 linkage conventions indicate that a function has a 16-bit linkage
type. The cdecl and _cdecl options are equivalent. The underscore is optional,
and is accepted for compatibility with C/2 cdecl linkage declarations.
Similarly, pascal and _pascal are equivalent, and specify C/2 pascal linkage;
fastcall and _fastcall specify Microsoft** _fastcall linkage. If far16 is
specified without a parameter, __cdecl linkage is used.
You can use compiler options to explicitly set the calling convention to
_Optlink (/Mp) or to change the default to _System linkage (/Ms). A linkage
keyword or #pragma linkage directive overrides the compiler option.
For more information about calling conventions, see the IBM VisualAge C++ for
OS/2 Programming Guide.
Related Information
/Mp, /Ms options
Linkage Keywords
typedef
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.19. map ΓòÉΓòÉΓòÉ
The #pragma map directive tells the compiler that all references to an
identifier are to be converted to "name".
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇmapΓöÇΓöÇ>
>ΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇidentifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ,ΓöÇΓöÇ"name"ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇfunc_or_op_identifierΓöÇΓöÇ(ΓöÇΓöÇargument_listΓöÇΓöÇ)ΓöÇΓöÿ
identifier a name of a data object or a nonoverloaded function
with external linkage.
func_or_op_identifier a name of a function or operator with internal linkage.
The name can be qualified.
argument_list a prototype list for the named function or operator.
name the external name that is to be bound to the given
object, function or operator.
The directive can appear anywhere in the source file within a single
compilation unit. It can appear before any declaration or definition of the
named object, function, or operator. The identifers appearing in the
directive, including any type names used in the prototype argument list, are
resolved as though the directive had appeared at file scope, independent of
its actual point of occurrence.
For example:
int func(int);
class X
{
public:
void func(void);
#pragma map(func, "funcname1") // maps ::func
#pragma map(X::func, "funcname2") // maps X::func
};
You should not use #pragma map to map member functions, overloaded functions,
or objects generated from templates. Such mappings override the
compiler-generated mangled names, which could cause binder errors. If mangled
names are overridden with #pragma map, the compiler issues a warning message.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.20. margins ΓòÉΓòÉΓòÉ
The #pragma margins directive specifies the columns in the input line that are
to be scanned for input to the compiler.
Note: This directive is valid for C programs only.
ΓöîΓöÇnomarginsΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇΓö┤ΓöÇmarginsΓöÇΓöÇ(ΓöÇΓöÇleftΓöÇΓöÇ,ΓöÇΓöÇrightΓöÇΓöÇ)ΓöÇΓö┤ΓöÇΓöÇ><
Use the #pragma margins directive if you want to have characters outside
certain columns ignored in your source file. The compiler ignores any text in
the source input that does not fall within the range specified in the
directive.
The variable left specifies the first column of the source input that is to be
scanned, and must be between 1 and 65535, inclusive. The variable right
specifies the last column of the source input that is to be scanned. It must be
greater than or equal to left and less than or equal to 65535. An asterisk (*)
can be assigned to right to indicate the last column of input.
By default, the left margin is set to 1, and the right margin is set to
infinity. The default for this directive is #pragma margins(), which has the
effect of setting the right margin to infinity.
The #pragma margins directive can be used with the #pragma sequence directive
to specify the columns that are not to be scanned for sequence numbers. If the
#pragma sequence settings do not fall within the #pragma margins settings, the
#pragma sequence directive has no effect.
You can also set margins using the /Sg option.
Related Information
/Sg option
#pragma sequence
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.21. pack ΓòÉΓòÉΓòÉ
The #pragma pack directive specifies the alignment rules to use for the
structures, unions, and classes that follow it. In C++, packing is performed on
declarations or types. This is different from C, where packing is also
performed on definitions.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇpackΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
Γö£ΓöÇ1ΓöÇΓöñ
Γö£ΓöÇ2ΓöÇΓöñ
ΓööΓöÇ4ΓöÇΓöÿ
The #pragma pack directive causes all structures, unions and classes that
follow it in the program to be packed along a 1-byte, 2-byte, or 4-byte
boundary, according to the value specified in the directive, until another
#pragma pack directive changes the packing boundary.
Packing along a 4-byte boundary is the system default.
If no value is specified, packing is performed along the system default
boundary unless the /Sp compiler option was used. If it is used, #pragma pack()
causes packing to be performed along the boundary specified by /Sp.
For example:
#pragma pack(1)
struct hester{ /* this structure is packed */
char philip; /* along 1-byte boundaries */
int mark;
};
.
.
.
#pragma pack(2)
struct jeff{ /* this structure is packed */
float bill; /* along 2-byte boundaries */
int *chris;
}
.
.
.
#pragma pack()
struct dor{ /* this structure is packed */
double stephen; /* along the default boundaries */
long alex;
}
Note: If data types are by default packed along boundaries smaller than those
specified by #pragma pack, they are still aligned along the smaller boundaries.
For example, type char is always aligned along a 1-byte boundary, regardless of
the value of #pragma pack.
The following table describes how each data type is packed for each of the
#pragma pack options:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé DATA TYPE Γöé #PRAGMA PACK VALUE Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Γöé 1 Γöé 2 Γöé 4 Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé char Γöé 1 Γöé 1 Γöé 1 Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé short Γöé 1 Γöé 2 Γöé 2 Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé int, long Γöé 1 Γöé 2 Γöé 4 Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé float, double, Γöé 1 Γöé 2 Γöé 4 Γöé
Γöé long double Γöé Γöé Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé pointer Γöé 1 Γöé 2 Γöé 4 Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Note: If more than one #pragma pack directive appears in a structure defined
in an inline function, the #pragma pack directive effective at the beginning of
the class takes precedence.
For more information on the alignment of data types in structures, see the IBM
VisualAge C++ for OS/2 Programming Guide.
Related Information
/Sp option
_Packed Qualifier
Structures
Type Specifiers
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.22. page ΓòÉΓòÉΓòÉ
The #pragma page directive skips the number of pages specified by pages of the
generated source listing. If pages is not specified, the next page is started.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇpageΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇpagesΓöÇΓöÿ
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.23. pagesize ΓòÉΓòÉΓòÉ
The #pragma pagesize directive sets the number of lines per page to lines for
the generated source listing.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇpagesizeΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇlinesΓöÇΓöÿ
The value of lines must be between 16 and 32767, inclusive. The default page
length is 66 lines.
You can also use the /Lp compiler option to set the listing page size.
Related Information
/Lp option
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.24. priority ΓòÉΓòÉΓòÉ
The #pragma priority directive specifies the order in which static objects are
to be initialized at run time.
Note: This directive is valid for C++ programs only.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇpriorityΓöÇΓöÇ(ΓöÇΓöÇnΓöÇΓöÇ)ΓöÇΓöÇ><
Where n is an integer literal in the range of INT_MIN to INT_MAX. The default
value is 0. A negative value indicates a higher priority; a positive value
indicates a lower priority.
The first 1024 priorities (INT_MIN to INT_MIN + 1023) are reserved for use by
the compiler and its libraries. The priority value specified applies to all
runtime static initialization in the current compilation unit.
Any global object declared before another object in a file is constructed
first. Use #pragma priority to specify the construction order of objects across
files.
To ensure that the objects are always constructed from top to bottom in a file,
the compiler enforces the restriction that the priority specified all objects
before and all objects after it until the next #pragma is at that priority.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.25. seg16 ΓòÉΓòÉΓòÉ
The #pragma seg16 directive specifies that a data object will be shared between
16-bit and 32-bit processes.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇseg16ΓöÇΓöÇ(ΓöÇΓöÇidentifierΓöÇΓöÇ)ΓöÇΓöÇ><
It causes the compiler to lay out the identifier in memory so that it does not
cross a 64K boundary. The identifier can then be used in a 16-bit program.
The identifier can be a typedef or a data object. For example:
typedef struct foo foostr;
#pragma seg16(foostr)
foostr quux;
uses the typedef foostr to declare quux as an object addressable by a 16-bit
program.
You can also use the /Gt compiler option to perform the equivalent of a #pragma
seg16 for all variables in the program.
Note: If #pragma seg16 is used on variables of a structure type, the pointers
inside that structure are not automatically qualified as usable by 16-bit
programs. If you want the pointers in the structure qualified as such, you must
declare them using the _Seg16 type qualifier.
See _Seg16 Type Qualifier for more information about _Seg16. For more
information on calling 16-bit programs, see the IBM VisualAge C++ for OS/2
Programming Guide.
Related Information
/Gt option
_Seg16 Type Qualifier
Structures
typedef
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.26. sequence ΓòÉΓòÉΓòÉ
The #pragma sequence directive defines the section of the input line that is to
contain sequence numbers.
Note: This directive is valid for C programs only.
ΓöîΓöÇnosequenceΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇΓö┤ΓöÇsequenceΓöÇΓöÇ(ΓöÇΓöÇleftΓöÇΓöÇ,ΓöÇΓöÇrightΓöÇΓöÇ)ΓöÇΓö┤ΓöÇΓöÇ><
If you are using a source file produced on a system that uses sequence numbers,
you can use this option to have the sequence numbers ignored.
The variable left specifies the column number of the left-hand margin. The
value of left must be between 1 and 65535 inclusive, and must also be less than
or equal to the value of right. The variable right specifies the column number
of the right-hand margin. The value of right must be greater than or equal to
left and less than or equal to 65535. An asterisk (*) can be assigned to right
to indicate the last column of the line.
The default for this directive is nosequence, which specifies there are no
sequence numbers.
The #pragma sequence directive can be used with the #pragma margins directive
to specify the columns that are not to be scanned. If the #pragma sequence
settings do not fall within the #pragma margins settings, the sequence
directive has no effect.
You can also set sequence numbers using the /Sq option.
Related Information
/Sq option
#pragma margins
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.27. skip ΓòÉΓòÉΓòÉ
The #pragma skip directive skips the specified number of lines of the generated
source listing. The value of lines must be a positive integer less than 255. If
lines is omitted, one line is skipped.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇskipΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇlinesΓöÇΓöÿ
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
/L options
ΓòÉΓòÉΓòÉ 9.13.28. sourcedir ΓòÉΓòÉΓòÉ
The #pragma sourcedir directive defines a new path to the directory containing
the original source from which the compiler generates files in the TEMPINC
directory.
Note: This directive is valid for C++ programs only.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇsourcedirΓöÇΓöÇ(ΓöÇΓöÇpathΓöÇΓöÇ)ΓöÇΓöÇ><
Instead of searching the TEMPINC directory first for the original source of the
include file, the pragma directs the compiler to the directory specified by the
supplied path. The compiler automatically inserts the necessary #pragma
sourcedir directives into the source files it generates in the TEMPINC
directory.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.29. stack16 ΓòÉΓòÉΓòÉ
The #pragma stack16 directive specifies the size of the stack to be allocated
for calls to 16-bit routines.
>>ΓöÇΓöÇΓöÇ#ΓöÇΓöÇΓöÇpragmaΓöÇΓöÇstack16ΓöÇΓöÇ(ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇsizeΓöÇΓöÿ
The variable size is the size of the stack in bytes, and has a value between
512 and 65532. The size specified applies to any 16-bit functions called from
that point until the end of the compilation unit, or until another #pragma
stack16 directive is given.
The default value is 4096 bytes (4K). Note that the 16-bit stack is taken from
the 32-bit stack allocated for the thread calling the 16-bit code. The 32-bit
stack is therefore reduced by the amount you specify with #pragma stack16. Make
sure your 32-bit stack is large enough for both your 32-bit and 16-bit code.
If the sum of the size, the number of bytes for parameters, and the number of
local bytes in the function calling the 16-bit routine is greater than 65532,
the actual stack size will be 65532 bytes less the number of parameter and
local bytes. If the sum of the parameter bytes and local bytes alone is greater
than 65532, no bytes will be allocated for calls to 16-bit routines.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.30. strings ΓòÉΓòÉΓòÉ
The #pragma strings directive sets the storage type for strings. It specifies
that the compiler can place strings into read-only memory or must place strings
into read/write memory.
ΓöîΓöÇwriteableΓöÇΓöÉ
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇstringsΓöÇΓöÇ(ΓöÇΓöÇΓö┤ΓöÇreadonlyΓöÇΓöÇΓö┤ΓöÇΓöÇ)ΓöÇΓöÇ><
C strings are read/write by default. C++ strings are readonly by default. This
pragma must appear before any C or C++ code in a file.
Related Information
String Literals
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.31. subtitle ΓòÉΓòÉΓòÉ
The #pragma subtitle directive places the text specified by subtitle on all
subsequent pages of the generated source listing.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇsubtitleΓöÇΓöÇ(ΓöÇΓöÇ"subtitle"ΓöÇΓöÇ)ΓöÇΓöÇ><
The string subtitle must be less than 255 characters.
You can also use the /Lu compiler option to specify the listing subtitle.
Related Information
/Lu option
#pragma title
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.32. title ΓòÉΓòÉΓòÉ
The #pragma title directive places the text specified by title on all
subsequent pages of the generated source listing.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇtitleΓöÇΓöÇ(ΓöÇΓöÇ"title"ΓöÇΓöÇ)ΓöÇΓöÇ><
The string title must be less than 255 characters.
You can also use the /Lt compiler option to specify the listing title.
Related Information
/Lt option
#pragma subtitle
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.33. undeclared ΓòÉΓòÉΓòÉ
The #pragma undeclared directive is used only by the compiler and only in
template-include files. It is valid for C++ programs only.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇundeclaredΓöÇΓöÇ><
In the template-include file, template functions that were explicitly declared
in at least one compilation unit appear before this line. Template functions
that were called, but never declared, appear after this line.
For more information on template-include files, see "Using Templates in C++
Programs" in the Programming Guide.
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 9.13.34. weak ΓòÉΓòÉΓòÉ
The #pragma weak directive adds an alternate function name with weak binding
for the function function_name.
>>ΓöÇΓöÇ#ΓöÇΓöÇpragmaΓöÇΓöÇweakΓöÇΓöÇ(ΓöÇΓöÇfunction_nameΓöÇΓöÇ,ΓöÇΓöÇbackup_function_nameΓöÇΓöÇ)ΓöÇΓöÇ><
If the definition for the function function_name is not found, the linker
resolves the function call to the definition for the function
backup_function_name. If the definition for function_name is found, the linker
resolves the function call to the definition for function_name.
If function_name is not referenced, neither function_name nor
backup_function_name. needs to be declared.
If function_name is referenced, both function_name and backup_function_name.
must be declared.
Note: Both functions must have full prototypes within the compilation unit.
Neither function can be a C++ member function.
In the following example, after the program is linked, the call to
specialization resolves to the definition of generalization because no
definition of specialization exists.
#include <stdio.h>
int generalization(int i) {
printf("in generalization\n");
}
#pragma weak (specialization, generalization)
int main() {
printf("in main\n");
return specialization(6);
}
Related Information
Pragma Directives (#pragma)
Preprocessor Directives
ΓòÉΓòÉΓòÉ 10. C++ Classes ΓòÉΓòÉΓòÉ
A C++ class is a mechanism for creating user-defined data types. It is similar
to the C-language structure data type. In C, a structure is composed of a set
of data members. In C++, a class type is like a C structure, except that a
class is composed of a set of data members and an optional set of operations
that can be performed on the class.
In C++, a class type can be declared with the keywords union, struct, or class.
A union object can hold any one of a set of named members. Structure and class
objects hold a complete set of members. Each class type represents a unique set
of class members including data members, member functions, and other type
names. The default access for members depends on the class key:
The members of a class declared with the class key class are private by
default. A class is inherited privately by default.
The members of a class declared with the class key struct are public be
default. A structure is inherited publicly by default.
The members of a union (declared with the class key union) are public by
default. A union cannot be used as a base class in derivation. Base
classes and derivation are described in C++ Inheritance.
Once you create a class type, you can declare one or more objects of that
class type.
For example:
class X
{ /* define class members here */ };
void main()
{
X xobject1; // create an object of class type X
X xobject2; // create another object of class type X
}
This chapter discusses the following topics:
Declaring Class Objects
Scope of Class Names
Classes are also used in C++ to support polymorphic functions through
overloaded functions (static compile time binding) and virtual functions
(dynamic binding). C++ allows you to redefine standard operators and functions
through the concept of overloading. Operator overloading facilitates data
abstraction by allowing you to use classes as easily as built-in types.
Related Information
C++ Class Members and Friends
C++ Inheritance
C++ Overloading
Virtual Functions
Structures
Unions
ΓòÉΓòÉΓòÉ 10.1. Classes and Structures ΓòÉΓòÉΓòÉ
The C++ class is an extension of the C-language structure. Because the only
difference between a structure and a class is that structure members have
public access by default and a class members have private access by default,
you can use the keywords class or struct to define equivalent classes.
For example, in the following code fragment, the class X is equivalent to the
structure Y:
// In this example, class X is equivalent to struct Y
class X
{
int a; // private by default
public:
int f() { return a = 5; }; // public member function
};
struct Y
{
int f() { return a = 5; }; // public by default
private:
int a; // private data member
};
If you define a structure and then declare an object of that structure using
the keyword class, the members of the object are still public by default.
Example of Access for Classes and Structures
An aggregate class is a class that has no user-defined constructors, no private
or protected members, no base classes, and no virtual functions.
Initialization of aggregate classes is described in Initializers.
Related Information
Structures
Declaring Class Objects
Scope of Class Names
ΓòÉΓòÉΓòÉ <hidden> Example of Access for Classes and Structures ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, main() has access to the members of X even though X
is declared as using the keyword class:
*
************************************************************************/
// This example declares a structure, then declares a class
// that is an object of the structure.
#include <iostream.h>
struct x {
int a;
int b;
} ;
class x X;
void main() {
X.a = 0;
X.b = 1;
cout << "Here are e and f " << X.a << " " << X.b << endl;
}
ΓòÉΓòÉΓòÉ 10.2. Declaring Class Objects ΓòÉΓòÉΓòÉ
A class declaration creates a unique type class name.
A class specifier is used to declare a class. Once a class specifier has been
seen and its members declared, a class is considered to be defined even if the
member functions of that class are not yet defined. A class specifier has the
following form:
>>ΓöÇΓöÇΓö¼ΓöÇclassΓöÇΓöÇΓö¼ΓöÇΓöÇclass_nameΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ{ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ}ΓöÇΓöÇ><
Γö£ΓöÇstructΓöÇΓöñ ΓööΓöÇ:base_classΓöÇΓöÿ ΓööΓöÇmember_listΓöÇΓöÿ
ΓööΓöÇunionΓöÇΓöÇΓöÿ
The member_list is optional. It specifies the class members, both data and
functions, of the class class_name. If the member_list of a class is empty,
objects of that class have a nonzero size. You can use a class_name within the
member_list of the class specifier itself as long as the size of the class is
not required. For more information, see Class Member Lists.
The base_class is optional. It specifies the base class or classes from which
the class class_name inherits members. If the base_class is not empty, the
class class_name is called a derived class.
The declarator for a class variable declared with the class, struct, or union
keyword is an identifier. If the symbol * precedes the identifier, the
identifier names a pointer to a class of the specified data type. If **
precedes the identifier, the identifier names a pointer to a pointer to a class
of the specified data type.
If a constant expression enclosed in [ ] (brackets) follows the identifier, the
identifier names an array of classes of the specified data type. If * precedes
the identifier and a constant expression enclosed in [ ] follows the
identifier, the identifier names an array of pointers to classes of the
specified data type.
Related Information
Class Names
Scope of Class Names
Class Member Lists
Derivation
ΓòÉΓòÉΓòÉ 10.2.1. Class Names ΓòÉΓòÉΓòÉ
A class name is a unique identifier that becomes a reserved word within its
scope. Once a class name is declared, it hides other declarations of the same
name within the enclosing scope.
If a class name is declared in the same scope as a function, enumerator, or
object with the same name, that class can be referred to by using an elaborated
type specifier. In the following example, the elaborated type specifier is used
to refer to the class print that is hidden by the later definition of the
function print():
class print
{
/* definition of class print */
};
void print (class print*); // redefine print as a function
// . // prefix class-name by class-key
// . // to refer to class print
// .
void main ()
{
class print* paper; // prefix class-name by class-key
// to refer to class print
print(paper); // call function print
}
You can use an elaborated type specifier with a class name to declare a class.
For more information on elaborated type specifiers, see Incomplete Class
Declarations.
You can also qualify type names to refer to hidden type names in the current
scope. You can reduce complex class name syntax by using a typedef to represent
a nested class name.
Example of Using a typedef for a Class Name
Related Information
Declaring Class Objects
Scope of Class Names
Nested Classes
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Nested Class Specifier ΓòÉΓòÉΓòÉ
The syntax of a nested class specifier is:
>>ΓöÇΓöÇclass_nameΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ::nested_classΓöÇΓöÿ
where class_name specifies the name of the enclosing class and nested-class
specifies the name of the nested class. For more information, see Nested
Classes.
ΓòÉΓòÉΓòÉ <hidden> Example of Using a typedef for a Class Name ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, a typedef is used so that the simple name nested can
be used in place of outside::middle::inside.
*
************************************************************************/
// This example illustrates a typedef used to simplify
// a nested class name.
#include <iostream.h>
class outside {
public:
class middle {
public:
class inside {
private:
int a;
public:
inside(int a_init = 0): a(a_init) {}
void printa();
};
};
};
typedef outside::middle::inside nested;
void nested::printa() {
cout << "Here is a " << this->a << endl;
}
void main() {
nested n(9);
n.printa();
}
ΓòÉΓòÉΓòÉ 10.2.2. Using Class Objects ΓòÉΓòÉΓòÉ
You can use a class type to create instances or objects of that class type. For
example, you can declare a class, structure, and union with class names X, Y,
and Z respectively:
class X { /* definition of class X */ };
struct Y { /* definition of struct Y */ };
union Z { /* definition of union Z */ };
You can then declare objects of each of these class types. Remember that
classes, structures, and unions are all types of C++ classes.
void main()
{
X xobj; // declare a class object of class type X
Y yobj; // declare a struct object of class type Y
Z zobj; // declare a union object of class type Z
}
In C++, unlike C, you do not need to precede declarations of class objects with
the keywords union, struct, and class unless the name of the class is hidden.
For more information on hidden names, see Scope of Class Names.
When you declare more than one class object in a declaration, the declarators
are treated as if declared individually. For example, if you declare two
objects of class S in a single declaration:
class S { /* ... */ };
void main()
{
S S,T; // declare two objects of class type S
}
this declaration is equivalent to:
class S { /* ... */ };
void main()
{
S S;
class S T; // keyword class is required
// since variable S hides class type S
}
but is not equivalent to:
class S { /* ... */ };
void main()
{
S S;
S T; // error, S class type is hidden
}
You can also declare references to classes, pointers to classes, and arrays of
classes.
Examples of Declaring Class Objects
Objects of class types that are not copy restricted can be assigned, passed as
arguments to functions, and returned by functions. For more information, see
Copying Class Objects.
Related Information
Objects
Scope of Class Names
Initialization by Constructor
Copying Class Objects
ΓòÉΓòÉΓòÉ <hidden> Examples of Declaring Class Objects ΓòÉΓòÉΓòÉ
/************************************************************************
*
In C++, unlike C, you do not need to precede declarations of class objects with
the keywords union, struct, and class unless the name of the class is hidden.
For example:
*
************************************************************************/
struct Y { /* ... */ };
class X { /* ... */ };
void main ()
{
int X; // hides the class name X
Y yobj; // valid
X xobj; // error, class name X is hidden
class X xobj; // valid
}
/************************************************************************
*
The following example declares a reference, a pointer, and an array:
*
************************************************************************/
class X { /* ... */ };
struct Y { /* ... */ };
union Z { /* ... */ };
void main()
{
X xobj;
X &xref = xobj; // reference to class object of type X
Y *yptr; // pointer to struct object of type Y
Z zarray[10]; // array of 10 union objects of type Z
}
ΓòÉΓòÉΓòÉ 10.3. Scope of Class Names ΓòÉΓòÉΓòÉ
A class declaration introduces the class name into the scope where it is
declared. Any class, object, function or other declaration of that name in an
enclosing scope is hidden. If a class name is declared in a scope where an
object, function, or enumerator of the same name is also declared, you can only
refer to the class by using the elaborated type specifier. The class key
(class, struct, or union) must precede the class name to identify it.
For example:
// This example shows the scope of class names.
class x { int a; }; // declare a class type class-name
x xobject; // declare object of class type x
int x(class x*) // redefine x to be a function
{return 0;} // use class-key class to define
// a pointer to the class type x
// as the function argument
void main()
{
class x* xptr; // use class-key class to define
// a pointer to class type x
xptr = &xobject; // assign pointer
x(xptr); // call function x with pointer to class x
}
An elaborated type specifier can be used in the declaration of objects and
functions. See Class Names for an example.
An elaborated type specifier can also be used in the incomplete declaration of
a class type to reserve the name for a class type within the current scope.
Related Information
Incomplete Class Declarations
Nested Classes
Local Classes
Local Type Names
Class Names
Declaring Class Objects
Scope in C
ΓòÉΓòÉΓòÉ 10.3.1. Incomplete Class Declarations ΓòÉΓòÉΓòÉ
An incomplete class declaration is a class declaration that does not define any
class members. You cannot declare any objects of the class type or refer to the
members of a class until the declaration is complete. However, an incomplete
declaration allows you to make specific references to a class prior to its
definition as long as the size of the class is not required.
For example, you can define a pointer to the structure first in the definition
of the structure second. Structure first is declared in an incomplete class
declaration prior to the definition of second, and the definition of oneptr in
structure second does not require the size of first:
struct first; // incomplete declaration of struct first
struct second // complete declaration of struct second
{
first* oneptr; // pointer to struct first refers to
// struct first prior to its complete
// declaration
first one; // error, you cannot declare an object of
// an incompletely declared class type
int x, y;
};
struct first // complete declaration of struct first
{
second two; // define an object of class type second
int z;
};
If you declare a class with an empty member list, it is a complete class
declaration. For example:
class X; // incomplete class declaration
class Z {}; // empty member list
class Y
{
public:
X yobj; // error, cannot create an object of an
// incomplete class type
Z zobj; // valid
};
Related Information
Declaring Class Objects
Class Member Lists
Scope of Class Names
ΓòÉΓòÉΓòÉ 10.3.2. Nested Classes ΓòÉΓòÉΓòÉ
A nested class is declared within the scope of another class. The name of a
nested class is local to its enclosing class. Unless you use explicit
pointers, references, or object names, declarations in a nested class can only
use visible constructs, including type names, static members, and enumerators
from the enclosing class and global variables.
Member functions of a nested class follow regular access rules and have no
special access privileges to members of their enclosing classes. Member
functions of the enclosing class have no special access to members of a nested
class.
You can define member functions and static data members of a nested class in
the global scope. For example, in the following code fragment, you can access
the static members x and y and member functions f() and g() of the nested class
nested by using a qualified type name. Qualified type names allow you to define
a typedef to represent a qualified class name. You can then use the typedef
with the :: (scope resolution) operator to refer to a nested class or class
member, as shown in the following example:
class outside
{
public:
class nested
{
public:
static int x;
static int y;
int f();
int g();
};
};
int outside::nested::x = 5;
int outside::nested::f() { return 0; };
typedef outside::nested outnest; // define a typedef
int outnest::y = 10; // use typedef with ::
int outnest::g() { return 0; };
// . . .
Related Information
Class Names
Declaring Class Objects
Scope of Class Names
ΓòÉΓòÉΓòÉ 10.3.3. Local Classes ΓòÉΓòÉΓòÉ
A local class is declared within a function definition. The local class is in
the scope of the enclosing function scope. Declarations in a local class can
only use type names, enumerations, static variables from the enclosing scope,
as well as external variables and functions.
Examples of Local Classes
Member functions of a local class have to be defined within their class
definition. Member functions of a local class must be inline functions. Like
all member functions, those defined within the scope of a local class do not
need the keyword inline.
A local class cannot have static data members. In the following example, an
attempt to define a static member of a local class causes an error:
void f()
{
class local
{
int f(); // error, local class has noninline
// member function
int g() {return 0;} // valid, inline member function
static int a; // error, static is not allowed for
// local class
int b; // valid, nonstatic variable
};
}
// . . .
An enclosing function has no special access to members of the local class.
Related Information
Scope of Class Names
Function Scope
Inline Member Functions
Inline Specifiers
Local Type Names
ΓòÉΓòÉΓòÉ <hidden> Examples of Local Classes ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example uses local classes.
*
************************************************************************/
int x; // global variable
void f() // function definition
{
static int y; // static variable y can be used by
// local class
int x; // auto variable x cannot be used by
// local class
extern int g(); // extern function g can be used by
// local class
class local // local class
{
int g() { return x; } // error, local variable x
// cannot be used by g
int h() { return y; } // valid,static variable y
int k() { return ::x; } // valid, global x
int l() { return g(); } // valid, extern function g
};
}
void main()
{
local* z; // error, local is undefined
// .
// .
// .
}
ΓòÉΓòÉΓòÉ 10.3.4. Local Type Names ΓòÉΓòÉΓòÉ
Local type names follow the same scope rules as other names. Scope rules are
described in Scope in C++. Type names defined within a class declaration have
class scope and cannot be used outside their class without qualification.
If you use a class name, typedef name, or a constant name that is used in a
type name, in a class declaration, you cannot redefine that name after it is
used in the class declaration.
For example:
void main ()
{
typedef double db;
struct st
{
db x;
typedef int db; // error
db y;
};
}
The following declarations are valid:
typedef float T;
class s {
typedef int T;
void f(const T);
};
Here, function f() takes an argument of type s::T. However, the following
declarations, where the order of the members of s has been reversed, cause an
error:
typedef float T;
class s {
void f(const T);
typedef int T;
};
In a class declaration, you cannot redefine a name that is not a class name, or
a typedef name to a class name or typedef name once you have used that name in
the class declaration.
Related Information
Scope in C
Declaring Class Objects
Scope of Class Names
Local Classes
typedef
ΓòÉΓòÉΓòÉ 11. C++ Class Members and Friends ΓòÉΓòÉΓòÉ
This chapter describes class members and friends, including the following
topics:
Class Member Lists
Data Members
Class-Type Class Members
Member Functions
Member Scope
Pointers to Members
The this Pointer
Static Members
Member Access
Friends
Related Information
C++ Classes
C++ Inheritance
Special C++ Member Functions
ΓòÉΓòÉΓòÉ 11.1. Class Member Lists ΓòÉΓòÉΓòÉ
An optional member list declares sub-objects called class members. Class
members can be data, functions, classes, enumeration, bit fields, and typedef
names. A member list is the only place you can declare class members. Friend
declarations are not class members but must appear in member lists.
The member list follows the class name and is placed between braces. It can
contain access specifiers, member declarations, and member definitions.
You can access members by using the class access . (dot) and -> (arrow)
operators.
An access specifier is one of public, private, or protected.
A member declaration declares a class member for the class containing the
declaration. For more information on declarations, see Declarations, and
Declaring Class Objects.
A member declaration that is a qualified name followed by a ; (semicolon) is
used to restore access to members of base classes and is described in Access
Declarations.
A member declarator declares an object, function, or type within a declaration.
It cannot contain an initializer. You can initialize a member by using a
constructor or, if the member belongs to an aggregate class, by using a brace
initializer list (a list surrounded by braces { }) in the declarator list. You
must explicitly initialize a class containing constant or reference members
with a brace initializer list or explicitly with a constructor.
A member declarator of the form:
[identifier] : constant-expression
specifies a bit field.
A pure specifier (= 0) indicates that a function has no definition. It is only
used with virtual member functions and replaces the function definition of a
member function in the member list. Pure specifiers are described in Virtual
Functions.
You can use the storage-class specifier static (but not extern, auto or
register) in a member list.
The order of mapping of class members in a member list is implementation
dependent. For the VisualAge C++ compiler, class members are allocated in the
order they are declared. For more information, see the IBM VisualAge C++ for
OS/2 User's Guide and Reference.
Related Information
Member Access
Dot Operator .
Arrow Operator ->
Declaring Class Objects
Access Declarations
Initialization by Constructor
Virtual Functions
Initializers
Storage Class Specifiers
ΓòÉΓòÉΓòÉ 11.2. Data Members ΓòÉΓòÉΓòÉ
Data members include members that are declared with any of the fundamental
types, as well as other types, including pointer, reference, array types, and
user-defined types. You can declare a data member the same way as a variable,
except that explicit initializers are not allowed inside the class definition.
If an array is declared as a nonstatic class member, you must specify all of
the dimensions of the array.
Related Information
Type Specifiers
Class Member Lists
Class-Type Class Members
Static Members
Member Access
ΓòÉΓòÉΓòÉ 11.3. Class-Type Class Members ΓòÉΓòÉΓòÉ
A class can have members that are of a class type or are pointers or references
to a class type. Members that are of a class type must be of a class type that
is previously declared. An incomplete class type can be used in a member
declaration as long as the size of the class is not needed. For example, a
member can be declared that is a pointer to an incomplete class type.
A class X cannot have a member that is of type X, but it can contain pointers
to X, references to X, and static objects of X. Member functions of X can take
arguments of type X and have a return type of X. For example:
class X
{
X();
X *xptr;
X &xref;
static X xcount;
X xfunc(X);
};
The bodies of member functions are always processed after the definition of
their class is complete. For this reason, the body of a member function can
refer to the name of the class that owns it. even if this requires information
about the class definition.
The language allows member functions to refer to any class member even if the
member function definition appears before the declaration of that member in the
class member list. For example,
class Y
{
public:
int a;
Y ();
private:
int f() {return sizeof(Y);};
void g(Y yobj);
Y h(int a);
};
In this example, it is permitted for the inline function f() to make use of the
size of class Y.
Related Information
Incomplete Class Declarations
Member Functions
Inline Member Functions
C++ Classes
Class Member Lists
ΓòÉΓòÉΓòÉ 11.4. Member Functions ΓòÉΓòÉΓòÉ
Member functions are operators and functions that are declared as members of a
class. Member functions do not include operators and functions declared with
the friend specifier. These are called friends of a class.
The definition of a member function is within the scope of its enclosing class.
The body of a member function is analyzed after the class declaration so that
members of that class can be used in the member function body. When the
function add() is called in the following example, the data variables a, b, and
c can be used in the body of add().
class x
{
public:
int add() // inline member function add
{return a+b+c;};
private:
int a,b,c;
};
There are several kinds of member functions:
const and volatile member functions
Virtual member functions
Special member functions
Inline member functions
Member function templates
Related Information
Member Scope
Static Member Functions
Functions
Class Member Lists
ΓòÉΓòÉΓòÉ <hidden> const and volatile Member Functions ΓòÉΓòÉΓòÉ
A member function declared with the const qualifier can be called for constant
and nonconstant objects. A nonconstant member function can only be called for a
nonconstant object. Similarly, a member function declared with the volatile
qualifier can be called for volatile and nonvolatile objects. A nonvolatile
member function can only be called for a nonvolatile object.
ΓòÉΓòÉΓòÉ <hidden> Virtual Member Functions ΓòÉΓòÉΓòÉ
Virtual member functions are declared with the keyword virtual. They allow
dynamic binding of member functions. Because all virtual functions must be
member functions, virtual member functions are simply called virtual functions.
If the definition of a virtual function is replaced by a pure specifier in the
declaration of the function, the function is said to be declared pure. A class
that has at least one pure virtual function is called an abstract class.
ΓòÉΓòÉΓòÉ <hidden> Special Member Functions ΓòÉΓòÉΓòÉ
Special member functions are used to create, destroy, initialize, convert, and
copy class objects. These include:
Constructors
Destructors
Conversion constructors
Conversion functions
Copy constructors
ΓòÉΓòÉΓòÉ <hidden> Inline Member Functions ΓòÉΓòÉΓòÉ
A member function that is both declared and defined in the class member list is
called an inline member function. Member functions containing a few lines of
code are usually declared inline.
An equivalent way to declare an inline member function is to declare it outside
of the class declaration using the keyword inline and the :: (scope resolution)
operator to identify the class the member function belongs to. For example:
class Y
{
char* a;
public:
char* f() {return a;};
};
is equivalent to:
class Z
{
char* a;
public:
char* f();
};
inline char* Z::f() {return a;}
When you declare an inline function without the inline keyword and do not
define it in the class member list, you cannot call the function before you
define it. In the above example, you cannot call f() until after its
definition.
Inline member functions have internal linkage. Noninline member functions have
external linkage.
For more information, see C++ Inline Functions.
ΓòÉΓòÉΓòÉ <hidden> Member Function Templates ΓòÉΓòÉΓòÉ
Any member function (inlined or noninlined) declared within a class template is
implicitly a function template. When a template class is declared, it
implicitly generates template functions for each function defined in the class
template. If a class template is instantiated, only the function templates
whose instantiations will actually be used by the resulting template class are
instantiated.
For more information about member function templates, see Member Function
Templates.
ΓòÉΓòÉΓòÉ 11.5. Member Scope ΓòÉΓòÉΓòÉ
Member functions and static members can be defined outside their class
declaration if they have already been declared, but not defined, in the class
member list. Nonstatic data members are defined when their class is
instantiated. The declaration of a static data member is not a definition. The
declaration of a member function is a definition if the body of the function is
also given.
Whenever the definition of a class member appears outside of the class
declaration, the member name must be qualified by the class name using the ::
(scope resolution) operator.
Example of Defining a Member outside of the Class
All member functions are in class scope even if they are defined outside their
class declaration.
The name of a class member is local to its class. Unless you use one of the
class access operators, . (dot), or -> (arrow), or :: (scope resolution)
operator, you can only use a class member in a member function of its class and
in nested classes. You can only use types, enumerations and static members in a
nested class without qualification with the :: operator.
The order of search for a name in a member function body is:
1. Within the member function body itself
2. Within all the enclosing classes, including inherited members of those
classes
3. Within the lexical scope of the body declaration
Example of Member Function Search Path
Note: When the containing classes are being searched, only the definitions of
the containing classes and their base classes are searched. The scope
containing the base class definitions (global scope, in this example) is not
searched.
Related Information
Scope in C++
Scope Resolution Operator ::
Member Functions
Dot Operator .
Arrow Operator ->
Member Access
ΓòÉΓòÉΓòÉ <hidden> Example of Defining a Member ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example defines a member function outside of its class
declaration.
*
************************************************************************/
// This example illustrates member scope.
#include <iostream.h>
class X
{
public:
int a, b ; // public data members
int add(); // member function declaration only
};
int a = 10; // global variable
// define member function outside its class declaration
int X::add() {return a + b;};
// .
// .
// .
void main()
{
int answer;
X xobject;
xobject.a = 1;
xobject.b = 2;
answer = xobject.add();
cout << xobject.a << " + " << xobject.b << " = " << answer;
}
/************************************************************************
*
The output for this example is: 1 + 2 = 3
Note: All member functions are in class scope even if they are defined outside
their class declaration. In the above example, the member function add()
returns the data member a, not the global variable a.
*
************************************************************************/
ΓòÉΓòÉΓòÉ <hidden> Example of Member Function Search Path ΓòÉΓòÉΓòÉ
/************************************************************************
*
The search of the enclosing classes, including inherited members, is
demonstrated in the following example:
*
************************************************************************/
class A { /* ... */ };
class B { /* ... */ };
class C { /* ... */ };
class Z : A {
class Y : B {
class X : C { int f(); /* ... */ };
};
};
int Z::Y::X f()
{
// ...
j();
// ...
}
/************************************************************************
*
In this example, the search for the name j in the definition of the function f
follows this order:
1. In the body of the function f
2. In X and in its base class C
3. In Y and in its base class B
4. In Z and in its base class A
5. In the lexical scope of the body of f. In this case, this is global
scope.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 11.6. Pointers to Members ΓòÉΓòÉΓòÉ
Pointers to members allow you to refer to nonstatic members of class objects.
You cannot use a pointer to member to point to a static class member because
the address of a static member is not associated with any particular object. To
point to a static class member, you must use a normal pointer.
You can use pointers to member functions in the same manner as pointers to
functions. You can compare pointers to member functions, assign values to them,
and use them to call member functions. Note that a member function does not
have the same type as a nonmember function that has the same number and type of
arguments and the same return type.
Example of Pointers to Members
To reduce complex syntax, you can declare a typedef to be a pointer to a
member. A pointer to a member can be declared and used as shown in the
following code fragment:
typedef void (X::*ptfptr) (int); // declare typedef
void main ()
{
// ...
ptfptr ptf = &X::f; // use typedef
X xobject;
(xobject.*ptf) (20); // call function
}
The pointer to member operators .* and ->* are used to bind a pointer to a
member of a specific class object. Because the precedence of () (function call
operator) is higher than .* and ->*, you must use parentheses to call the
function pointed to by ptf.
Related Information
Pointer to Member Operators .* ->*
Pointers
Static Members
The this Pointer
ΓòÉΓòÉΓòÉ <hidden> Example of Pointers to Members ΓòÉΓòÉΓòÉ
/************************************************************************
*
Pointers to members can be declared and used as shown in the following example:
*
************************************************************************/
// This example illustrates pointers to members.
#include <iostream.h>
class X
{
public:
int a;
void f(int b)
{
cout << "The value of b is "<< b << endl;
}
};
// .
// .
// .
void main ()
{
// declare pointer to data member
int X::*ptiptr = &X::a;
// declare a pointer to member function
void (X::* ptfptr) (int) = &X::f;
X xobject; // create an object of class type X
xobject.*ptiptr = 10; // initialize data member
cout << "The value of a is " << xobject.*ptiptr << endl;
(xobject.*ptfptr) (20); // call member function
}
/************************************************************************
*
The output for this example is:
The value of a is 10
The value of b is 20
*
************************************************************************/
ΓòÉΓòÉΓòÉ 11.7. The this Pointer ΓòÉΓòÉΓòÉ
The keyword this identifies a special type of pointer. When a nonstatic member
function is called, the this pointer identifies the class object which the
member function is operating on. You cannot declare the this pointer or make
assignments to it.
The type of the this pointer for a member function of a class type X, is X*
const. If the member function is declared with the constant qualifier, the type
of the this pointer for that member function for class X, is const X* const. If
the member function is declared with the volatile qualifier, the type of the
this pointer for that member function for class X is volatile X* const.
this is passed as a hidden argument to all nonstatic member function calls and
is available as a local variable within the body of all nonstatic functions.
For example, you can refer to the particular class object that a member
function is called for by using the this pointer in the body of the member
function. The following code example produces the output a = 5:
// This example illustrates the this pointer
#include <iostream.h>
class X
{
int a;
public:
// The 'this' pointer is used to retrieve 'xobj.a' hidden by
// the automatic variable 'a'
void Set_a(int a) { this->a = a; }
void Print_a() { cout << "a = " << a << endl; }
};
void main()
{
X xobj;
int a = 5;
xobj.Set_a(a);
xobj.Print_a();
}
Unless a class member name is hidden, using the class member name is equivalent
to using the class member name qualified with the this pointer.
Example of the this Pointer
Related Information
Pointers
Member Functions
Pointers to Members
C++ Classes
ΓòÉΓòÉΓòÉ <hidden> Example of the this Pointer ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows code using class members without the this pointer.
The comments on each line show the equivalent code with the hidden use of the
this pointer.
*
************************************************************************/
// This example uses class members without the this pointer.
#include <string.h>
#include <iostream.h>
class X
{
int len;
char *ptr;
public:
int GetLen() // int GetLen (X* const this)
{ return len; } // { return this->len; }
char * GetPtr() // char * GetPtr (X* const this)
{ return ptr; } // { return this->ptr; }
X& Set(char *);
X& Cat(char *);
X& Copy(X&);
void Print();
};
X& X::Set(char *pc) // X& X::Set(X* const this, char *pc)
{
len = strlen(pc); // this->len = strlen(pc);
ptr = new char[len]; // this->ptr =
// new char[this->len];
strcpy(ptr, pc); // strcpy(this->ptr, pc);
return *this;
}
X& X::Cat(char *pc) // X& X::Cat(X* const this, char *pc)
{
len += strlen(pc); // this->len += strlen(pc);
strcat(ptr,pc); // strcat(this->ptr,pc);
return *this;
}
X& X::Copy(X& x) // X& X::Copy(X* const this, X& x)
{
Set(x.GetPtr()); // this->Set(x.GetPtr(&x));
return *this;
}
void X::Print() // void X::Print(X* const this)
{
cout << ptr << endl; // cout << this->ptr << endl;
}
void main()
{
X xobj1;
xobj1.Set("abcd").Cat("efgh");
// xobj1.Set(&xobj1, "abcd").Cat(&xobj1, "efgh");
xobj1.Print(); // xobj1.Print(&xobj1);
X xobj2;
xobj2.Copy(xobj1).Cat("ijkl");
// xobj2.Copy(&xobj2, xobj1).Cat(&xobj2, "ijkl");
xobj2.Print(); // xobj2.Print(&xobj2);
}
/************************************************************************
*
This example produces the following output:
abcdefgh
abcdefghijkl
*
************************************************************************/
ΓòÉΓòÉΓòÉ 11.8. Static Members ΓòÉΓòÉΓòÉ
Class members can be declared using the storage-class specifier static in the
class member list. Only one copy of the static member is shared by all objects
of a class in a program. When you declare an object of a class having a static
member, the static member is not part of the class object.
A typical use of static members is for recording data common to all objects of
a class. For example, you can use a static data member as a counter to store
the number of objects of a particular class type that are created. Each time a
new object is created, this static data member can be incremented to keep track
of the total number of objects.
The declaration of a static member in the member list of a class is not a
definition. The definition of a static member is equivalent to an external
variable definition. You must define the static member outside of the class
declaration.
For example:
class X
{
public:
static int i;
}
int X::i = 0; // definition outside class declaration
// .
// .
// .
A static member can be accessed from outside of its class only if it is
declared with the keyword public. You can then access the static member by
qualifying the class name using the :: (scope resolution) operator. In the
following example:
class X
{
public:
static int f();
};
void main ()
{
X::f();
}
you can refer to the static member f() of class type X as X::f().
Related Information
static Storage Class Specifier
Using the Class Access Operators with Static Members
Static Data Members
Static Member Functions
ΓòÉΓòÉΓòÉ 11.8.1. Using the Class Access Operators with Static Members ΓòÉΓòÉΓòÉ
You can also access a static member from a class object by using the class
access operators . (dot) and -> (arrow). When a static member is accessed
through a class access operator, the expression on the left of the . or ->
operator is not evaluated.
Example of Accessing Static Members
A static member can be referred to independently of any association with a
class object because there is only one static member shared by all objects of a
class. A static member can exist even if no objects of its class have been
declared.
When you access a static member, the expression that you use to access it is
not evaluated. In the following example, the external function f() returns
class type X. The function f() can be used to access the static member i of
class X. The function f() itself is not called.
// This example shows that the expression used to
// access a static member is not evaluated.
class X
{
public:
static int i;
};
int X::i = 10;
X f() { /* ... */ }
void main ()
{
int a;
a = f().i; // f().i does not call f()
}
Related Information
Static Members
Dot Operator .
Arrow Operator ->
Static Data Members
Static Member Functions
ΓòÉΓòÉΓòÉ <hidden> Example of Accessing Static Members ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example uses the class access operators to access static members.
*
************************************************************************/
// This example illustrates access to static
// members with class access operators.
#include <iostream.h>
class X
{
static int cnt;
public:
// The following routines all set X's static variable cnt
// and print its value.
void Set_Show (int i)
{ X::cnt = i;
cout << "X::cnt = " << X::cnt << endl; }
void Set_Show (int i, int j )
{ this->cnt = i+j;
cout << "X::cnt = " << X::cnt << endl; }
void Set_Show (X& x, int i)
{ x.cnt = i;
cout << "X::cnt = " << X::cnt << endl; }
};
int X::cnt;
void main()
{
X xobj1, xobj2;
xobj1.Set_Show(11);
xobj1.Set_Show(11,22);
xobj1.Set_Show(xobj2, 44);
}
/************************************************************************
*
The above example produces the following output:
X::cnt = 11
X::cnt = 33
X::cnt = 44
*
************************************************************************/
ΓòÉΓòÉΓòÉ 11.8.2. Static Data Members ΓòÉΓòÉΓòÉ
Static data members of global classes have external linkage and can be
initialized in file scope like other global objects. Static data members follow
the usual class access rules, except that they can be initialized in file
scope. Static data members and their initializers can access other static
private and protected members of their class. The initializer for a static data
member is in the scope of the class declaring the member.
The following example shows how you can initialize static members using other
static members, even though these members are private:
class C {
static int i;
static int j;
static int k;
static int l;
static int m;
static int n;
static int p;
static int q;
static int r;
static int s;
static int f() { return 0; }
int a;
public:
C() { a = 0; }
};
C c;
int C::i = C::f(); // initialize with static member function
int C::j = C::i; // initialize with another static data member
int C::k = c.f(); // initialize with member function from an object
int C::l = c.j; // initialize with data member from an object
int C::s = c.a; // initialize with nonstatic data member
int C::r = 1; // initialize with a constant value
class Y : private C {} y;
int C::m = Y::f();
int C::n = Y::r;
int C::p = y.r; // error
int C::q = y.f(); // error
The initializations of C::p and C::x cause errors because y is an object of a
class that is derived privately from C, and its members are not accessible to
members of C.
You can only have one definition of a static member in a program. If a static
data member is not initialized, it is assigned a zero default value. Local
classes cannot have static data members.
Example of Static Data Members
Related Information
Static Member Functions
Static Members
Using the Class Access Operators with Static Members
External Linkage
Member Access
Local Classes
ΓòÉΓòÉΓòÉ <hidden> Example of Static Data Members ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows the declaration, initialization, use, and scope of
the static data member si and static member functions Set_si(int) and
Print_si().
*
************************************************************************/
// This example shows the declaration, initialization,
// use, and scope of a static data member.
#include <iostream.h>
class X
{
int i;
static int si;
public:
void Set_i(int i) { this->i = i; }
void Print_i() { cout << "i = " << i << endl; }
// Equivalent to:
// void Print_i(X* this)
// { cout << "X::i = " << this->i << endl; }
static void Set_si(int si) { X::si = si; }
static void Print_si()
{
cout << "X::si = " << X::si << endl;
}
// Print_si doesn't have a 'this' pointer
};
int X::si = 77; // Initialize static data member
void main()
{
X xobj;
// Non-static data members and functions belong to specific
// instances (here xobj) of class X
xobj.Set_i(11);
xobj.Print_i();
// static data members and functions belong to the class and
// can be accessed without using an instance of class X
X::Print_si();
X::Set_si(22);
X::Print_si();
}
/************************************************************************
*
This example produces the following output:
i = 11
X::si = 77
X::si = 22
*
************************************************************************/
ΓòÉΓòÉΓòÉ 11.8.3. Static Member Functions ΓòÉΓòÉΓòÉ
You cannot have static and nonstatic member functions with the same names and
the same number and type of arguments.
A static member function does not have a this pointer. You can call a static
member function using the this pointer of a nonstatic member function. In the
following example, the nonstatic member function printall() calls the static
member function f() using the this pointer:
// This example illustrates a static member function f().
#include <iostream.h>
class c {
static void f() { cout << "Here is i"
<< i << endl;}
static int i;
int j;
public:
c(int firstj): j(firstj) {}
void printall();
};
void c::printall() {
cout << "Here is j " << this->j << endl;
this->f();
}
int c::i = 3;
void main() {
class c C(0);
C.printall();
}
A static member function cannot be declared with the keyword virtual.
A static member function can access only the names of static members,
enumerators, and nested types of the class in which it is declared.
Related Information
Member Functions
Using the Class Access Operators with Static Members
Static Data Members
Static Members
The this Pointer
Virtual Functions
ΓòÉΓòÉΓòÉ 11.9. Member Access ΓòÉΓòÉΓòÉ
Member access determines if a class member is accessible in an expression or
declaration. Note that accessibility and visibility are independent. Visibility
is based on the scoping rules of C++. A class member can be visible and
inaccessible at the same time. This section describes how you control the
access to the individual nonderived class members by using access specifiers
when you declare class members in a member list.
Related Information
Access Declarations
Inherited Member Access
Class Member Lists
Classes and Access Control
Access Specifiers
C++ Classes
ΓòÉΓòÉΓòÉ 11.9.1. Classes and Access Control ΓòÉΓòÉΓòÉ
C++ facilitates data abstraction and encapsulation by providing access control
for members of class types.
For example, if you declare private data members and public member functions, a
client program can only access the private members through the public member
functions and friends of that class. Such a class would have data hiding
because client programs do not have access to implementation details and are
forced to use a public interface to manipulate objects of the class.
You can control access to class members by using access specifiers. In the
following example, the class abc has three private data members a, b, and c,
and three public member functions add(), mult(), and the constructor abc(). The
main() function creates an object danforth of the abc class and then attempts
to print the value of the member a for this object:
// This example illustrates class member access specifiers
#include <iostream.h>
class abc
{
private:
int a, b, c;
public:
abc(int p1, int p2, int p3): a(p1), b(p2), c(p3) {}
int add() { return a + b + c ; }
int mult() { return a * b * c; }
};
void main() {
abc danforth(1,2,3);
cout << "Here is the value of a " << danforth.a << endl;
// This causes an error because a is not
// a public member and cannot be accessed
// directly
}
Because class members are private by default, you can omit the keyword private
in the definition of abc. Because a is not a public member, the attempt to
access its value directly causes an error.
Related Information
Access Declarations
Inherited Member Access
Class Member Lists
Member Access
Access Specifiers
C++ Classes
ΓòÉΓòÉΓòÉ 11.9.2. Access Specifiers ΓòÉΓòÉΓòÉ
The three class member access specifiers have the following effect:
public class members
can be accessed by any function, file or class.
private class members
can be accessed only by member functions and friends of the class in
which the member is declared.
protected class members
can be accessed only by member functions and friends of the class in
which they are declared and by member functions and friends of
classes derived with public or protected access from the class in
which the protected members are declared. The access specifier
protected can be used for nonbase class members, but it is
equivalent to private unless it is used in a base class member
declaration or in a base list.
The default access for an individual class member depends on the class key
used in the class declaration. Members of classes declared with the keyword
class are private by default. Members of classes declared with the keyword
struct or union are public by default.
The access specifier protected is meaningful only in the context of
derivation. You can control the access to inherited members (that is, base
class members) by including access specifiers in the base list of the derived
class declaration. You can also restore the access to an inherited member from
a derived class by using an access declaration.
Access for inherited members is described in Inherited Member Access.
Member lists can include access specifiers as labels. Members declared after
these labels have access as specified by the label they follow. An access
specifier determines the access for members until another access specifier is
used or until the end of the class declaration. You can use any number of
access specifiers in any order.
Examples of Access Specifiers
Related Information
Access Declarations
Inherited Member Access
Class Member Lists
Member Access
Classes and Access Control
C++ Classes
ΓòÉΓòÉΓòÉ <hidden> Examples of Access Specifiers ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows access specifiers in member lists.
*
************************************************************************/
class X
{
int a; // private data by default
public:
void f(int); // public function
int b; // public data
private:
int c; // private data
protected:
void g(int); // protected function
};
struct Y
{
int a; // public data by default
public:
int b; // public data
private:
void g(int); // private function
int c; // private data
};
ΓòÉΓòÉΓòÉ 11.10. Friends ΓòÉΓòÉΓòÉ
A friend of a class X is a function or class that is granted the same access to
X as the members of X. Functions declared with the friend specifier in a class
member list are called friend functions of that class. Classes declared with
the friend specifier in the member list of another class are called friend
classes of that class.
A class Y must be defined before any member of Y can be declared a friend of
another class.
You can declare an entire class as a friend.
If the class has not been previously declared, use an elaborated type specifier
and a qualified type specifier to specify the class name.
If the friend class has been previously declared, you can omit the keyword
class, as shown in the following example:
class F;
class X
{
public:
X() {a=1; b=2;}
private:
int a, b;
friend F; // elaborated-type-specifier not required
};
Examples of Friends
Related Information
Friend Scope
Friend Access
C++ Classes
Member Functions
ΓòÉΓòÉΓòÉ <hidden> Example of a friend Function ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, the friend function print is a member of class Y and
accesses the private data members a and b of class X.
*
************************************************************************/
// This example illustrates a friend function.
#include <iostream.h>
class X;
class Y
{
public:
void print(X& x);
};
class X
{
public:
X() {a=1; b=2;}
private:
int a, b;
friend void Y::print(X& x);
};
void Y::print(X& x)
{
cout << "A is "<< x.a << endl;
cout << "B is " << x.b << endl;
}
void main ()
{
X xobj;
Y yobj;
yobj.print(xobj);
}
/************************************************************************
*
In the following example, the friend class F has a member function print that
accesses the private data members a and b of class X and performs the same task
as the friend function print in the above example. Any other members declared
in class F also have access to all members of class X. In the example, the
friend class F has not been previously declared, so an elaborated type
specifier and a qualified type specifier are used to specify the class name.
*
************************************************************************/
// This example illustrates a friend class.
#include <iostream.h>
class X
{
public:
X() {a=1; b=2;} // constructor
private:
int a, b;
friend class F; // friend class
};
class F
{
public:
void print(X& x)
{
cout << "A is " << x.a << endl;
cout << "B is " << x.b << endl;
}
};
void main ()
{
X xobj;
F fobj;
fobj.print(xobj);
}
/************************************************************************
*
Both the above examples produce the following output:
A is 1
B is 2
*
************************************************************************/
ΓòÉΓòÉΓòÉ 11.10.1. Friend Scope ΓòÉΓòÉΓòÉ
The name of a friend function or class first introduced in a friend declaration
is not in the scope of the class granting friendship (also called the enclosing
class) and is not a member of the class granting friendship.
The name of a function first introduced in a friend declaration is in the scope
of the first nonclass scope that contains the enclosing class. The body of a
function provided in a friend declaration is handled in the same way as a
member function defined within a class. Processing of the definition does not
start until the end of the outermost enclosing class. In addition, unqualified
names in the body of the function definition are searched for starting from the
class containing the function definition.
A class that is first declared in a friend declaration is equivalent to an
extern declaration. For example:
class B {};
class A
{
friend class B; // global class B is a friend of A
};
If the name of a friend class has been introduced before the friend
declaration, the compiler searches for a class name that matches the name of
the friend class beginning at the scope of the friend declaration. If the
declaration of a nested class is followed by the declaration of a friend class
with the same name, the nested class is a friend of the enclosing class.
The scope of a friend class name is the first nonclass enclosing scope. For
example:
class A {
class B { // arbitrary nested class definitions
friend class C;
};
};
is equivalent to:
class C;
class A {
class B { // arbitrary nested class definitions
friend class C;
};
};
If the friend function is a member of another class, you need to use the class
member access operators. For example:
class A
{
public:
int f() { /* ... */ }
};
class B
{
friend int A::f();
};
Friends of a base class are not inherited by any classes derived from that base
class.
Related Information
Scope of Class Names
Friend Access
Friends
Nested Classes
Derivation
ΓòÉΓòÉΓòÉ 11.10.2. Friend Access ΓòÉΓòÉΓòÉ
A friend of a class can access the private and protected members of that class.
Normally, you can only access the private members of a class through member
functions of that class, and you can only access the protected members of a
class through member functions of a class or classes derived from that class.
Friend declarations are not affected by access specifiers.
Related Information
Member Access
Friend Scope
Friends
ΓòÉΓòÉΓòÉ 12. C++ Overloading ΓòÉΓòÉΓòÉ
Overloading enables you to redefine functions and most standard C++ operators.
Typically, you overload a function or operator if you want to extend the
operations the function or operator performs to different data types.
This section discusses:
Overloading Functions
Argument Matching in Overloaded Functions
Overloading Operators
Overloading Unary Operators
Overloading Binary Operators
Special Overloaded Operators
Related Information
Expressions and Operators
Functions
C++ Classes
ΓòÉΓòÉΓòÉ 12.1. Overloading Functions ΓòÉΓòÉΓòÉ
You can overload a function by having multiple declarations of the same
function name in the same scope. The declarations differ in the type and number
of arguments in the argument list. When an overloaded function is called, the
correct function is selected by comparing the types of the actual arguments
with the types of the formal arguments.
Consider a function print, which displays an int. As shown in the following
example, you can overload the function print to display other types, for
example, double and char*. You can have three functions with the same name,
each performing a similar operation on a different data type.
// This example illustrates function overloading.
#include <iostream.h>
void print(int i) { cout << " Here is int " << i << endl; }
void print(double f) { cout << " Here is float "
<< f << endl; }
void print(char* c) { cout << " Here is char* " << c << endl; }
void main() {
print(10); // calls print(int)
print(10.10); // calls print(double)
print("ten"); // calls print(char*)
}
Two function declarations are identical if all of the following are true:
They have the same function name
They are declared in the same scope
They have identical argument lists
When you declare a function name more than once in the same scope, the second
declaration of the function name is interpreted by the compiler as follows:
If the return type, argument types, and number of arguments of the two
declarations are identical, the second declaration is considered a
declaration of the same function as the first.
If only the return types of the two function declarations differ, the
second declaration is an error.
If either the argument types or number of arguments of the two
declarations differ, the function is considered to be overloaded.
Restrictions on Overloaded Functions
Related Information
Functions
Function Declarations
Scope in C++
Argument Matching in Overloaded Functions
Member Functions
C++ Inheritance
ΓòÉΓòÉΓòÉ <hidden> Restrictions on Overloaded Functions ΓòÉΓòÉΓòÉ
Functions that differ only in return type cannot have the same name.
Two member functions that differ only in that one is declared with the
keyword static cannot have the same name.
A typedef is a synonym for another type, not a separate type. The
following two declarations of spadina() are declarations of the same
function:
typedef int I;
void spadina(float, int);
void spadina(float, I);
A member function of a derived class is not in the same scope as a member
function in a base class with the same name. A derived class member hides
a base class member with the same name.
Argument types that differ only in that one is a pointer * and the other
is an array [] are identical. The following two declarations are
equivalent:
f(char*);
f(char[10]);
ΓòÉΓòÉΓòÉ 12.2. Argument Matching in Overloaded Functions ΓòÉΓòÉΓòÉ
When an overloaded function or overloaded operator is called, the compiler
chooses the function declaration with the best match on all arguments from all
the function declarations that are visible. The compiler compares the actual
arguments of a function call with the formal arguments of all declarations of
the function that are visible. For a best match to occur, the compiler must be
able to distinguish a function that:
Has at least as good a match on all arguments as any other function with
the same name
Has at least one better argument match than any other function with the
same name
If no such function exists, the call is not allowed. A call to an overloaded
function has three possible outcomes. The compiler can find:
An exact match
No match
An ambiguous match
An ambiguous match occurs when the actual arguments of the function call match
more than one overloaded function.
Argument matching can include performing standard and user-defined conversions
on the arguments to match the actual arguments with the formal arguments. Only
a single user-defined conversion is performed in a sequence of conversions on
an actual argument. In addition, the best-matching sequence of standard
conversions is performed on an actual argument. The best-matching sequence is
the shortest sequence of conversions between two standard types. For example,
the conversion:
int -> float -> double
can be shortened to the best-matching conversion sequence:
int -> double
because the conversion from int to double is allowed.
Trivial Conversions do not affect the choice of conversion sequence.
Conversion sequences are described in Sequence of Argument Conversions.
Related Information
Sequence of Argument Conversions
Trivial Conversions
Overloading Functions
Overloading Operators
ΓòÉΓòÉΓòÉ 12.2.1. Sequence of Argument Conversions ΓòÉΓòÉΓòÉ
Argument-matching conversions occur in the following order:
1. An exact match in which the actual arguments match exactly (including a
match with one or more trivial conversions) with the type and number of
formal arguments of one declaration of the overloaded function
2. A match with promotions in which a match is found when one or more of the
actual arguments is promoted
3. A match with standard conversions in which a match is found when one or
more of the actual arguments is converted by a standard conversion
4. A match with user-defined conversions in which a match is found when one
or more of the actual arguments is converted by a user-defined conversion
5. A match with ellipses
Match through promotion follows the rules for Integral Promotions and Standard
Type Conversions.
You can override an exact match by using an explicit cast. In the following
example, the second call to f() matches with f(void*):
void f(int);
void f(void*);
void main()
{
f(0xaabb); // matches f(int);
f((void*) 0xaabb); // matches f(void*)
}
The implicit first argument for a nonstatic member function or operator is the
this pointer. It refers to the class object for which the member function is
called. When you overload a nonstatic member function, the first implicit
argument, the this pointer, is matched with the object or pointer used in the
call to the member function. User-defined conversions are not applied in this
type of argument matching for overloaded functions or operators.
When you call an overloaded member function of class X using the . (dot) or ->
(arrow) operator, the this pointer has type X* const. The type of the this
pointer for a constant object is const X* const. The type of the this pointer
for a volatile object is volatile X* const.
Related Information
Argument Matching in Overloaded Functions
Trivial Conversions
Implicit Type Conversions
User-Defined Conversions
The this Pointer
Overloading Functions
Overloading Operators
Dot Operator .
Arrow Operator ->
ΓòÉΓòÉΓòÉ 12.2.2. Trivial Conversions ΓòÉΓòÉΓòÉ
Functions cannot be distinguished if they have the same name and have arguments
that differ only in that one is declared as a reference to a type and the other
is that type. You cannot have two functions with the same name and with
arguments differing only in this respect. Because the following two
declarations cannot be distinguished, the second one causes an error:
double f(double i); // declaration
double f(double &i); // error
However, functions with the same name having arguments that differ only in that
one is a pointer or reference and the other is a pointer to const or const
reference can be distinguished. Functions with the same name having arguments
that differ only in that one is a pointer or reference and the other is a
pointer to volatile or volatile reference can also be distinguished. For the
purpose of finding a best match of arguments, functions that have a volatile or
const match (not requiring a trivial conversion) are better than those that
have a volatile or const mismatch.
Related Information
Argument Matching in Overloaded Functions
Sequence of Argument Conversions
Implicit Type Conversions
User-Defined Conversions
Overloading Functions
Overloading Operators
ΓòÉΓòÉΓòÉ 12.3. Overloading Operators ΓòÉΓòÉΓòÉ
You can overload one of the standard C++ operators by redefining it to perform
a particular operation when it is applied to an object of a particular class.
Overloaded operators must have at least one argument that has class type. An
overloaded operator is called an operator function and is declared with the
keyword operator preceding the operator. Overloaded operators are distinct from
overloaded functions, but, like overloaded functions, they are distinguished by
the number and types of operands used with the operator.
You can overload any of the following operators:
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> <<= >>= == !=
<= >= && || ++ -- , ->* ->
() [] new delete
where () is the function call operator and [] is the subscript operator.
Consider the standard + (plus) operator. When this operator is used with
operands of different standard types, the operators have slightly different
meanings. For example, the addition of two integers is not implemented in the
same way as the addition of two floating-point numbers. C++ allows you to
define your own meanings for the standard C++ operators when they are applied
to class types.
Example of an Overloaded Operator
You can overload both the unary and binary forms of:
+ - * &
When an overloaded operator is a member function, the first operand is matched
against the class type of the overloaded operator. The second operand, if one
exists, is matched against the argument in the overloaded operator call.
When an overloaded operator is a nonmember function, at least one operand must
have class or enumeration type. The first operand is matched against the first
argument in the overloaded operator call. The second operand, if one exists, is
matched against the second argument in the overloaded operator call.
The argument-matching conventions and rules described in Argument Matching in
Overloaded Functions apply to overloaded operators.
An overloaded operator must be either a member function, as shown in the
following example:
class X
{
public:
X operator!();
X& operator =(X&);
X operator+(X&);
};
X X::operator!() { /* ... */ }
X& X::operator=(X& x) { /* ... */ }
X X::operator+(X& x) { /* ... */ }
or take at least one argument of class, a reference to a class, an enumeration,
or a reference to an enumeration, as shown below:
class Y;
{
// .
};
class Z;
{
// .
};
Y operator!(Y& y);
Z operator+(Z& z, int);
Usually, overloaded operators are invoked using the normal operator syntax. You
can also call overloaded operators explicitly by qualifying the operator name.
Restrictions on Overloaded Operators
Related Information
Overloading Unary Operators
Overloading Binary Operators
Special Overloaded Operators
Argument Matching in Overloaded Functions
Overloading Functions
Expressions and Operators
ΓòÉΓòÉΓòÉ <hidden> Example of an Overloaded Operator ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, a class called complx is defined to model complex
numbers, and the + (plus) operator is redefined in this class to add two
complex numbers.
*
************************************************************************/
// This example illustrates overloading the plus (+) operator.
#include <iostream.h>
class complx
{
double real,
imag;
public:
complx( double real = 0., double imag = 0.); // constructor
complx operator+(const complx&) const; // operator+()
};
// define constructor
complx::complx( double r, double i )
{
real = r; imag = i;
}
// define overloaded + (plus) operator
complx complx::operator+ (const complx& c) const
{
complx result;
result.real = (this->real + c.real);
result.imag = (this->imag + c.imag);
return result;
}
void main()
{
complx x(4,4);
complx y(6,6);
complx z = x + y; // calls complx::operator+()
}
/************************************************************************
*
For the class complx, described above, you can call the overloaded + (plus)
operator either implicitly or explicitly as shown below.
*
************************************************************************/
// This example shows implicit and explicit calls
// to an overloaded plus (+) operator.
class complx
{
double real,
imag;
public:
complx( double real = 0., double imag = 0.);
complx operator+(const complx&) const;
};
// .
// .
// .
void main()
{
complx x(4,4);
complx y(6,6);
complx u = x.operator+(y); // explicit call
complx z = x + y; // implicit call to complx::operator+()
}
ΓòÉΓòÉΓòÉ 12.3.1. Restrictions on Overloaded Operators ΓòÉΓòÉΓòÉ
The following C++ operators cannot be overloaded:
. .* :: ?:
You cannot overload the preprocessing symbols # and ##.
You cannot change the precedence, grouping, or number of operands of the
standard C++ operators.
An overloaded operator (except for the function call operator) cannot
have default arguments or an ellipsis in the argument list.
You must declare the overloaded =, [], () and -> operators as nonstatic
member functions to ensure that they receive lvalues as their first
operands.
The operators new and delete do not follow the general rules described in
this section. Overloading new and delete is described in Overloaded new
and delete.
All operators except the = operator are inherited. Copy by Assignment
describes the behavior of the assignment operator.
Unless they are explicitly mentioned in Special Overloaded Operators,
overloaded unary and binary operators follow the rules outlined in
Overloading Unary Operators and Overloading Binary Operators.
For more information on standard C and C++ operators, see Expressions and
Operators.
ΓòÉΓòÉΓòÉ 12.3.2. Overloading Unary Operators ΓòÉΓòÉΓòÉ
You can overload a prefix unary operator by declaring a nonmember function
taking one argument or a nonstatic member function taking no arguments.
When you prefix a class object with an overloaded unary operator, for example:
class X
{
// ...
};
void main ()
{
X x;
!x; // overloaded unary operator
}
the operator function call !x can be interpreted as: x.operator!() or
operator!(x) . depending on the declarations of the operator function. If both
forms of the operator function have been declared, argument matching determines
which interpretation is used.
Related Information
Overloading Binary Operators
Overloading Operators
Special Overloaded Operators
Restrictions on Overloaded Operators
Unary Expressions
ΓòÉΓòÉΓòÉ 12.3.3. Overloading Binary Operators ΓòÉΓòÉΓòÉ
You can overload a binary operator by declaring a nonmember function taking two
arguments or a nonstatic member function taking one argument.
When you use a class object with an overloaded binary operator, for example:
class X
{
// ...
};
void main ()
{
X x;
int y=10;
x*y; // overloaded binary operator
}
the operator function call x*y can be interpreted as: x.operator*(y) or
operator*(x,y) depending on the declarations of the operator function. If both
forms of the operator function have been declared, argument matching determines
which interpretation is used.
Related Information
Overloading Unary Operators
Overloading Operators
Special Overloaded Operators
Restrictions on Overloaded Operators
Binary Expressions
ΓòÉΓòÉΓòÉ 12.4. Special Overloaded Operators ΓòÉΓòÉΓòÉ
The following overloaded operators do not fully follow the rules for unary or
binary overloaded operators:
Assignment
Function Call
Subscripting
Class Member Access
Increment and Decrement
new and delete
Related Information
Overloading Unary Operators
Overloading Binary Operators
Overloading Operators
Restrictions on Overloaded Operators
Member Functions
ΓòÉΓòÉΓòÉ 12.4.1. Overloaded Assignment ΓòÉΓòÉΓòÉ
You can only overload an assignment operator by declaring a nonstatic member
function. The following example shows how you can overload the assignment
operator for a particular class:
class X
{
public:
X();
X& operator=(X&);
X& operator=(int);
};
X& X::operator=(X& x) { /* ... */ }
X& X::operator=(int i) { /* ... */ }
void main()
{
X x1, x2;
x1 = x2; // call x1.operator=(x2)
x1 = 5; // call x1.operator=(5)
}
You cannot declare an overloaded assignment operator that is a nonmember
function. Overloaded assignment operators are not inherited.
If a copy assignment operator function is not defined for a class, the copy
assignment operator function is defined by default as a memberwise assignment
of the class members. If assignment operator functions exist for base classes
or class members, these operators are used when the compiler generates default
copy assignment operators. See Copy by Assignment for more information.
For more information on standard assignment operators, see Assignment
Expressions.
ΓòÉΓòÉΓòÉ 12.4.2. Overloaded Function Calls ΓòÉΓòÉΓòÉ
The operands are function_name and an optional expression_list. The operator
function operator() must be defined as a nonstatic member function. You cannot
declare an overloaded function call operator that is a nonmember function.
If you make the following call for the class object x:
x (arg1, arg2, arg3)
it is interpreted as
x.operator()(arg1, arg2, arg3)
Unlike all other overloaded operators, you can provide default arguments and
ellipses in the argument list for the function call operator. For example:
class X
{
public:
X& operator() (int = 5);
};
For more information on the standard function call operator, see Function
Calls ( ).
ΓòÉΓòÉΓòÉ 12.4.3. Overloaded Subscripting ΓòÉΓòÉΓòÉ
An expression containing the subscripting operator has syntax of the form:
identifier [ expression ]
and is considered a binary operator. The operands are identifier and
expression. The operator function operator[] must be defined as a nonstatic
member function. You cannot declare an overloaded subscript operator that is a
nonmember function.
A subscripting expression for the class object x:
x [y]
is interpreted as x.operator[](y). It is not interpreted as operator[](x,y)
because it is defined as a nonstatic member function.
For more information on the standard subscripting operator, see Array
Subscript[ ].
ΓòÉΓòÉΓòÉ 12.4.4. Overloaded Class Member Access ΓòÉΓòÉΓòÉ
An expression containing the class member access -> (arrow) operator has syntax
of the form:
identifier -> name-expression
and is considered a unary operator. The operator function operator->() must be
defined as a nonstatic member function.
The following restrictions apply to class member access operators:
You cannot declare an overloaded arrow operator that is a nonmember
function.
You cannot overload the class member access . (dot) operator.
Consider the following example of overloading the -> (arrow) operator:
class Y
{
public:
void f();
};
class X
{
public:
Y* operator->();
};
X x;
x->f();
Here x->f() is interpreted as: ( x.operator->() )-> f() .
x.operator->() must return either a reference to a class object or a class
object for which the overloaded operator-> function is defined or a pointer to
any class. If the overloaded operator-> function returns a class type, the
class type must not be the same as the class declaring the function, and the
class type returned must contain its own definition of an overloaded ->
operator function.
For more information on the standard class member access arrow operator, see
Arrow Operator ->.
ΓòÉΓòÉΓòÉ 12.4.5. Overloaded Increment and Decrement ΓòÉΓòÉΓòÉ
The prefix increment operator ++ can be overloaded for a class type by
declaring a nonmember function operator with one argument of class type or a
reference to class type, or by declaring a member function operator with no
arguments.
In the following example, the increment operator is overloaded in both ways:
// This example illustrates an overloaded prefix increment operator.
class X
{
int a;
public:
operator++(); // member prefix increment operator
};
class Y { /* ... */ };
operator++(Y& y); // nonmember prefix increment operator
// Definitions of prefix increment operator functions
// ...
void main()
{
X x;
Y y;
++x; // x.operator++
x.operator++(); // x.operator++
operator++(y); // nonmember operator++
++y; // nonmember operator++
}
The postfix increment operator ++ can be overloaded for a class type by
declaring a nonmember function operator operator++() with two arguments, the
first having class type and the second having type int. Alternatively, you can
declare a member function operator operator++() with one argument having type
int. The compiler uses the int argument to distinguish between the prefix and
postfix increment operators. For implicit calls, the default value is zero.
For example:
// This example illustrates an overloaded postfix increment operator.
class X
{
int a;
public:
operator++(int); // member postfix increment operator
};
operator++(X x, int i); // nonmember postfix increment operator
// Definitions of postfix increment operator functions
// ...
void main()
{
X x;
x++; // x.operator++
// default zero is supplied by compiler
x.operator++(0); // x.operator++
operator++(x,0); // nonmember operator++
}
The prefix and postfix decrement operators follow the same rules as their
increment counterparts.
For more information on the standard postfix and prefix increment operators,
see Increment ++. For more information on the standard postfix and prefix
decrement operators, see Decrement - -.
ΓòÉΓòÉΓòÉ 12.4.6. Overloaded new and delete ΓòÉΓòÉΓòÉ
You can implement your own memory management scheme for a class by overloading
the operators new and delete. The overloaded operator new must return a void*,
and its first argument must have type size_t. The overloaded operator delete
must return a void type, and its first argument must be void*. The second
argument for the overloaded delete operator is optional and, if present, it
must have type size_t. You can only define one delete operator function for a
class.
Type size_t is an implementation dependent unsigned integral type defined in
<stddef.h>. For more information about size_t, see the IBM VisualAge C++ for
OS/2 User's Guide and Reference.
The size argument is required because a class can inherit an overloaded new
operator. The derived class can be a different size than the base class. The
size argument ensures that the correct amount of storage space is allocated or
deallocated for the object.
When new and delete are overloaded within a class declaration, they are static
member functions whether they are declared with the keyword static or not. They
cannot be virtual functions.
You can access the standard, nonoverloaded versions of new and delete within a
class scope containing the overloading new and delete operators by using the ::
(scope resolution) operator to provide global access.
For more information on the class member operators new and delete, see Free
Store. For more information on the standard new and delete operators, see new
Operator and delete Operator.
ΓòÉΓòÉΓòÉ 13. Special C++ Member Functions ΓòÉΓòÉΓòÉ
This chapter introduces the special member functions that are used to create,
destroy, convert, initialize, and copy class objects. This chapter discusses:
Constructors
Destructors
Free Store
Temporary Objects
User-Defined Conversions
Initialization by Constructor
Copying Class Objects
Related Information
Functions
C++ Classes
C++ Class Members and Friends
C++ Overloading
C++ Inheritance
ΓòÉΓòÉΓòÉ 13.1. Constructors and Destructors Overview ΓòÉΓòÉΓòÉ
Because classes have complicated internal structures, including data and
functions, object initialization and cleanup for classes is much more
complicated than it is for simple data structures. Constructors and destructors
are special member functions of classes that are used to construct and destroy
class objects. Construction may involve memory allocation and initialization
for objects. Destruction may involve cleanup and deallocation of memory for
objects.
Like other member functions, constructors and destructors are declared within a
class declaration. They can be defined inline or external to the class
declaration. Constructors can have default arguments. Unlike other member
functions, constructors can have member initialization lists. The following
restrictions apply to constructors and destructors:
Constructors and destructors do not have return types nor can they return
values.
References and pointers cannot be used on constructors and destructors
because their addresses cannot be taken.
Constructors cannot be declared with the keyword virtual.
Constructors and destructors cannot be declared static, const, or
volatile.
Unions cannot contain class objects that have constructors or
destructors.
Constructors and destructors obey the same access rules as member functions.
For example, if a constructor is declared with the keyword protected, only
derived classes and friends can use it to create class objects. Class member
access is described in Member Access.
The compiler automatically calls constructors when defining class objects and
calls destructors when class objects go out of scope. A constructor does not
allocate memory for the class object its this pointer refers to, but may
allocate storage for more objects that its class object refers to. If memory
allocation is required for objects, constructors can explicitly call the new
operator. During cleanup, a destructor may release objects allocated by the
corresponding constructor. To release objects, use the delete operator.
Derived classes do not inherit constructors or destructors from their base
classes, but they do call the constructor and destructor of base classes.
Destructors can be declared with the keyword virtual.
Constructors are also called when local or temporary class objects are
created, and destructors are called when local or temporary objects go out of
scope.
You can call member functions from constructors or destructors. You can call a
virtual function, either directly or indirectly, from a constructor or
destructor. In this case, the function called is the one defined in the class
or base class containing the constructor (or destructor), but not a function
defined in any class derived from the class being constructed. This avoids the
possibility of accessing an unconstructed object from a constructor or
destructor.
Related Information
Constructors
Destructors
Virtual Functions
Member Access
new Operator
delete Operator
ΓòÉΓòÉΓòÉ 13.1.1. Constructors ΓòÉΓòÉΓòÉ
A constructor is a member function with the same name as its class. For
example:
class X
{
public:
X(); // constructor for class X
};
Constructors are used to create, and can initialize, objects of their class
type. Initialization of class objects using constructors is described in
Initialization by Constructor.
A default constructor is a constructor that either has no arguments, or, if it
has arguments, all the arguments have default values. If no user-defined
constructor exists for a class and one is needed, the compiler creates a
default constructor, with public access, for that class. No default constructor
is created for a class that has any constant or reference type members.
Like all functions, a constructor can have default arguments. They are used to
initialize member objects. If default values are supplied, the trailing
arguments can be omitted in the expression list of the constructor. Note that
if a constructor has any arguments that do not have default values, it is not a
default constructor.
A copy constructor is used to make a copy of one class object from another
class object of the same class type. A copy constructor is called with a single
argument that is a reference to its own class type. You cannot use a copy
constructor with an argument of the same type as its class; you must use a
reference. You can provide copy constructors with additional default arguments.
If a user-defined copy constructor does not exist for a class and one is
needed, the compiler creates a copy constructor, with public access, for that
class. It is not created for a class if any of its members or base classes have
an inaccessible copy constructor.
If a class has a base class or members with constructors when it is
constructed, the constructor for the base class is called, followed by any
constructors for members. The constructor for the derived class is called last.
Virtual base classes are constructed before nonvirtual base classes. When more
than one base class exists, the base class constructors are called in the order
that their classes appear in the base list. For more information, see
Construction Order of Derived Class Objects.
Examples of Constructors and Construction Order
You cannot call constructors directly. You use a function style cast to
explicitly construct an object of the specified type.
Example of Explicitly Constructing an Object
Related Information
Destructors
Constructors and Destructors Overview
Construction Order of Derived Class Objects
Default Arguments in C++ Functions
References
ΓòÉΓòÉΓòÉ <hidden> Example of Explicitly Constructing an Object ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, a constructor is used as an initializer to create a
named object.
*
************************************************************************/
#include <iostream.h>
class X
{
public:
X (int, int , int = 0); // constructor with default argument
private:
int a, b, c;
int f();
};
X::X (int i, int j, int k) { a = i; b = j; c = k; }
void main ()
{
X xobject = X(1,2,3); // explicitly create and initialize
// named object with constructor call
}
ΓòÉΓòÉΓòÉ <hidden> Examples of Constructors and Construction Order ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following code fragment shows two classes with constructors, default
constructors, and copy constructors:
class X
{
public:
X(); // default constructor, no arguments
X(int, int , int = 0); // constructor
X(const X&); // copy constructor
X(X); // error, incorrect argument type
};
class Y
{
public:
Y( int = 0); // default constructor with one
// default argument
Y(const Y&, int = 0); // copy constructor
};
When more than one base class exists, the base class constructors are called in
the order that their classes appear in the base list, as shown in the following
example:
*
************************************************************************/
class B1 { public: B1(); };
class B2
{
public:
B2();
B1 b1obj;
};
class B3 { public: B3(); };
// ...
class D : public B1, public B2, public B3
{
public:
D();
~D();
};
void main ()
{
D object;
}
/************************************************************************
*
In the above example, the constructors for object are called in the following
order:
B1(); // first base constructor declared
B1(); // member constructor for B2::b1obj
B2(); // second base constructor declared
B3(); // last base constructor declared
D(); // derived constructor called last
Note that the construction of class D involves construction of the base classes
B1, B2, and B3. The construction of base class B2 involves the construction of
its class B1 member object. When class B2 is constructed, the constructor for
class B1 is called in addition to B2's own constructor.
As explained above, the second call to the constructor of B1 followed by the
call to the constructor of B2 is part of the construction of B2.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 13.1.2. Destructors ΓòÉΓòÉΓòÉ
A destructor is a member function with the same name as its class prefixed by a
~ (tilde).
For example:
class X
{
public:
X(); // constructor for class X
~X(); // destructor for class X
};
A destructor takes no arguments and has no return type. Its address cannot be
taken. Destructors cannot be declared const, volatile, or static. A destructor
can be declared virtual or pure virtual. A union cannot have as a member an
object of a class with a destructor.
Destructors are usually used to deallocate memory and do other cleanup for a
class object and its class members when the object is destroyed. A destructor
is called for a class object when that object passes out of scope or is
explicitly deleted.
Class members that are class types can have their own destructors. Both base
and derived classes can have destructors, although destructors are not
inherited. If a base class or a member of a base class has a destructor and a
class derived from that base class does not declare a destructor, a default
destructor is generated. The default destructor calls the destructors of the
base class and members of the derived class. Default destructors are generated
with default public access.
Destructors are called in the reverse order to constructors:
1. The destructor for a class object is called before destructors for
members and bases are called.
2. Destructors for nonstatic members are called before destructors for base
classes are called.
3. Destructors for nonvirtual base classes are called before destructors for
virtual base classes are called.
When an exception is thrown for a class object with a destructor, the
destructor for the temporary object thrown is not called until control passes
out of the catch block. For more information, see Constructors and Destructors
in Exception Handling.
Destructors are implicitly called when an automatic or temporary object passes
out of scope. They are implicitly called at program termination for
constructed external and static objects. Destructors are invoked when you use
the delete operator for objects created with the new operator.
Example of Destructors
You can use a destructor explicitly to destroy objects, although this practice
is not recommended. If an object has been placed at a specific address by the
new operator, you can call the destructor of the object explicitly to destroy
it. An explicitly called destructor cannot delete storage.
Note: You can only call destructors for class types. You cannot call
destructors for simple types. The call to the destructor in the following
example causes the compiler to issue a warning:
int * ptr;
ptr -> int::~int(); // warning
Related Information
Constructors and Destructors Overview
Constructors
Virtual Functions
Constructors and Destructors in Exception Handling
new Operator
delete Operator
ΓòÉΓòÉΓòÉ <hidden> Example of Destructors ΓòÉΓòÉΓòÉ
/***********************************************************************
*
The following example shows the use of destructors:
*
************************************************************************/
#include <string.h>
class Y
{
private:
char * string;
int number;
public:
Y(const char* n,int a); // constructor
~Y() { delete[] string; } // destructor
};
Y::Y(const char* n, int a) // define class Y constructor
{
string = strcpy(new char[strlen(n) + 1 ], n);
number = a;
}
void main ()
{
Y yobj = Y("somestring", 10); // create and initialize
// object of class Y
// ...
// destructor ~Y is called before control returns from main()
}
ΓòÉΓòÉΓòÉ 13.2. Free Store ΓòÉΓòÉΓòÉ
Free store is used for dynamic allocation of memory. The new and delete
operators are used to allocate and deallocate free store, respectively. You can
define your own versions of new and delete for a class by overloading them. You
can supply the new and delete operators with additional arguments. You can also
use the /Tm option to enable the debug versions of these operators. See Debug
Versions of new and delete for more information on these versions. When new and
delete operate on class objects, the class member operator functions new and
delete are called, if they have been declared.
If you create a class object with the new operator, the operator function
operator new() (if it has been declared) is called to create the object. An
operator new() for a class is always a static class member, even if it is not
declared with the keyword static. It has a return type void* and its first
argument must be the size of the object type and have type size_t. It cannot be
virtual.
Type size_t is an implementation dependent unsigned integral type defined in
<stddef.h>. For more information about size_t, see the IBM VisualAge C++ for
OS/2 User's Guide and Reference.
When you overload the new operator, you must declare it as a class member,
returning type void*, with first argument size_t, as described above. You
supply additional arguments in the declaration of operator new(). Use the
placement syntax to specify values for these arguments in an allocation
expression.
The delete operator destroys an object created by the new operator. The operand
of delete must be a pointer returned by new. If delete is called for an object
with a destructor, the destructor is invoked before the object is deallocated.
If you destroy a class object with the delete operator, the operator function
operator delete() (if it has been declared) is called to destroy the object. An
operator delete() for a class is always a static member, even if it is not
declared with the keyword static. Its first argument must have type void*.
Because operator delete() has a return type void, it cannot return a value. It
cannot be virtual.
When you overload the delete operator, you must declare it as class member,
returning type void, with first argument having type void*, as described above.
You can add a second argument of type size_t to the declaration. You can only
have one operator delete() for a single class.
Examples of operator new() and operator delete()
The result of trying to access a deleted object is undefined because the value
of the object can change after deletion.
If new and delete are called for a class object that does not declare the
operator functions new and delete, or they are called for a nonclass object,
the global operators new and delete are used. The global operators new and
delete are provided in the C++ library. The VisualAge C++ compiler does not
support them. When you declare arrays of class objects, the global new and
delete operators are used.
Related Information
new Operator
delete Operator
Overloaded new and delete
Debug Versions of new and delete
ΓòÉΓòÉΓòÉ <hidden> Examples of operators new() and delete() ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows two overloaded new operator functions.
*
************************************************************************/
#include <stddef.h>
class X
{
public:
void* operator new(size_t);
void* operator new(size_t, int);
};
void main ()
{
X* ptr1 = new X; // calls X::operator new(sizeof(X))
X* ptr2 = new(10) X; // calls X::operator
// new(sizeof(X),10)
}
/************************************************************************
*
The following example shows the declaration and use of the operator functions
operator new() and operator delete():
*
************************************************************************/
#include <stddef.h>
class X
{
public:
void* operator new(size_t);
void operator delete(void*); // single argument
};
class Y
{
public:
void operator delete(void*, size_t); // two arguments
};
void main ()
{
X* ptr = new X;
delete ptr; // call X::operator delete(void*)
Y* yptr;
// ...
delete yptr; // call Y::operator delete(void*, size_t)
// with size of Y as second argument
}
ΓòÉΓòÉΓòÉ <hidden> Debug Versions of new and delete ΓòÉΓòÉΓòÉ
VisualAge C++ provides special versions of new and delete to assist you in
debugging memory management problems. These versions can help you find where
memory is being incorrectly allocated, written to, or freed.
To enable the debug memory management functions, use the /Tm option, which also
defines the macro __DEBUG_ALLOC__. The debug versions of new and delete, as
well as of the C library functions (malloc and so on), are automatically called
in place of the regular functions in your code. Do not parenthesize any calls
to these functions, because parentheses disable the definition of the function
name to the debug function name.
To each call to new, the compiler adds 2 parameters equivalent to the current
__FILE__ and __LINE__ macros. They are inserted as the first two parameters in
the placement syntax. As a result, the global and class-specific versions of
operator new change from:
operator new(size_t);
operator new(size_t , additional_parameters );
to:
operator new(size_t, const char *, size_t);
operator new(size_t, const char *, size_t , additional_parameters );
The compiler adds the same parameters to each delete call, changing all global
and class-specific versions of delete from:
operator delete(void *);
operator delete(void * , size_t );
to:
operator delete(void *, const char *, size_t);
operator delete(void *, const char *, size_t , size_t );
The debug versions also automatically call the C library function _heap_check.
This function checks all memory blocks allocated or freed by debug memory
management functions to make sure that no overwriting has occurred outside the
bounds of allocated blocks or in a free memory block. You can also call
_heap_check explicitly.
You can also call the C library function _dump_allocated to print out
information about each memory block currently allocated by the debug memory
management functions. Both _heap_check and _dump_allocated are only available
when the __DEBUG_ALLOC__ macro is defined. They are described in the IBM
VisualAge C++ for OS/2 C Library Reference.
All output from these functions is sent to the OS/2 file handle 2, which is
usually associated with stderr.
Note: The information provided by these functions is Diagnosis, Modification,
and Tuning information only. It is not intended to be used as a
programming interface.
You may need to include the <new.h> header file to include the prototypes of
the debug malloc and free code that the debug versions of new and delete use.
Important The changes described above take place for all occurrences of new
and delete whether global or specific to a class. If you have provided member
new or delete functions, you must make code changes before compiling with /Tm.
You can use the __DEBUG_ALLOC__ macro for conditional compilation.
For more information on debug memory management functions in the C library,
see the IBM VisualAge C++ for OS/2 C Library Reference.
ΓòÉΓòÉΓòÉ 13.3. Temporary Objects ΓòÉΓòÉΓòÉ
It is sometimes necessary for the compiler to create temporary objects. They
are used during reference initialization and during evaluation of expressions
including standard type conversions, argument passing, function returns, and
evaluation of the throw expression.
When a temporary object is created to initialize a reference variable, the name
of the temporary object has the same scope as that of the reference variable.
When a temporary object is created during the evaluation of an expression, it
exists until there is a break in the flow of control of the program.
If a temporary object is created for a class with constructors, the compiler
calls the appropriate (matching) constructor to create the temporary object.
When a temporary object is destroyed and a destructor exists, the compiler
calls the destructor to destroy the temporary object. When you exit from the
scope in which the temporary object was created, it is destroyed. If a
reference is bound to a temporary object, the temporary object is destroyed
when the reference passes out of scope unless it is destroyed earlier by a
break in the flow of control. For example, a temporary object created by a
constructor initializer for a reference member is destroyed on leaving the
contructor.
Examples of Temporary Objects
Use the /Wgnr option to flag the points where temporary objects are generated.
Related Information
Constructors
Destructors
References
Initializing References
Expressions and Operators
Functions
Using Exception Handling
ΓòÉΓòÉΓòÉ <hidden> Examples of Temporary Objects ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows two expressions in which temporary objects xre
constructed:
*
************************************************************************/
class Y
{
public:
Y(int);
Y(Y&);
~Y();
};
Y add(Y y) { /* ... */ }
void main ()
{
Y obj1(10);
Y obj2 = add(Y(5)); // one temporary created
obj1 = add(obj1); // two temporaries created
}
/************************************************************************
*
In the above example, a temporary object of class type Y is created to
construct Y(5) before it is passed to the function add(). Because obj2 is being
constructed, the function add() can construct its return value directly into
obj2, so another temporary object is not created. A temporary object of class
type Y is created when obj1 is passed to the function add(). Because obj1 has
already been constructed, the function add() constructs its return value into a
temporary object. This second temporary object is then assigned to obj1 using
an assignment operator.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 13.4. User-Defined Conversions ΓòÉΓòÉΓòÉ
User-defined conversions allow you to specify object conversions with
constructors or with conversion functions User-defined conversions are
implicitly used in addition to standard conversions for conversion of
initializers, functions arguments, function return values, expression operands,
expressions controlling iteration, selection statements, and explicit type
conversions.
There are two types of user-defined conversions:
Conversion by constructor
Conversion functions.
Related Information
Implicit Type Conversions
Constructors and Destructors Overview
Constructors
C++ Classes
Functions
ΓòÉΓòÉΓòÉ 13.4.1. Conversion by Constructor ΓòÉΓòÉΓòÉ
You can call a class constructor with a single argument to convert from the
argument type to the type of the class.
At most one user-defined conversion, either a constructor or conversion
function, is implicitly applied to a class object. When you call a constructor
with an argument and you have not defined a constructor accepting that argument
type, only standard conversions are used to convert the argument to another
argument type acceptable to a constructor for that class. Other constructors or
conversions functions are not called to convert the argument to a type
acceptable to a constructor defined for that class.
Example of Conversion by Constructor
Related Information
Conversion Functions
Constructors and Destructors Overview
Constructors
User-Defined Conversions
Implicit Type Conversions
ΓòÉΓòÉΓòÉ <hidden> Example of Conversion by Constructor ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows conversion by constructor:
*
************************************************************************/
class Y
{
int a,b;
char* name;
public:
Y(int i);
Y(const char* n, int j = 0);
};
void add(Y);
// ...
void main ()
{
// code equivalent code
Y obj1 = 2; // obj1 = Y(2)
Y obj2 = "somestring"; // obj2 = Y("somestring",0)
obj1 = 10; // obj1 = Y(10)
add(5); // add(Y(5))
}
ΓòÉΓòÉΓòÉ 13.4.2. Conversion Functions ΓòÉΓòÉΓòÉ
You can define a member function of a class, called a conversion function, that
converts from the type of its class to another specified type.
Syntax of a Conversion Function
The conversion function specifies a conversion from the class type the
conversion function is a member of, to the type specified by the name of the
conversion function. Classes, enumerations, and typedef names cannot be
declared or defined as part of the function name.
Conversion functions have no arguments, and the return type is implicitly the
conversion type. Conversion functions can be inherited. You can have virtual
conversion functions but not static ones.
Only one user-defined conversion is implicitly applied to a single value.
User-defined conversions must be unambiguous, or they are not called.
If a conversion function is declared with the keyword const, the keyword has no
affect on the function except for acting as a tie-breaker when there is more
than one conversion function that could be applied. Specifically, if more than
one conversion function could be applied, all of these functions are compared.
If any of these functions is declared with the keyword const, const is ignored
for the purposes of this comparison. If one of these functions is a best match,
this function is applied. If there is no best match, the functions are compared
again, but this time const is not ignored.
Example of a Conversion Function
Related Information
Conversion by Constructor
Functions
Implicit Type Conversions
volatile and const Qualifiers
Virtual Functions
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Conversion Function ΓòÉΓòÉΓòÉ
>>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇoperatorΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇconversion_typeΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ(ΓöÇΓöÇ)ΓöÇΓöÇ>
ΓööΓöÇclass::ΓöÇΓöÿ Γö£ΓöÇconstΓöÇΓöÇΓöÇΓöÇΓöñ Γö£ΓöÇ*ΓöÇΓöñ
ΓööΓöÇvolatileΓöÇΓöÿ ΓööΓöÇ&ΓöÇΓöÿ
>ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ{ΓöÇΓöÇfunction_bodyΓöÇΓöÇ}ΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Example of a Conversion Function ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following code fragment shows a conversion function called operator int():
*
************************************************************************/
class Y
{
int b;
public:
operator int();
};
Y::operator int() {return b;}
void f(Y obj )
{
// each value assigned is converted by Y::operator int()
int i = int(obj);
int j = (int)obj;
int k = i + obj;
}
ΓòÉΓòÉΓòÉ 13.5. Initialization by Constructor ΓòÉΓòÉΓòÉ
A class object with a constructor must be explicitly initialized or have a
default constructor. Explicit initialization using a constructor is the only
way, except for aggregate initialization, to initialize nonstatic constant and
reference class members.
A class object that has no constructors, no virtual functions, no private or
protected members, and no base classes is called an aggregate. Aggregates are
described in Structures and Unions.
Class objects with constructors can be initialized with a parenthesized
expression list. This list is used as an argument list for the call of a
constructor that is used to initialize the class. You can also call a
constructor with a single initialization value using the = operator. Because
this type of expression is an initialization, not an assignment, the assignment
operator function, if one exists, is not called. This value is used as a single
argument for the call of a constructor. The type of the single argument must
match the type of the first argument to the constructor. If the constructor has
remaining arguments, these arguments must have default values.
Syntax of an Explicit Initializer by Constructor
Constructors can initialize their members in two different ways. A constructor
can use the arguments passed to it to initialize member variables in the
constructor definition:
complx( double r, double i = 0.0) {re = r; im = i;}
Or a constructor can have an initializer list within the definition but prior
to the function body:
complx ( double r, double i = 0) : re(r), im(i) { /* ... */ }
Both methods assign the argument values to the appropriate data members of the
class. The second method must be used to initialize base classes from within a
derived class to initialize constant and reference members and members with
constructors.
Example of Explicit Initialization by Constructor
Related Information
Initializing Base Classes and Members
Construction Order of Derived Class Objects
Constructors and Destructors Overview
Constructors
C++ Classes
ΓòÉΓòÉΓòÉ <hidden> Syntax of an Explicit Initializer by Constructor ΓòÉΓòÉΓòÉ
The syntax for an initializer that explicitly initializes a class object with a
constructor is:
>>ΓöÇΓöÇΓö¼ΓöÇ(ΓöÇΓöÇexpressionΓöÇΓöÇ)ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇ=ΓöÇΓöÇΓö¼ΓöÇexpressionΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ
Γöé ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇ{ΓöÇΓöÇΓöÇΓöÇexpressionΓöÇΓö┤ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ}ΓöÇΓöÿ
ΓööΓöÇ,ΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Example of Explicit Initialization by Constructor ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows the declaration and use of several constructors
that explicitly initialize class objects:
*
************************************************************************/
// This example illustrates explicit initialization
// by constructor.
#include <iostream.h>
class complx
{
double re, im ;
public:
complx(); // default constructor
complx(const complx& c) {re = c.re; im = c.im;}
// copy constructor
complx( double r, double i = 0.0) {re = r; im = i;}
// constructor with default trailing argument
void display()
{
cout << "re = "<< re << " im = " << im << endl;
}
};
void main ()
{
complx one(1); // initialize with complx(double, double)
complx two = one; // initialize with a copy of one
// using complx::complx(const complx&)
complx three = complx(3,4); // construct complx(3,4)
// directly into three
complx four; // initialize with default constructor
complx five = 5; // complx(double, double) & construct
// directly into five
one.display();
two.display();
three.display();
four.display();
five.display();
}
/************************************************************************
*
The above example produces the following output:
re = 1 im = 0
re = 1 im = 0
re = 3 im = 4
re = 0 im = 0
re = 5 im = 0
*
************************************************************************/
ΓòÉΓòÉΓòÉ 13.5.1. Initializing Base Classes and Members ΓòÉΓòÉΓòÉ
You can initialize immediate base classes and derived class members that are
not inherited from base classes by specifying initializers in the constructor
definition prior to the function body.
Syntax of a Constructor Initializer
In a constructor that is not inline, include the initialization list as part of
the function definition, not as part of the class declaration.
For example:
class B1
{
int b;
public:
B1();
B1(int i) : b(i) { /* ... */ } // inline constructor
};
class B2
{
int b;
protected:
B2();
B2(int i); // noninline constructor
};
// B2 constructor definition including initialization list
B2::B2(int i) : b(i) { /* ...*/ }
// ...
class D : public B1, public B2
{
int d1, d2;
public:
D(int i, int j) : B1(i+1), B2(), d1(i) {d2 = j;}
};
If you do not explicitly initialize a base class or member that has
constructors by calling a constructor, the compiler automatically initializes
the base class or member with a default constructor. In the above example, if
you leave out the call B2() in the constructor of class D (as shown below), a
constructor initializer with an empty expression list is automatically created
to initialize B2. The constructors for class D, shown above and below, result
in the same construction of an object of class D.
class D : public B1, public B2
{
int d1, d2;
public:
// call B2() generated by compiler
D(int i, int j) : B1(i+1), d1(i) {d2 = j;}
};
Note: You must declare base constructors with the access specifiers public or
protected to enable a derived class to call them.
Example of Base Constructors and Derivation
Related Information
Construction Order of Derived Class Objects
Initialization by Constructor
Constructors and Destructors Overview
Constructors
Member Access
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Constructor Initializer ΓòÉΓòÉΓòÉ
The syntax for a constructor initializer is:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé
>>ΓöÇΓöÇ:ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇidentifierΓöÇΓö¼ΓöÇΓöÇ(ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ)ΓöÇΓö┤ΓöÇΓöÇ><
ΓööΓöÇclass_nameΓöÇΓöÿ ΓööΓöÇassignment_expressionΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Example of Base Constructors and Derivation ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows how to call base constructors from derived classes:
*
************************************************************************/
class B1
{
int b;
public:
B1();
B1(int i) : b(i) { /* ... */ }
};
class B2
{
int b;
protected:
B2();
B2(int i);
};
B2::B2(int i) : b(i) { /* ... */ }
class B4
{
public:
B4(); // public constructor for B4
int b;
private:
B4(int); // private constructor for B4
};
// .
// .
// .
class D : public B1, public B2, public B4
{
int d1, d2;
public:
D(int i, int j) : B1(i+1), B2(i+2) ,
B4(i) {d1 = i; d2 = j; }
// error, attempt to access private constructor B4()
D(int i, int j) : B1(i+1), B2(i+2) {d1 = i; d2 = j;}
// valid, calls public constructor for B4
};
ΓòÉΓòÉΓòÉ 13.5.2. Construction Order of Derived Class Objects ΓòÉΓòÉΓòÉ
When a derived class object is created using constructors, it is created in the
following order:
1. Virtual base classes are initialized, in the order they appear in the
base list.
2. Nonvirtual base classes are initialized, in declaration order.
3. Class members are initialized in declaration order (regardless of their
order in the initialization list).
4. The body of the constructor is executed.
In the following code fragment, the constructor for class B1 is called before
the member d1 is initialized. The value passed to the constructor for class B1
is undefined.
class B1
{
int b;
public:
B1();
B1(int i) {b = i;}
};
class D : public B1
{
int d1, d2;
public:
D(int i, int j) : d1(i), B1(d1) {d2 = j;}
// d1 is not initialized in call B1::B1(d1)
};
Related Information
Initializing Base Classes and Members
Initialization by Constructor
Constructors and Destructors Overview
Constructors
Derivation
ΓòÉΓòÉΓòÉ 13.6. Copying Class Objects ΓòÉΓòÉΓòÉ
You can copy one class object to another object of the same type by either
assignment or initialization.
Copy by assignment is implemented with an assignment operator function. If you
do not define the assignment operator, it is defined as memberwise assignment.
Copy by initialization is implemented with a copy constructor. If you do not
define a copy constructor, it is defined as memberwise initialization of
members of its class.
Memberwise assignment and memberwise initialization mean that, if a class has a
member that is a class object, the assignment operator and copy constructor of
that class object are used to implement assignment and initialization of the
member.
Restrictions:
A default assignment operator cannot be generated for a class that has:
A nonstatic constant or reference data member
A nonstatic data member or base class whose assignment operator is not
accessible
A nonstatic data member or base class with no assignment operator and for
which a default assignment operator cannot be generated.
A default copy constructor cannot be generated for a class that has:
A nonstatic data member or base class whose copy constructor is not
accessible
A nonstatic data member or base class with no copy constructor and for
which a default copy constructor cannot be generated.
Related Information
Copy by Assignment
Copy by Initialization
Constructors and Destructors Overview
Constructors
Overloaded Assignment
ΓòÉΓòÉΓòÉ 13.6.1. Copy by Assignment ΓòÉΓòÉΓòÉ
If you do not define an assignment operator and one is required, a default
assignment operator is defined. If you do not define an assignment operator and
one is not required, a default assignment operator is declared but not defined.
If an assignment operator that takes a single argument of a class type exists
for a class, a default assignment operator is not generated.
Copy by assignment is used only in assignment.
You can define an assignment operator for a class with a single argument that
is a constant reference to that class type, only if all its base classes and
members have assignment operators that accept constant arguments.
For example:
class B1
{
public:
B1& operator=(const B1&);
};
class D: public B1
{
public:
D& operator=(const D&);
};
D& D::operator=(const D& dobj) {D dobj2 = dobj;
return dobj2;}
Otherwise, you can define an assignment operator for a class with a single
argument that is a reference to that class type. For example:
class Z
{
public:
Z& operator=( Z&);
};
Z& Z::operator=(Z& zobj) {Z zobj2 = zobj;
return zobj2;}
The default assignment operator for a class is a public class member. The
return type is a reference to the class type it is a member of.
Related Information
Assignment Expressions
Overloaded Assignment
Copy by Initialization
C++ Classes
ΓòÉΓòÉΓòÉ 13.6.2. Copy by Initialization ΓòÉΓòÉΓòÉ
You can define a copy constructor for a class. If you do not define a copy
constructor and one is required, a default copy constructor is defined. If you
do not define a copy constructor, and one is not required, a default copy
constructor is declared but not defined. If a class has a copy constructor
defined, a default copy constructor is not generated.
Copy by initialization is used only in initialization.
You can define a copy constructor for a class with a single argument that is a
constant reference to a class type only if all its base classes and members
have copy constructors that accept constant arguments. For example:
class B1
{
public:
B1(const B1&) { /* ... */ }
};
class D: public B1
{
public:
D(const D&);
};
D::D(const D& dobj):B1(dobj) { /* ... */ }
Otherwise, you can define a copy constructor with a single reference to a class
type argument. For example:
class Z
{
public:
Z(Z&);
};
Z::Z(Z&) { /* ...*/ }
The default copy constructor for a class is a public class member.
Related Information
Initialization by Constructor
Constructors
Copy by Assignment
C++ Classes
ΓòÉΓòÉΓòÉ 14. C++ Inheritance ΓòÉΓòÉΓòÉ
In C++, you can create classes from existing classes using the object-oriented
programming technique called inheritance. Inheritance allows you to define an
is a relationship between classes. When members are inherited, they can be used
as if they are members of the class that inherits them.
This chapter discusses:
Derivation
Inherited Member Access
Multiple Inheritance
Virtual Functions
Abstract Classes
Related Information
Functions
C++ Classes
C++ Class Members and Friends
ΓòÉΓòÉΓòÉ 14.1. Inheritance Overview ΓòÉΓòÉΓòÉ
C++ implements inheritance through the mechanism of derivation. Derivation
allows you to reuse code by creating new classes, called derived classes, that
inherit properties from one or more existing classes, called base classes. A
derived class inherits the properties, including data and function members, of
its base class. You can also add new data members and member functions to the
derived class. You can modify the implementation of existing member functions
or data by overriding base class member functions or data in the newly derived
class.
Multiple inheritance allows you to create a derived class that inherits
properties from more than one base class.
Because a derived class inherits members from all its base classes, ambiguities
can result. For example, if two base classes have a member with the same name,
the derived class cannot implicitly differentiate between the two members. Note
that, when you are using multiple inheritance, the access to names of base
classes may be ambiguous.
Examples of Single and Multiple Inheritance
Multiple inheritance allows you to have more than one base class for a single
derived class. You can create an interconnected inheritance graph of inherited
classes by using derived classes as base classes for other derived classes. You
can build an inheritance graph through the process of specialization, in which
derived classes are more specialized than their base classes. You can also work
in the reverse direction and build an inheritance graph through generalization.
If you have a number of related classes that share a group of properties, you
can generalize and build a base class to embody them. The group of related
classes becomes the derived classes of the new base class.
A direct base class is a base class that appears directly as a base specifier
in the declaration of its derived class. A direct base class is analogous to a
parent in a hierarchical graph.
An indirect base class is a base class that does not appear directly in the
declaration of the derived class but is available to the derived class through
one of its base classes. An indirect base class is analogous to a grandparent
or great grandparent or great-great grandparent in a hierarchical graph. For a
given class, all base classes that are not direct base classes are indirect
base classes.
Polymorphic functions are functions that can be applied to objects of more than
one type. In C++, polymorphic functions are implemented in two ways:
Overloaded functions are statically bound at compile time, as discussed
in Overloading Functions.
C++ provides virtual functions. A virtual function is a function that can
be called for a number of different user-defined types that are related
through derivation. Virtual functions are bound dynamically at run time.
Typically, a base class has several derived classes, each requiring its own
customized version of a particular operation. It is difficult for a base class
to implement member functions that are useful for all of its derived classes.
A base class would have to determine which derived class an object belonged to
before it could execute the applicable code for that object. When a virtual
function is called, the compiler executes the function implementation
associated with the object that the function is called for. The implementation
of the base class is only a default that is used when the derived class does
not contain its own implementation.
Related Information
Derivation
Multiple Inheritance
Virtual Functions
C++ Classes
C++ Class Members and Friends
ΓòÉΓòÉΓòÉ 14.2. Examples of Single and Multiple Inheritance ΓòÉΓòÉΓòÉ
Suppose you have defined a shape class to describe and operate on geometric
shapes. Now suppose you want to define a circle class. Because you have
existing code that operates on the shape class, you can use inheritance to
create the circle class. You can redefine operations in the derived circle
class that were originally defined in the shape base class. When you manipulate
an object of the circle class, these redefined function implementations are
used.
For example:
class shape
{
char* name;
int xpoint, ypoint;
public:
virtual void rotate(int);
virtual void draw();
void display() const;
};
class circle: public shape // derive class circle from
class shape
{
int xorigin, yorigin;
int radius;
public:
void rotate(int);
void draw();
void display() const;
};
In the above example, class circle inherits the data members name, xpoint and
ypoint, as well as the member functions display(), rotate(), and draw() from
class shape. Because the member functions rotate() and draw() are declared in
class shape with the keyword virtual, you can provide an alternative
implementation for them in class circle.
You can also provide an alternative implementation for the nonvirtual member
function display() in class circle. When you manipulate an argument of type
circle using a pointer to shape, and call a virtual member function, the member
function defined in the derived class overrides the base-class member function.
A similar call to a nonvirtual member function will call the member function
defined in the base class. In addition to inheriting the members of class
shape, class circle has declared its own data members, xorigin, yorigin, and
radius.
The key difference between virtual and nonvirtual member functions is that,
when you treat the circle class as if it were a shape, the implementations of
the virtual functions rotate() and draw() defined in class circle are used,
rather than those originally defined in class shape. Because display() is a
nonvirtual member function, the original implementation of display() defined in
class shape is used.
Multiple inheritance allows you to create a derived class that inherits
properties from more than one base class.
For example, in addition to the shape class, described above, you could also
have a symbol class. Because a circle is both a shape and a symbol, you can use
multiple inheritance to reflect this relationship. If the circle class is
derived from both the shape and symbol classes, the circle class inherits
properties from both classes.
class symbol
{
char* language;
char letter;
int number;
public:
virtual void write();
virtual void meaning();
};
class shape
{
char* name;
int xpoint, ypoint;
public:
virtual void rotate(int);
virtual void draw();
void display() const;
};
class circle: public symbol, public shape
{
int xorigin, yorigin;
int radius;
public:
void rotate(int);
void draw ();
void write();
void meaning();
void display() const;
};
In the above example, class circle inherits the members name, xpoint, ypoint,
display(), rotate(), and draw() from class shape and also inherits the members
language, letter, number, write(), and meaning() from class symbol.
ΓòÉΓòÉΓòÉ 14.3. Derivation ΓòÉΓòÉΓòÉ
Inheritance is implemented in C++ through the mechanism of derivation.
Derivation allows you to derive a class, called a derived class, from another
class, called a base class.
In the declaration of a derived class, you list the base classes of the derived
class. The derived class inherits its members from these base classes. All
classes that appear in the list of base classes must be previously defined
classes.
Syntax of a Derived Class
Incompletely declared classes are not allowed in base lists.
For example:
class X; // incomplete declaration of class X
class Y: public X // error
{
};
When you derive a class, the derived class inherits class members of the base
class. You can refer to inherited members (base class members) as if they were
members of the derived class. If you redefine base class members in the derived
class, you can still refer to the base class members by using the :: (scope
resolution) operator.
You can manipulate a derived class object as if it were a base class object.
You can use a pointer or a reference to a derived class object in place of a
pointer or reference to its base class. For example, you can pass a pointer or
reference to a derived class object D to a function expecting a pointer or
reference to the base class of D. You do not need to use an explicit cast to
achieve this; a standard conversion is performed. You can implicitly convert a
pointer to a derived class to point to a base class. You can also implicitly
convert a reference to a derived class to a reference to a base class.
The reverse case is not allowed. You cannot implicitly convert a pointer or a
reference to a base class object to a pointer or reference to a derived class.
Examples of Derived Classes
If a member of a derived class and a member of a base class have the same name,
the base class member is hidden in the derived class. If a member of a derived
class has the same name as a base class, the base class name is hidden in the
derived class. In both cases, the name of the derived class member is called
the dominant name.
Related Information
Inheritance Overview
C++ Classes
Scope Resolution Operator ::
Multiple Inheritance
ΓòÉΓòÉΓòÉ <hidden> Examples of Derived Classes ΓòÉΓòÉΓòÉ
/************************************************************************
*
You can refer to inherited members (base class members) as if they were members
of the derived class:
*
************************************************************************/
// This example illustrates references
// to base class members.
class base
{
public:
int a,b;
};
class derived : public base
{
public:
int c;
};
void main()
{
derived d;
d.a = 1; // base::a
d.b = 2; // base::b
d.c = 3; // derived::c
}
/************************************************************************
*
The derived class can also add new class members and redefine existing base
class members. In the above example, the two inherited members, a and b, of the
derived class d, in addition to the derived class member c, are assigned
values.
For example:
*
************************************************************************/
// This example illustrates references to base class
// members with the scope resolution (::) operator.
#include <iostream.h>
class base
{
public:
char* name;
void display(char* i) {cout << i << endl;}
};
class derived : public base
{
public:
char* name;
void display(char* i){cout << i << endl;}
};
void main()
{
derived d; // create derived class object
d.name = "Derived Class"; // assignment to derived::name
d.base::name = "Base Class"; // assignment to base::name
// call derived::display(derived::name)
d.display(d.name);
// call base::display(base::name)
d.base::display(d.base::name);
}
/************************************************************************
*
In the following example, d, a pointer to a derived class object is assigned to
bptr, a pointer to a base class object. A call is made to display() using bptr.
Even though bptr has a type of pointer to base, in the body of display() the
name member of derived is manipulated:
*
************************************************************************/
// This example illustrates how to make a pointer
// to a derived class point to a base class.
#include <iostream.h>
class base
{
public:
char* name;
void display(char* i) {cout << i << endl;}
};
class derived : public base
{
public:
char* name;
void display(char* i){cout << i << endl;}
};
void main()
{
derived d;
// standard conversion from derived* to base*
base* bptr = &d;
// call base::display(base::name)
bptr->display(bptr->name);
}
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Derived Class Declaration ΓòÉΓòÉΓòÉ
The syntax for the list of base classes is:
>>ΓöÇΓöÇderived_classΓöÇΓöÇ:ΓöÇΓöÇ>
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇqualified_class_specifierΓöÇΓö┤ΓöÇΓöÇ><
Γö£ΓöÇvirtualΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Γö£ΓöÇpublicΓöÇΓöÇΓöÇΓöÇΓöñ Γöé
Γöé Γö£ΓöÇprivateΓöÇΓöÇΓöÇΓöñ Γöé
Γöé ΓööΓöÇprotectedΓöÇΓöÿ Γöé
ΓööΓöÇΓö¼ΓöÇpublicΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ
Γö£ΓöÇprivateΓöÇΓöÇΓöÇΓöñ ΓööΓöÇvirtualΓöÇΓöÿ
ΓööΓöÇprotectedΓöÇΓöÿ
The qualified class specifier must be a class that has been previously declared
in a class declaration as described in Class Names. The access specifiers
(public, private, and protected) are described in Member Access.
The virtual keyword can be used to declare virtual base classes.
The following example shows the declaration of the derived class D and the base
classes V, B1, and B2. The class B1 is both a base class and a derived class
because it is derived from class V and is a base class for D.
class V { /* ... */ };
class B1 : virtual public V { /* ... */ };
class B2 { /* ... */ };
class D : public B1, private B2 { /* ... */ };
ΓòÉΓòÉΓòÉ 14.4. Inherited Member Access ΓòÉΓòÉΓòÉ
Access specifiers, as described in Member Access, control the level of access
to noninherited class members. The access for an inherited member is controlled
in three ways:
When you declare a member in a base class, you can specify a level of
access using the keywords public, private, and protected.
When you derive a class, you can specify the access level for the base
class in the base list.
You can also restore the access level of inherited members. See
Derivation Access of Base Classes for an example.
Resolution of member names does not depend on the level of access associated
with each class member.
Consider the following example:
class A {
private:
int a;
};
class B {
public:
int a;
};
class C : public A, public B {
void f() { a = 0; } // ambiguous - is it A::a or B::a?
};
In this example, class A has a private member a, and class B has a public
member a. Class C is derived from both A and B. C does not have access to
A::a, but a in the body of f() can still resolve to either A::a or B::a. For
this reason, a is ambiguous in the body of f().
If a class is derived publicly from a base class, a protected static base
class member can be accessed by members and friends of any classes derived
from that base class. A protected nonstatic base class member can be accessed
by members and friends of any classes derived from that base class by using
one of the following:
A pointer to a directly or indirectly derived class
A reference to a directly or indirectly derived class
An object of a directly or indirectly derived class
If a class is derived privately from a base class, all protected base class
members become private members of the derived class.
Examples of Inherited Member Access Rules
Related Information
Derivation Access of Base Classes
Access Declarations
Access Resolution
Member Access
Derivation
C++ Classes
ΓòÉΓòÉΓòÉ 14.4.1. Derivation Access of Base Classes ΓòÉΓòÉΓòÉ
When you declare a derived class, an access specifier can precede each base
class in the base list of the derived class. This does not alter the access
attributes of the individual members of a base class as seen by the base class,
but allows the derived class to restore the access attributes of the members of
a base class.
You can derive classes using any of the three access specifiers:
In a public base class, public and protected members of the base class
remain public and protected members of the derived class.
In a private base class, public and protected members of the base class
become private members of the derived class.
In a protected base class, public and protected members of the base class
are protected members of the derived class.
In all cases, private members of the base class remain private. Private
members of the base class cannot be used by the derived class unless friend
declarations within the base class explicitly grant access to them.
You can use both a structure and a class as base classes in the base list of a
derived class declaration. If the base class is declared with the keyword
class, its default access specifier in the base list of a derived class is
private. If the base class is declared with the keyword struct, its default
access specifier in the base list of a derived class is public.
Examples of Public and Private Derivation
Members and friends of a class can implicitly convert a pointer to an object
of that class to a pointer to either:
A direct private base class
A protected base class (either direct or indirect)
Related Information
Access Declarations
Access Resolution
Member Access
Derivation
C++ Classes
ΓòÉΓòÉΓòÉ <hidden> Examples of Public and Private Derivation ΓòÉΓòÉΓòÉ
In the following example, class d is derived publicly from class b. Class b is
declared a public base class by this declaration.
class b
{
// ...
};
class d : public b // public derivation
{
// ...
};
In the following example, private derivation is used by default because no
access specifier is used in the base list:
struct bb
{
// ...
};
class dd : bb // private derivation
{
// ...
};
ΓòÉΓòÉΓòÉ 14.4.2. Access Declarations ΓòÉΓòÉΓòÉ
You can restore access to members of a base class using an access declaration.
It allows you to change the access of a public member of a private or protected
base class back to public. You can also change the access of a protected member
of a private base class back to protected. Access is adjusted by using the base
class member qualified name in the public or protected declarations of the
derived class.
You only use access declarations to restore base class access. You cannot
change the access to a member to give it more access than it was originally
declared with. You cannot change the access of a private member to public or to
protected. You cannot change the access of a protected member to public.
An access declaration cannot be used to restrict access to a member that is
accessible in a base class.
It is redundant to use an access declaration to change the access to a public
member of a public base class to public, or to change the access to a protected
member of a protected base class to protected.
Examples of Access Declarations
Access declarations can only be used to adjust the access of a member of a base
class. The base class that an access declaration appears in can be directly or
indirectly inherited by the derived class.
You cannot adjust the access to a base class member if a member with the same
name exists in a class derived from that base class.
If you use an access declaration to adjust the access to an overloaded
function, the access is adjusted for all functions with that name in the base
class.
Related Information
Derivation Access of Base Classes
Access Resolution
Member Access
Derivation
Overloading Functions
C++ Classes
ΓòÉΓòÉΓòÉ <hidden> Examples of Access Declarations ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, the member b of the base class base is declared
public in its base class declaration. Class derived is derived privately from
class base. The access declaration in the public section of class derived
restores the access level of the member b back to public.
*
************************************************************************/
// This example illustrates using access declarations
// to restore base class access.
#include <iostream.h>
class base
{
char a;
public:
char c, b;
void bprint();
};
class derived: private base
{
char d;
public:
char e;
base::b; // restore access to b in derived
void dprint();
derived(char ch) { base::b = ch; }
};
void print(derived& d)
{
cout << " Here is d " << d.b << endl;
}
void main()
{
derived obj('c');
print(obj);
}
/************************************************************************
*
The external function print(derived&) can use the member b of base because the
access of b has been restored to public. The external function print(derived&)
can also use the members e and dprint() because they are declared with the
keyword public in the derived class. The derived class member dprint() can use
the members of its own class, d and e, in addition to the inherited members, b,
c, and bprint(), that are declared with the keyword public in the base class.
The base class member bprint() can use all the members of its own class, a, b,
and c.
You can also use an access declaration in a nested class. For example:
class B
{
public:
class N // nested class
{
public:
int i; // public member
};
};
class D: private B::N // derive privately
{
public:
B::N::i; // restores access to public
};
You cannot convert a pointer to a derived class object to a pointer to a base
class object if the base class is private or protected. For example:
class B { /* ... */ };
class D : private B { /* ... */ }; // private base class
void main ()
{
D d;
B* ptr;
ptr = &d; // error
}
*
************************************************************************/
ΓòÉΓòÉΓòÉ 14.4.3. Access Resolution ΓòÉΓòÉΓòÉ
Access resolution is the process by which the accessibility of a particular
class member is determined. Accessibility is dependent on the context. For
example, a class member can be accessible in a member function but inaccessible
at file scope. The following describes the access resolution procedure used by
the compiler.
In general, two scopes must be established before access resolution is applied.
These scopes reduce an expression or declaration into a simplified construct to
which the access rules are applied. Access rules are described in Member
Access. These scopes are:
Call scope The scope that encloses the expression or declaration that
uses the class member.
Reference scope The scope that identifies the class.
For example, in the following code:
// This example illustrates access resolution.
class B { public: int member; }; // declaration
class A : B {} // declaration
void main()
{
A aobject; // declaration
aobject.member = 10; // expression
}
the reference scope for member is the type of aobject, that is class type A.
Reference scope is chosen by simplifying the expression (or declaration)
containing the member. An expression can be thought of as being reduced to a
simple expression of the form obj.member where obj is the reference scope.
Reference scope is selected as follows:
If the member is qualified with . (dot) or -> (arrow), the reference
scope is the type of the object that is immediately to the left of the .
or -> operator closest to the member. Unqualified members are treated as
if they are qualified with this->.
If the member is a type member or a static member and is qualified with
:: (the scope resolution operator), the reference scope is the type
immediately to the left of the :: operator closest to the member.
Otherwise, the reference scope is the call scope.
The call scope and the reference scope determine the accessibility of a class
member. Once these scopes are resolved, the effective access of the member is
determined. Effective access is the access of the member as it is seen from
the reference scope. It is determined by taking the original access of the
member in its scope as the effective access and changing it as the class
hierarchy is traversed from the member's class to the reference scope.
Effective access is altered as the class hierarchy is traversed for each
derivation by the following:
The derivation access of a base class (see Derivation Access of Base
Classes)
Access declarations that are applied to the members (see Access
Declarations)
Friendships that are granted to the call scope (see Member Access)
After effective access is determined for a member, the access rules are
applied as if the effective access were the original access of the member. A
member is only accessible if the access rules say that it is.
Example of Access Resolution
Related Information
Derivation Access of Base Classes
Access Declarations
Member Access
Derivation
Overloading Functions
Scope Resolution Operator ::
C++ Classes
ΓòÉΓòÉΓòÉ <hidden> Example of Access Resolution ΓòÉΓòÉΓòÉ
The following example demonstrates the access resolution procedure.
class A
{
public:
int a;
};
class B : private A
{
friend void f (B*);
};
void f(B* b)
{
b->a = 10; // is 'a' accessible to f(B*) ?
}
// ...
The following steps occur to determine the accessibility of A::a in f(B*):
1. The call scope and reference scope of the expression b->a are determined:
a. The call scope is the function f(B*).
b. The reference scope is class B.
2. The effective access of member a is determined:
a. Because the original access of the member a is public in class A,
the initial effective access of a is public.
b. Because B inherits from A privately, the effective access of a
inside class B is private.
c. Because class B is the reference scope, the effective access
procedure stops here. The effective access of a is private.
3. The access rules are applied. The rules state that a private member can
be accessed by a friend or a member of the member's class. Because f(B*)
is a friend of class B, f(B*) can access the private member a.
ΓòÉΓòÉΓòÉ <hidden> Examples of Inherited Member Access Rules ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example demonstrates inherited member access rules.
*
************************************************************************/
// This example illustrates inherited member access rules.
class B
{
int a;
public:
int b,c;
void f(int) {}
protected:
int d;
void g(int) {}
};
class D1 : public B
{
int a;
public:
int b;
void h(int i )
{
g(i); // valid, protected B::g(int)
B::b = 10; // valid, B::b (not local b)
d = 5 ; // valid, protected B::d
}
};
class D2 : private B
{
int e;
public:
B::c; // modify access to B::c
void h(int i) { d = 5; } // valid,protected B::d
};
void main( )
{
int i= 1; // declare and initialize local variable
D1 d1; // create object of class d1
D2 d2; // create object of class d2
d1.a = 5; // error, D1::a is private in class D1
d2.b = 10; // error, B::b is inherited private to
// derived class D2
d2.c = 5; // valid, modified access from private to public
d2.B::c = 5; // valid, public B::c
d1.c = 5; // valid, B::c is inherited publicly
d1.d = 5; // error, B::d is protected in base class
d2.e = 10; // error, private D2::e
d1.g(i); // error, g(int) is protected in base class
d1.h(i); // valid
d2.h(i); // valid
}
/************************************************************************
*
*
************************************************************************/
ΓòÉΓòÉΓòÉ 14.5. Multiple Inheritance ΓòÉΓòÉΓòÉ
You can derive a class from more than one base class. Deriving a class from
more than one direct base class is called multiple inheritance.
In the following example, classes A, B, and C are direct base classes for the
derived class X:
class A { /* ... */ };
class B { /* ... */ };
class C { /* ... */ };
class X : public A, private B, public C { /* ... */ };
The order of derivation is relevant only to determine the order of default
initialization by constructors and cleanup by destructors. For more
information, see Initialization by Constructor.
A direct base class cannot appear in the base list of a derived class more than
once:
class B1 { /* ... */ }; // direct base class
class D : public B1, private B1 { /* ... */ }; // error
However, a derived class can inherit an indirect base class more than once, as
shown in the following example:
class L { /* ... */ }; // indirect base class
class B2 : public L { /* ... */ };
class B3 : public L { /* ... */ };
class D : public B2, public B3 { /* ... */ }; // valid
In the above example, class D inherits the indirect base class L once through
class B2 and once through class B3. However, this may lead to ambiguities
because two objects of class L exist, and both are accessible through class D.
You can avoid this ambiguity by referring to class L using a qualified class
name, for example, B2::L or B3::L .
You can also avoid this ambiguity by using the base specifier virtual to
declare a base class.
Examples of Single and Multiple Inheritance
Related Information
Virtual Base Classes
Multiple Access
Inheritance Overview
Derivation
Initialization by Constructor
C++ Classes
ΓòÉΓòÉΓòÉ 14.5.1. Virtual Base Classes ΓòÉΓòÉΓòÉ
If you have an inheritance graph in which two or more derived classes have a
common base class, you can use a virtual base class to ensure that the two
classes share a single instance of the base class.
In the following example, an object of class D has two distinct objects of
class L, one through class B1 and another through class B2. You can use the
keyword virtual in front of the base class specifiers in the base lists of
classes B1 and B2 to indicate that only one class L, shared by class B1 and
class B2, exists.
For example:
class L { /* ... */ }; // indirect base class
class B1 : virtual public L { /* ... */ };
class B2 : virtual public L { /* ... */ };
class D : public B1, public B2 { /* ... */ }; // valid
Using the keyword virtual in this example ensures that an object of class D
inherits only one object of class L.
A derived class can have both virtual and nonvirtual base classes. For example:
class V { /* ... */ };
class B1 : virtual public V { /* ... */ };
class B2 : virtual public V { /* ... */ };
class B3 : public V { /* ... */ };
class D : public B1, public B2, public B3 { /* ... */
};
In the above example, class D has two objects of class V, one that is shared by
classes B1 and B2 and one through class B3.
Related Information
Multiple Inheritance
Multiple Access
Inheritance Overview
Derivation
C++ Classes
ΓòÉΓòÉΓòÉ 14.5.2. Multiple Access ΓòÉΓòÉΓòÉ
In an inheritance graph containing virtual base classes, a name that can be
reached through more than one path is accessed through the path that gives the
most access.
For example:
class L { public: void f(); };
class B1 : private virtual L { /* ... */ };
class B2 : public virtual L { /* ... */ };
class D : public B1, public B2
{
public:
void f() {L::f();} // L::f() is accessed through B2
// and is public
};
In the above example, the function f() is accessed through class B2. Because
class B2 is inherited publicly and class B1 is inherited privately, class B2
offers more access.
An accessible base class is a publicly derived base class that is neither
hidden nor ambiguous in the inheritance hierarchy.
When you derive classes, ambiguities can result if base and derived classes
have members with the same names. Access to a base class member is ambiguous if
you use a name or qualified name that does not refer to a unique function,
object, type, or enumerator. The declaration of a member with an ambiguous name
in a derived class is not an error. The ambiguity is only flagged as an error
if you use the ambiguous member name.
For example, if two base classes have a member of the same name, an attempt to
access the member by the derived class is ambiguous. You can resolve ambiguity
by qualifying a member with its class name using the :: (scope resolution)
operator.
Example of Resolving Ambiguous Access
The compiler checks for ambiguities at compile time. Because ambiguity checking
occurs before access control or type checking, ambiguities may result even if
only one of several members with the same name is accessible from the derived
class.
Conversions (either implicit or explicit) from a derived class pointer or
reference to a base class pointer or reference must refer unambiguously to the
same accessible base class object. For example:
class W { /* ... */ };
class X : public W { /* ... */ };
class Y : public W { /* ... */ };
class Z : public X, public Y { /* ... */ };
void main ()
{
Z z;
X* xptr = &z; // valid
Y* yptr = &z; // valid
W* wptr = &z; // error, ambiguous reference to class W
// X's W or Y's W ?
}
You can use virtual base classes to avoid ambiguous reference. For example:
class W { /* ... */ };
class X : public virtual W { /* ... */ };
class Y : public virtual W { /* ... */ };
class Z : public X, public Y { /* ... */ };
void main ()
{
Z z;
X* xptr = &z; // valid
Y* yptr = &z; // valid
W* wptr = &z; // valid, W is virtual therefore only one
// W subobject exists
}
Related Information
Virtual Base Classes
Multiple Inheritance
Scope Resolution Operator ::
Inheritance Overview
Derivation
Member Access
ΓòÉΓòÉΓòÉ <hidden> Example of Resolving Ambiguous Access ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example uses the :: (scope resolution) operator to resolve an
ambiguous reference.
*
************************************************************************/
// This example illustrates ambiguous base classes.
class B1
{
public:
int i;
int j;
int g( );
};
class B2
{
public:
int j;
int g( );
};
// ...
class D : public B1, public B2
{
public:
int i;
};
void main ()
{
D dobj;
D *dptr = &dobj;
dptr -> i = 5; // valid, D::i
dptr -> j = 10; // error, ambiguous reference to j
dptr->B1::j = 10; // valid, B1::j
dobj.g( ); // error, ambiguous reference to g( )
dobj.B2::g( ); // valid, B2::g( )
}
ΓòÉΓòÉΓòÉ 14.6. Virtual Functions ΓòÉΓòÉΓòÉ
In C++, dynamic binding is supported by the mechanism of virtual functions.
Virtual functions must be members of a class. Use virtual functions when you
expect a class to be used as a base class in a derivation and when the
implementation of the function may be overridden in the derived class. You can
declare a member function with the keyword virtual in its class declaration.
For example:
class B
{
int a,b,c;
public:
virtual int f();
};
You can reimplement a virtual member function, like any member function, in any
derived class. The implementation that is executed when you make a call to a
virtual function depends on the type of the object for which it is called. If a
virtual member function is called for a derived class object and the function
is redefined in the derived class, the definition in the derived class is
executed. In this case, the redefined derived class function is said to
override the base class function. This occurs even if the access to the
function is through a pointer or reference to the base class.
If you call a virtual function with a pointer that has base class type but
points to a derived class object, the member function of the derived class is
called. However, if you call a nonvirtual function with a pointer that has base
class type, the member function of the base class is called regardless of
whether the pointer points to a derived class object.
Example of Overriding Virtual Functions
If the argument types or the number of arguments of the two functions are
different, the functions are considered different, and the function in the
derived class does not override the function in the base class. The function in
the derived class hides the function in the base class.
The return type of an overriding virtual function can differ from the return
type of the overridden virtual function provided that:
The overridden function returns a pointer or a reference to a class T
AND
The overriding virtual function returns a pointer or a reference to a
class derived from T.
An error does result when a virtual function that returns D* overrides a
virtual function that returns B* where B is an ambiguous base class of D. The
reason is that two or more instances of class B will exist within class D, and
the compiler will not know which base B to return. For more information, see
Function Return Values.
A virtual function cannot be global or static because, by definition, a
virtual function is a member function of a base class and relies on a specific
object to determine which implementation of the function is called. You can
declare a virtual function to be a friend of another class.
If a function is declared virtual in its base class, it can still be accessed
directly using the :: (scope resolution) operator. In this case, the virtual
function call mechanism is suppressed and the function implementation defined
in the base class is used. In addition, if you do not redefine a virtual
member function in a derived class, a call to that function uses the function
implementation defined in the base class.
A virtual function must be one of the following:
Defined
Declared pure
Defined and declared pure
A base class containing one or more pure virtual member functions is called an
abstract class.
Related Information
Virtual Base Classes
Ambiguous Virtual Function Calls
Virtual Function Access
Abstract Classes
Function Return Values
Friends
Scope Resolution Operator ::
Inheritance Overview
Derivation
ΓòÉΓòÉΓòÉ <hidden> Example of Overriding Virtual Functions ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows how virtual functions can be redefined.
*
************************************************************************/
class B
{
public:
virtual int f();
virtual int g();
int h();
};
class D : public B
{
public:
int f();
int g(char*); // hides B::g()
int h();
};
// ...
void main ()
{
D d;
B* bptr = &d;
bptr->f(); // calls D::f() because f() is virtual
bptr->h(); // calls B::h() because h() is nonvirtual
bptr->g(); // calls B::g()
d.g(); // error, wrong number and type of arguments
d.g("string"); // calls D::g(char*)
}
ΓòÉΓòÉΓòÉ 14.6.1. Ambiguous Virtual Function Calls ΓòÉΓòÉΓòÉ
It is an error to override one virtual function with two or more ambiguous
virtual functions. This can happen in a derived class that inherits from two
nonvirtual bases that are derived from a virtual base class.
Example of Ambiguous Virtual Functions
A special case occurs when the ambiguous overriding virtual functions come from
separate instances of the same class type. In the following example, there are
two objects (instances) of class L. There are two data members L::count, one in
class A and one in class B. If the declaration of class D is allowed,
incrementing L::count in a call to L::f() with a pointer to class V is
ambiguous.
class V
{
public:
virtual void f();
};
class L : virtual public V
{
int count;
void f();
};
void L::f() {++count;}
class A : public L
{ /* ... */ };
class B : public L
{ /* ... */ };
class D : public A, public B { /* ... */ }; // error
void main ()
{
D d;
V* vptr = &d;
vptr->f();
}
In the above example, the function L::f() is expecting a pointer to an L
object; that is, the this pointer for class L, as its first implicit argument.
Because there are two objects of class L in a D object, there are two this
pointers that could be passed to L::f(). Because the compiler cannot decide
which this pointer to pass to L::f(), the declaration of class D is flagged as
an error.
Related Information
Virtual Functions
Virtual Function Access
Multiple Access
Scope Resolution Operator ::
Member Functions
ΓòÉΓòÉΓòÉ <hidden> Example of Ambiguous Virtual Functions ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows a class that inherits from two nonvirtual bases
that are derived from a virtual base class.
*
************************************************************************/
class V
{
public:
virtual void f() { /* ... */ };
};
class A : virtual public V
{
void f() { /* ... */ };
};
class B : virtual public V
{
void f() { /* ... */ };
};
class D : public B, public A { /* ... */ }; // error
void main ()
{
D d;
V* vptr = &d;
vptr->f(); // which f(), A::f() or B::f()?
}
/************************************************************************
*
In class A, only A::f() will override V::f(). Similarly, in class B, only
B::f() will override V::f(). However, in class D, both A::f() and B::f() will
try to override V::f(). This attempt is not allowed because it is not possible
to decide which function to call if a D object is referenced with a pointer to
class V, as shown in the above example. Because only one function can override
a virtual function, the compiler flags this situation as an error.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 14.6.2. Virtual Function Access ΓòÉΓòÉΓòÉ
The access for a virtual function is specified when it is declared. The access
rules for a virtual function are not affected by the access rules for the
function that later overrides the virtual function. In general, the access of
the overriding member function is not known.
If a virtual function is called with a pointer or reference to a class object,
the type of the class object is not used to determine the access of the virtual
function. Instead, the type of the pointer or reference to the class object is
used.
In the following example, when the function f() is called using a pointer
having type B*, bptr is used to determine the access to the function f().
Although the definition of f() defined in class D is executed, the access of
the member function f() in class B is used. When the function f() is called
using a pointer having type D*, dptr is used to determine the access to the
function f(). This call produces an error because f() is declared private in
class D.
class B
{
public:
virtual void f();
};
class D : public B
{
private:
void f();
};
void main ()
{
D dobj;
B *bptr = &dobj;
D *dptr = &dobj;
bptr->f(); // valid, virtual B::f() is public,
// D::f() is called
dptr->f(); // error, D::f() is private
}
Related Information
Virtual Functions
Ambiguous Virtual Function Calls
Inherited Member Access
ΓòÉΓòÉΓòÉ 14.7. Abstract Classes ΓòÉΓòÉΓòÉ
An abstract class is a class that is designed to be specifically used as a base
class. An abstract class contains at least one pure virtual function. Pure
virtual functions are inherited. You can declare a function to be pure by using
a pure specifier in the declaration of the member function in the class
declaration.
For example:
class AB // abstract class
{
public:
virtual void f()= 0; // pure virtual member function
};
A function that is declared pure typically has no definition and cannot be
executed. Attempting to call a pure virtual function that has no implementation
is undefined; however, such a call does not cause an error. No objects of an
abstract class can be created.
Note: Because destructors are not inherited, a virtual destructor that is
declared pure must have a definition.
Virtual member functions are inherited. If a base class contains a pure virtual
member function and a class derived from that base class does not redefine that
pure virtual member function, the derived class itself is an abstract class.
Any attempt to create an object of the derived class type produces an error.
Examples of Errors using Abstract Classes
You cannot use an abstract class as the type of an explicit conversion, as an
argument type, or as the return type for a function. You can declare a pointer
or reference to an abstract class.
Related Information
Virtual Functions
Inheritance Overview
Derivation
C++ Classes
ΓòÉΓòÉΓòÉ <hidden> Examples of Errors using Abstract Classes ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows an attempt to create an object of an abstract class
type.
*
************************************************************************/
class AB // abstract class
{
public:
virtual void f()= 0; // pure virtual member function
};
class D: public AB
{
public:
void f();
};
// ...
void main ()
{
D d;
d.f() ; // calls D::f()
AB ab; // error, cannot create an object of an
// abstract class type
}
/************************************************************************
*
The following example shows an attempt to create an object of a class derived
from an abstract class, but that does not redefine the pure virtual function of
that abstract class.
*
************************************************************************/
For example:
class AB // abstract class
{
public:
virtual void f()= 0; // pure virtual member function
};
class D2: public AB
{
int a,b,c;
public:
void g();
};
// ...
void main ()
{
D2 d;
// error, cannot declare an object of abstract class D2
}
To avoid the error in the above example, provide a declaration of D2::f().
ΓòÉΓòÉΓòÉ 15. C++ Templates ΓòÉΓòÉΓòÉ
This chapter describes the C++ template facility. A template specifies how an
individual class, function, or static data member can be constructed by
providing a blueprint description of classes or functions within the template.
Unlike an ordinary class or function definition, a template definition contains
the template keyword, and uses a type argument, instead of a type, in one or
more of the constructs used to define the class or function template.
Individual classes or functions can then be generated simply by specifying the
template name and by naming the type for the particular class or function as
the type argument of the template. You can use templates to define a family of
types or functions.
This chapter discusses:
Template Syntax
Structuring Your Program Using Templates
Class Templates
Function Templates
Differences between Class and Function Templates
Member Function Templates
Friends and Templates
Static Data Members and Templates
Note: C++ objects with templates can now be linked as a separate step with
the VisualAge C++ linker command.
Related Information
Functions
C++ Classes
Type Specifiers
define
implementation
See the IBM VisualAge C++ for OS/2 User's Guide and Reference for programming
hints on using templates in C++ programs.
ΓòÉΓòÉΓòÉ 15.1. Template Syntax ΓòÉΓòÉΓòÉ
The syntax for a template is:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
>>ΓöÇΓöÇtemplateΓöÇΓöÇΓöÉΓöÇΓöÇΓö┤Γö¼ΓöÇargument-declarationΓöÇΓö¼Γö┤ΓöÇΓöÇ>ΓöÇΓöÇ><
Γöé Γöé Γöé
ΓööΓöÇtypeΓöÇΓöÇidentifierΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The declaration in a template declaration must define or declare one of the
following:
A class
A function
A static member of a template class
The identifier of a type is defined to be a type_name in the scope of the
template declaration. A template declaration can appear as a global
declaration only.
The template arguments (within the < and > delimitiers) specify the types and
the constants within the template that must be specified when the template is
instantiated.
Examples of Templates
Default intializers are permitted in template arguments, under the following
conditions:
They can only be applied to nontype template arguments.
Like functions, they can only be applied to trailing arguments.
Subsequent template declarations can add default initializers but cannot
redefine existing default initializers.
They can only be applied to class template declarations, not to function
template declarations.
Note: A template that defines a member function of a class template is
treated as a function template. Such a template cannot have default
intializers.
Example of Default Initializers in Templates
Related Information
Structuring Your Program Using Templates
Class Templates
Function Templates
C++ Classes
Functions
Static Members
ΓòÉΓòÉΓòÉ <hidden> Examples of Templates ΓòÉΓòÉΓòÉ
Given the following template:
template<class L> class Key
{
L k;
L* kptr;
int length;
public:
Key(L);
// ...
};
The following table shows what the classes Key<int>, Key<char*>, and
Key<mytype> look like:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé class Key<int> i; Γöé class Key<char*> c; Γöé class Key<mytype> m; Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé class Key<int> { Γöé class Key<char*> { Γöé class Key<mytype> { Γöé
Γöé int k; Γöé char* k; Γöé mytype k; Γöé
Γöé int * kptr; Γöé char** kptr; Γöé mytype* kptr; Γöé
Γöé int length; Γöé int length; Γöé int length; Γöé
Γöé public: Γöé public: Γöé public: Γöé
Γöé Key(int); Γöé Key(char*); Γöé Key(mytype); Γöé
Γöé // ... }; Γöé // ... }; Γöé // ... }; Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
The declarations create the following objects:
i of type Key<int>
c of type Key<char*>
m of type Key<mytype>
Note that these three classes have different names. The types contained within
the angle braces are not arguments to the class names, but part of the class
names themselves. Key<int> and Key<char*> are class names. Within the context
of the example, a class called Key (with no template argument list) is
undefined.
ΓòÉΓòÉΓòÉ <hidden> Example of Default Initializers in Templates ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows a valid template declaration with default
initializers:
*
************************************************************************/
// This example shows a template declaration
// with default initializers.
#include <stdio.h>
template <class T, int i=1> class X
{
public:
T s;
X(int j=4);
int val(T&)
{
return i;
};
};
template <class T, int i> X<T,i>::X(int j):s(i){
printf("i=%d j=%d\n",i,j);
}
void main()
{
X<int> myX(2);
X<int,3> myX2(4);
}
ΓòÉΓòÉΓòÉ 15.2. Structuring Your Program Using Templates ΓòÉΓòÉΓòÉ
You can structure your program three ways using templates:
1. Include the function template definition (both the .h and .c files) in
all files that may reference the corresponding template functions.
2. Include the function template declaration (the .h file only) in all files
that may reference the corresponding template functions, but include the
function definition (both the .h and .c files) in one file only.
3. Include the declaration of the function templates in a header file and
the definition in a source file that has the same name. When you include
the header file in your source, the compiler automatically generates the
template functions. Use the /Ft+ option to enable this method.
The following examples use two files to illustrate all three methods: stack.h
and stackdef.h
To instantiate a stack of 50 ints, you would declare the following in each
source file that requires it:
stack<int> intStack(50);
For method 1, each source file using the template should include both stack.h
and stackdef.h.
For method 2, every source file should include stack.h, but only one of the
files needs to include stackdef.h.
For method 3, every source file should include stack.h. The compiler
automatically generates the template functions in the TEMPINC subdirectory
that is created in the current directory. To use this method, copy stackdef.h
to stack.c and use the /Ft+ option, which is the default.
Note: C++ objects with templates can now be linked as a separate step with
the VisualAge C++ linker command.
Related Information
Template Syntax
Class Templates
Function Templates
Differences between Class and Function Templates
"Using Templates in C++ Programs" in the IBM VisualAge C++ for OS/2
Programming Guide.
ΓòÉΓòÉΓòÉ <hidden> stack.h and stackdef.h ΓòÉΓòÉΓòÉ
/************************************************************************
*
stack.h
*
************************************************************************/
#ifndef _STACK_TPL_H
#define _STACK_TPL_H
template<class T>
class stack
{
private:
T* v;
T* p;
int sz;
public:
stack( int );
~stack();
void push( T );
};
#endif
/************************************************************************
*
stackdef.h
*
************************************************************************/
#include "stack.h"
template<class T> stack<T>::stack( int s )
{
v = p = new T[sz=s];
}
template<class T> stack<T>::~stack()
{
delete [] v;
}
template<class T> void stack<T>::push( T a )
{
*p++ = a;
}
ΓòÉΓòÉΓòÉ 15.3. Class Templates ΓòÉΓòÉΓòÉ
The relationship between a class template and an individual class is like the
relationship between a class and an individual object. An individual class
defines how a group of objects can be constructed, while a class template
defines how a group of classes can be generated.
Note the distinction between the terms class template and template class:
Class template is a template used to generate template classes. A class
template can be only a declaration, or it can be a
definition of the class.
Template class is an instance of a class template.
A template definition is identical to any valid class definition that the
template might generate, except for the following:
The class template definition is preceded by template <
template-argument-list > where template-argument-list can include zero
or more arguments of user-defined type and zero or more argument
declarations. The template-argument-list must contain at least one
argument.
Types, variables, constants and objects within the class template can be
declared with arguments of user-defined type as well as with explicit
types (for example, int or char).
The template-argument-list can include argument-declarations (for
example, int a or char* b), which are generally used to define constant
values within the created class.
A class template can declare a class without defining it by using an
elaborated type specifier. For example:
template <class L,class T> class key;
This reserves the name as a class template name. All template declarations for
a class template must have the same types and number of template arguments.
Only one template declaration containing the class definition is allowed.
You can instantiate the class template by declaring a template class. If the
definitions of the member functions of the template class are not inlined,
then you have to define them. When you instantiate a template class, its
argument list must match the argument list in the class template declaration.
Syntax of a Template Class Instantiation
Note: When you have nested template argument lists, you must have a
separating space between the > at the end of the inner list and the one at the
end of the outer list. Otherwise, there is an ambiguity between the output
operator >> and two template list delimiters >.
template <class L,class T> class key
{
// ...
};
template <class L> class vector
{
// ...
};
void main ()
{
class key <int, vector<int> >; // instantiate template
}
Objects and functions of individual template classes can be accessed by any of
the techniques used to access ordinary class member objects and functions.
Examples of Accessing Class Template Members
Related Information
Class Template Declarations and Definitions
Nontype Template Arguments
Explicitly Defined Template Classes
Function Templates
Template Syntax
Structuring Your Program Using Templates
C++ Classes
Differences between Class and Function Templates
ΓòÉΓòÉΓòÉ <hidden> Syntax of a Template Class Instantiation ΓòÉΓòÉΓòÉ
The syntax for instantiation of a template class is:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
>>ΓöÇΓöÇtemplate-nameΓöÇΓöÇΓöÉΓöÇΓöÇΓö┤Γö¼ΓöÇtypeΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ>ΓöÇΓöÇ><
Γöé Γöé Γöé
ΓööΓöÇassignment-expressionΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Examples of Accessing Class Template Members ΓòÉΓòÉΓòÉ
Given a class template:
template<class T> class vehicle
{
public:
vehicle() { /* ... */ } // constructor
~vehicle() {}; // destructor
T kind[16];
T* drive();
static void roadmap();
// ...
};
and the declaration:
vehicle<char> bicycle; // instantiates the template
the constructor, the constructed object, and the member function drive() can be
accessed with any of the following (assuming the standard header file
<string.h> is included in the program file):
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé constructor Γöé "vehicle<char> bicycle; Γöé
Γöé Γöé // constructor called automatically Γöé
Γöé Γöé // object bicycle created" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé object "bicycle" Γöé "strcpy (bicycle.kind, "10 speed"); Γöé
Γöé Γöé bicycle.kind[0] = '2';" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé function "drive()" Γöé "char* n = bicycle.drive();" Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé function "roadmap()" Γöé "vehicle<char>::roadmap();" Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
ΓòÉΓòÉΓòÉ 15.3.1. Class Template Declarations and Definitions ΓòÉΓòÉΓòÉ
A class template must be declared before any declaration of a corresponding
template class. A class template definition can only appear once in any single
compilation unit. A class template must be defined before any use of a template
class that requires the size of the class or refers to members of the class.
In the following example, the class template key is declared before it is
defined. The declaration of the pointer keyiptr is valid because the size of
the class is not needed. The declaration of keyi, however, causes an error.
template <class L> class key; // class template declared,
// not defined yet
//
class key<int> *keyiptr; // declaration of pointer
//
class key<int> keyi; // error, cannot declare keyi
// without knowing size
//
template <class L> class key // now class template defined
{
// ...
};
If a template class is used before the corresponding class template is defined,
the compiler issues an error. A class name with the appearance of a template
class name is considered to be a template class. In other words, angle brackets
are valid in a class name only if that class is a template class.
The definition of a class template is not compiled until the definition of a
template class is required. At that point, the class template definition is
compiled using the argument list of the template class to instantiate the
template arguments. Any errors in the class definition are flagged at this
time. If the definition of a class template is never required, it is not
compiled. In this case, some errors in the definition might not be flagged by
the compiler. The /Wcls option can be used to find errors in class templates
that are not compiled.
A class template can only be defined once within a compilation unit, and the
class template name cannot be declared to refer to any other template, class,
object, function, value, or type in the same scope.
Related Information
Class Templates
Nontype Template Arguments
Explicitly Defined Template Classes
/Wcls option
C++ Classes
Function Templates
Differences between Class and Function Templates
ΓòÉΓòÉΓòÉ 15.3.2. Nontype Template Arguments ΓòÉΓòÉΓòÉ
A nontype template argument provided within a template argument list is an
expression whose value can be determined at compile time. Such arguments must
be constant expressions, addresses of functions or objects with external
linkage, or addresses of static class members. Nontype template arguments are
normally used to initialize a class or to specify the sizes of class members.
For nontype integral arguments, the instance argument matches the corresponding
template argument as long as the instance argument has a value and sign
appropriate to the argument type.
For nontype address arguments, the type of the instance argument must be of the
form identifier or &identifier, and the type of the instance argument must
match the template argument exactly, except that a function name is changed to
a pointer to function type before matching.
The resulting values of nontype template arguments within a template argument
list form part of the template class's type. If two template class names have
the same template name and if their arguments have identical values, they are
the same class.
Example of Nontype Template Arguments
Note: Arguments that contain the < symbol or the > symbol must be enclosed in
parentheses to prevent it from being parsed as a template argument list
delimiter when it is being used as a relational operator or a nested
template delimiter. For example, the arguments in the following
definition are valid:
myfilebuf<double, (20>10)> x; // valid
The following definition, however, is not valid because the greater than
operator (>) is interpreted as the closing delimiter of the template argument
list:
myfilebuf<double, 20>10> x; // error
If the template arguments do not evaluate identically, the objects created are
of different types:
myfilebuf<double,200> x; // create object x of class
// myfilebuf<double,200>
myfilebuf<double,200.0> y; // error, 200.0 is a double,
// not an int
The instantiation of y fails because the value 200.0 is of type double, and
the template argument is of type int.
The following two objects:
myfilebuf<double, 128> x
myfilebuf<double, 512> y
belong to separate template classes, and referencing either of these objects
later with myfilebuf<double> is an error.
A class template does not need to have a type argument if it has nontype
arguments. For example, the following template is a valid class template:
template<int i> class C
{
public:
int k;
C() { k = i; }
};
This class template can be instantiated by declarations such as:
class C<100>;
class C<200>;
Again, these two declarations refer to distinct classes because the values of
their nontype arguments differ.
Related Information
Class Templates
Class Template Declarations and Definitions
Explicitly Defined Template Classes
C++ Classes
Function Templates
Differences between Class and Function Templates
ΓòÉΓòÉΓòÉ <hidden> Example of Nontype Template Arguments ΓòÉΓòÉΓòÉ
In the following example, a class template is defined that requires a nontype
template int argument as well as the type argument:
template<class T, int size> class myfilebuf
{
T* filepos;
static int array[size];
public:
myfilebuf() { /* ... */ }
~myfilebuf();
advance(); // function defined elsewhere in program
};
In this example, the template argument size becomes a part of the template
class name. An object of such a template class is created with both the type
arguments of the class and the values of any additional template arguments.
An object x, and its corresponding template class with arguments double and
size=200, can be created from this template with a value as its second template
argument:
myfilebuf<double,200> x;
x can also be created using an arithmetic expression:
myfilebuf<double,10*20> x;
The objects created by these expressions are identical because the template
arguments evaluate identically. The value 200 in the first expression could
have been represented by an expression whose result at compile time is known to
be equal to 200, as shown in the second construction.
ΓòÉΓòÉΓòÉ 15.3.3. Explicitly Defined Template Classes ΓòÉΓòÉΓòÉ
You can override the definition of a class template of a particular template
class by providing a class definition for the type of class required. For
example, the following class template creates a class for each type for which
it is referenced, but that class may be inappropriate for a particular type:
template<class M> class portfolio
{
double capital;
M arr;
// ...
} ;
The type for which the template class is inappropriate can be defined by using
the applicable template class name. Assuming the inappropriately defined type
is stocks, you can redefine the class portfolio<stocks> as follows:
class portfolio<stocks>
{
double capital;
stocks yield;
// ...
};
An explicit specialization of a template class can be defined before the class
template is declared. In particular, a template class such as portfolio<stocks>
can be defined before its class template has been defined.
Related Information
Class Templates
Class Template Declarations and Definitions
Nontype Template Arguments
C++ Classes
Function Templates
Differences between Class and Function Templates
ΓòÉΓòÉΓòÉ 15.4. Function Templates ΓòÉΓòÉΓòÉ
A function template allows you to define a group of functions that are the same
except for the types of one or more of their arguments or objects. All type
arguments in a function template must be used in the argument list or in the
class qualifier for the function name. The type of a template function argument
need not be explicitly specified when the template function is called. In this
respect, a template function differs from a template class.
Note the distinction between the terms function template and template function:
Function template is a template used to generate template functions. A
function template can be only a declaration, or it can
define the function.
Template function is a function generated by a function template.
Example of a Function Template
Because template functions can be generated in all compilation units that
contain function template definitions, you may want to group function template
definitions into one or two compilation units. Using templates in C++ programs
is described completely in the IBM VisualAge C++ for OS/2 Programming Guide.
Related Information
Overloading Resolution for Template Functions
Explicitly Defined Template Functions
Function Template Declarations and Definitions
Differences between Class and Function Templates
Functions
Class Templates
ΓòÉΓòÉΓòÉ <hidden> Example of a Function Template ΓòÉΓòÉΓòÉ
If you want to create a function approximate(), which determines whether two
values are within 5% of each other, you can define the following template:
#include <math.h>
template <class T> int approximate (T first, T second)
{
double aptemp=double(first)/double(second);
return int(abs(aptemp-1.0) <= .05);
};
Assuming you have two values of type float you want to compare, you can use the
approximate function template:
float a=3.24, b=3.35;
if (approximate(a,b))
cout << "a and b are pretty close" << endl;
A template function int approximate(float,float) is generated to resolve the
call.
ΓòÉΓòÉΓòÉ 15.4.1. Overloading Resolution for Template Functions ΓòÉΓòÉΓòÉ
Resolution of overloaded template functions is done in the following order:
1. Look for a function with an exact type match. This does not include
template functions, unless such functions were explicitly declared using
a function declaration. Trivial conversions are performed if they produce
an exact type match.
2. Look for a function template that allows generation of a function with an
exact type match. Trivial conversions are performed if they produce an
exact type match.
3. Try ordinary overloading resolution for functions already present. This
does not include template functions, unless such functions were
explicitly declared using a function declaration.
A call to a template function causes an error, and no overloading is done if
the following conditions are true:
The only available functions for a call are template functions.
These functions would require nontrivial conversions for the call to
succeed.
These functions have not been explicitly declared.
Example of Overloading a Template Function
Related Information
Trivial Conversions
Implicit Type Conversions
Function Templates
Explicitly Defined Template Functions
Function Template Declarations and Definitions
Functions
Differences between Class and Function Templates
Class Templates
ΓòÉΓòÉΓòÉ <hidden> Example of Overloading a Template Function ΓòÉΓòÉΓòÉ
In the case of the approximate() function template:
#include <math.h>
template <class T> int approximate (T first, T second)
{
double aptemp=double(first)/double(second);
return int(abs(aptemp-1.0) <= .05);
};
if the two input values are of different types, overloading resolution does not
take place:
float a=3.24;
double b=3.35;
if (approximate(a,b)) // error, different types
{ /* ... */ }
The solution is to force a conversion to one of the available function types by
explicitly declaring the function for the chosen type. To resolve the
float/double example, include the following function declaration:
int approximate(double a, double b);
// force conversion of the float to double
This declaration creates a function approximate() that expects two arguments of
type double, so that when approximate(a,b) is called, the overloading is
resolved by converting variable a to type double.
ΓòÉΓòÉΓòÉ 15.4.2. Explicitly Defined Template Functions ΓòÉΓòÉΓòÉ
In some situations, a function template can define a group of functions in
which, for one function type, the function definition would be inappropriate.
For instance, the function template:
template<class T> int approximate(T first, T second);
determines whether two values are within 5% of each other. The algorithm used
for this function template is appropriate for numerical values, but for char*
values, it would indicate whether the pointers to two character strings are
within 5% of one another, not whether the strings themselves are approximately
equal. Whether two pointers are within 5% of each other is not useful
information. You can define an explicit template function for char* values to
compare the two strings themselves, character by character.
Example of an Explicitly Defined Template Function
Explicit definition has the same effect on template overloading resolution as
explicit declaration (See Overloading Resolution for Template Functions for
more information.) If a template function is explicitly defined for:
int approximate(double a, double b) { /* ... */ }
then a call of:
double a=3.54;
float b=3.5;
approximate(a,b);
resolves in a call to approximate(double a, double b) and variable b is
converted to type double.
Related Information
Function Templates
Overloading Resolution for Template Functions
Function Template Declarations and Definitions
Functions
Differences between Class and Function Templates
Class Templates
ΓòÉΓòÉΓòÉ <hidden> Example of an Explicitly Defined Template Function ΓòÉΓòÉΓòÉ
The following explicitly defined template function compares two strings and
returns a value indicating whether more than 5% of the characters differ
between the two strings:
#include <string.h>
int approximate(char *first, char *second)
{
if (strcmp(first,second) == 0)
return 1; // strings are identical
double difct=0;
int maxlen=0;
if (strlen(first)>strlen(second))
maxlen=strlen(first);
else maxlen=strlen(second);
for (int i=0; i<=maxlen ; ++i)
if ( first[i] != second[i] ) difct++;
return int((difct / maxlen) <= .05 );
}
Given this definition, the function call: approximate("String A","String B");
invokes the explicitly defined function above, and no template function is
generated.
ΓòÉΓòÉΓòÉ 15.4.3. Function Template Declarations and Definitions ΓòÉΓòÉΓòÉ
When a template function is defined explicitly within a compilation unit, this
definition is used in preference to any instantiation from the function
template. For example, if one compilation unit contains the code:
#include <iostream.h>
template <class T> T f(T i) {return i+1;}
void main()
{
cout << f(2) << endl;
}
and another contains:
int f(int i) {return i+2;}
when compiled and run, the program prints the number 4 to standard output,
indicating that the explicitly defined function was used to resolve the call to
f().
Each template, whether of a class or of a function, must be defined at most
once within a compilation unit. The same applies to an explicitly defined
template class or function. Function templates and class templates can be
declared many times.
A template class is considered declared if its name is used. A template
function is considered declared if any of the following applies:
A function whose name matches a function template's name is declared, and
an appropriate template function can be generated.
A function whose name matches a function template's name is called, and
an appropriate template function can be generated.
A function whose name matches a function template's name is called, and
the template function has been explicitly defined.
The address of a template function is taken in such a way that
instantiation can occur. This means the pointer to function must supply a
return type and argument types that can be used to instantiate the
template function.
A template function is instantiated or generated if the function is referenced
in any of the following ways, provided that function is not explicitly defined
elsewhere in the program:
The function is declared.
A call to the function is made.
The address of the function is taken.
When a template function is instantiated, the body of the function template is
compiled using the template argument list of the template class to instantiate
the template arguments. Any errors in the function definition are flagged at
this time. If a template function is never generated from a function template,
it is not compiled. In this case, some errors in the function definition might
not be flagged by the compiler.
Related Information
Function Templates
Overloading Resolution for Template Functions
Explicitly Defined Template Functions
Functions
Differences between Class and Function Templates
Class Templates
ΓòÉΓòÉΓòÉ 15.5. Differences between Class and Function Templates ΓòÉΓòÉΓòÉ
The name of a template class is a compound name consisting of the template name
and the full template argument list enclosed in angle braces. Any references to
a template class must use this complete name. For example:
template <class T, int range> class ex
{
T a;
int r;
// ...
};
//...
ex<double,20> obj1; // valid
ex<double> obj2; // error
ex obj3; // error
C++ requires this explicit naming convention to ensure that the appropriate
class can be generated.
A template function, on the other hand, has the name of its function template
and the particular function chosen to resolve a given template function call is
determined by the type of the calling arguments. In the following example, the
call min(a,b) is effectively a call to min(int a, int b), and the call min(af,
bf) is effectively a call to min(float a, float b):
// This example illustrates a template function.
template<class T> T min(T a, T b)
{
if (a < b)
return a;
else
return b;
}
void main()
{
int a = 0;
int b = 2;
float af = 3.1;
float bf = 2.9;
cout << "Here is the smaller int " << min(a,b) << endl;
cout << "Here is the smaller float " << min(af, bf) << endl;
}
Related Information
Class Templates
Function Templates
C++ Classes
Functions
ΓòÉΓòÉΓòÉ 15.6. Member Function Templates ΓòÉΓòÉΓòÉ
In Function Templates, a function template was defined outside of any template
class. However, functions in C++ are often member functions of a class. If you
want to create a class template and a set of function templates to go with that
class template, you do not have to create the function templates explicitly, as
long as the function definitions are contained within the class template. Any
member function (inlined or noninlined) declared within a class template is
implicitly a function template. When a template class is declared, it
implicitly generates template functions for each function defined in the class
template.
You can define template member functions three ways:
1. Explicitly at file scope for each type used to instantiate the template
class.
2. At file scope with the template arguments.
3. Inlined in the class template itself.
Examples of Defining Template Member Functions
Member function templates are used to instantiate any functions that are not
explicitly generated. If you have both a member function template and an
explicit definition, the explicit definition is used.
The template argument is not used in a constructor name. For example:
template<class L> class Key
{
Key(); // default constructor
Key( L ); // constructor taking L by value
Key<L>( L ); // error, <L> implicit within class template
};
The declaration Key<L>(L) is an error because the constructor does not use the
template argument. Assuming this class template was corrected by removing the
offending line, you can define a function template for the class template's
constructor:
// Constructor contained in function template:
template<class L>
Key<L>::Key(int) { /* ... */ }
// valid, constructor template argument assumed template<class L>
Key<L>::Key<L>(int) { /* ... */ }
/* error, constructor template argument <L> implicit
in class template argument */
A template function name does not include the template argument. The template
argument does, however, appear in the template class name if a member function
of a template class is defined or declared outside of the class template. The
definition: Key<L>::Key(int) { /* ... */ } is valid because Key<L> (with
template argument) refers to the class, while Key(int) { /* ... */ } refers to
the member function.
Related Information
Class Templates
Function Templates
C++ Class Members and Friends
ΓòÉΓòÉΓòÉ <hidden> Examples of Defining Template Member Functions ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following three examples illustrate the three ways to define template
member functions:
Method 1
*
************************************************************************/
template <class T> class key
{
public:
void f(T);
};
void key<char>::f(char) { /* ... */ }
void key<int>::f(int ) { /* ... */ }
void main()
{
int i = 9;
key< int> keyobj;
keyobj.f(i);
}
/************************************************************************
*
Method 2
*
************************************************************************/
template <class T> class key
{
public:
void f(T);
};
template <class T> void key <T>::f(T) { /* ... */ }
void main()
{
int i = 9;
key< int> keyobj;
keyobj.f(i);
}
/************************************************************************
*
Method 3
*
************************************************************************/
template <class T> class key
{
public:
void f(T) { /* ... */ }
};
void main()
{
int i = 9;
key< int> keyobj;
keyobj.f(i);
}
ΓòÉΓòÉΓòÉ 15.7. Friends and Templates ΓòÉΓòÉΓòÉ
A friend function can be declared in a class template either as a single
function shared by all classes created by the template or as a template
function that varies from class to class within the class template. For
example:
template<class T> class portfolio
{
//...
friend void taxes();
friend void transact(T);
friend portfolio<T>* invest(portfolio<T>*);
friend portfolio* divest(portfolio*); //error
// ...
};
In this example, each declaration has the following characteristics:
taxes()
is a single function that can access private and protected members of any
template class generated by the class template. Note that taxes() is not a
template function.
transact(T)
is a function template that declares a distinct function for each class
generated by the class template. The only private and protected members
that can be accessed by functions generated from this template are the
private and protected members of their template class.
invest(portfolio<T>*)
is a function template whose return and argument types are pointers to
objects of type portfolio<T>. Each class generated by the class template
will have a friend function of this name, and each such function will have
a pointer to an object of its own class as both its return type and its
argument type.
divest(portfolio*)
is an error because portfolio* attempts to point to a class template. A
pointer to a class template is undefined and produces an error. This
statement can be corrected by using the syntax of the invest() function
template instead.
Because all friend functions in this example are declared but not defined, you
could create a set of function templates to define those functions that are
implicitly template functions (that is, all the valid functions except
taxes()). The function templates would then be used to instantiate the
template functions as required.
Related Information
Friends
Class Templates
Function Templates
C++ Classes
Functions
ΓòÉΓòÉΓòÉ 15.8. Static Data Members and Templates ΓòÉΓòÉΓòÉ
A static declaration within a class template declares a static data member for
each template class generated from the template. The static declaration can be
of template argument type or of any defined type.
Like member function templates, you can explicitly define a static data member
of a template class at file scope for each type used to instantiate a template
class. For example:
template <class T> class key
{
public:
static T x;
};
int key<int>::x;
char key<char>::x;
void main()
{
key<int>::x = 0;
}
You can also define a static data member of a template class using a template
definition at file scope. For example:
template <class T> class key
{
public:
static T x;
};
template <class T> T key<T> ::x; // template definition
void main()
{
key<int>::x = 0;
}
When you instantiate a template class, you must have either an explicit
definition or a template definition for each static data member, but not both.
Example of Static Data Members in Templates
Related Information
Static Members
Class Templates
Explicitly Defined Template Classes
Function Templates
Explicitly Defined Template Functions
ΓòÉΓòÉΓòÉ <hidden> Example of Static Data Members in Templates ΓòÉΓòÉΓòÉ
In the following example:
template<class L> class Key
{
static L k;
static L* kptr;
static int length;
// ...
}
The definitions of static variables and objects must be instantiated at file
scope. If the classes Key<int> and Key<double> are instantiated from this
template, and no template definitions exist, the following static data members
must be explicitly defined at file scope, or an error occurs:
int Key<int>::k, Key<int>::length, Key<double>::length;
int* Key<int>::kptr;
double Key<double>::k;
double* Key<double>::kptr = 0;
ΓòÉΓòÉΓòÉ 16. C++ Exception Handling ΓòÉΓòÉΓòÉ
This chapter describes the VisualAge C++ implementation of C++ exception
handling. It discusses:
Formal and Informal Exception Handling
Using Exception Handling
Transferring Control
Constructors and Destructors in Exception Handling
Exception Specifications
unexpected() and terminate() Functions
Note: C++ exception handling is not the same as OS/2 exception handling. A
C++ exception exists only within the C++ language. An OS/2 exception is
generated by the operating system, and can be used by the VisualAge C++
library to generate a signal. In this section, the term exception
refers to a C++ exception.
OS/2 exception handling is described in detail in the IBM VisualAge C++ for
OS/2 User's Guide and Reference.
ΓòÉΓòÉΓòÉ 16.1. C++ Exception Handling Overview ΓòÉΓòÉΓòÉ
Exception handling provides a way for a function that encounters an unusual
situation to throw an exception and pass control to a direct or indirect caller
of that function. The caller may or may not be able to handle the exception.
Code that intercepts an exception is called a handler. Regardless of whether or
not the caller can handle an exception, it may rethrow the exception so it can
be intercepted by another handler.
C++ provides three language constructs to implement exception handling:
Try blocks
Catch blocks
Throw expressions
Within a function, any unusual situation can be flagged with a throw
expression. The throw expression is of type void. Your program can throw an
object to pass information back to the caller. Any object can be thrown,
including the object that caused the exception or an object constructed when
the exception occurred.
A throw expression, or a call to a function that may throw an exception,
should be enclosed within a try block. If the called function throws an
exception and an exception handler is defined to catch the type of the object
thrown, the exception handler is executed. In C++, a catch block implements an
exception handler. A try block must be accompanied by one or more catch
clauses, otherwise the compiler will flag it as an error.
A catch block follows immediately after a try statement or immediately after
another catch block. A catch block includes a parenthesized exception
declaration containing optional qualifiers, a type, and an optional variable
name. The declaration specifies the type of object that the exception handler
may catch. Once an exception is caught, the body of the catch block is
executed. If no handler catches an exception, the program is terminated.
Exception handling is not strictly synonymous with error handling, because the
implementation allows the passing of an exception whether or not an error
actually occurred. You can use exception handlers for things other than
handling errors. For example, you can transfer control back to the original
caller of a function. You might use this if you wanted to process the Quit key
in a program and transfer control back to the driver program when the user
types Quit. To do this exception handlers could be used to throw an object
back to the driver.
Note: C++ exception handling is not the same as OS/2 exception handling. A
C++ exception exists only within the C++ language. An OS/2 exception is
generated by the operating system, and can be used by the VisualAge C++
library to generate a signal. In this section, the term exception
refers to a C++ exception.
OS/2 exception handling is described in detail in the IBM VisualAge C++ for
OS/2 User's Guide and Reference.
Related Information
Formal and Informal Exception Handling
Using Exception Handling
Transferring Control
ΓòÉΓòÉΓòÉ 16.2. Formal and Informal Exception Handling ΓòÉΓòÉΓòÉ
While the exception handling features of C++ offer a formal mechanism for
handling exceptions (language implemented), in many situations informal
exception handling (logic implemented) is more appropriate. Generally speaking,
formal exception handling should be implemented in libraries, classes, and
functions likely to be accessed by several programs or programmers. It should
also be used in classes and functions that are repeatedly accessed within a
program but are not well-suited to handling their exceptions themselves.
Because formal exception handling is designed for exceptional circumstances, it
is not guaranteed to be efficient. Program performance is usually not affected
when you do not invoke formal exception handling, although it can inhibit some
optimizations.
Informal exception handling, in which an appropriate action is defined if an
error or exception occurs, is often more suitable for handling errors. For
example, a simple error, such as entering incorrect input, can more easily and
clearly be handled by testing the input for validity and by requesting the
input again if the original input is incorrect.
Related Information
C++ Exception Handling Overview
Using Exception Handling
Transferring Control
ΓòÉΓòÉΓòÉ 16.3. Using Exception Handling ΓòÉΓòÉΓòÉ
The three keywords designed for exception handling in C++ are try, catch, and
throw.
Syntax of Exception Handling Keywords
The steps required to implement an exception handler are:
1. Functions that are expected to be used by many programs are coded so
that, when an error is detected, an exception is thrown. The throw
expression generally throws an object. It may be created explicitly for
purposes of exception handling, or it may be the object that caused the
error to be detected. An example of throwing the problem object:
.
.
.
int input=0;
cout << "Enter a number between 1 and 10:";
cin >> input;
if (input < 1 || input >> 10);
throw(input); //throw the actual problem object
.
.
.
The following is an example of throwing an object for the purpose of
exception handling:
.
.
.
int input=0;
cout << "Enter a number between 1 and 10:;
cin >> input;
if (input < 1 || input >> 10)
throw(out_of_range_object); //throw object to tell handler
//what happened
2. Exceptions are anticipated in a caller by means of a try statement.
Function calls that you anticipate might produce an exception must be
enclosed in braces and preceded by the keyword try.
3. Immediately following the try block, you must code one or more catch
blocks. Each catch block identifies what type or class of objects it can
catch:
a. If the object thrown matches the type of a catch expression, control
passes to that catch block.
b. If the object thrown does not match the first catch block,
subsequent catch blocks are searched for a matching type.
c. If no match is found, the search continues in all enclosing try
blocks and then in the code that called the current function.
d. If no match is found after all try blocks are searched, a call to
terminate() is made. For information on the default handlers of
uncaught exceptions, see unexpected() and terminate() Functions.
Notes:
Any object can be thrown if it can be copied and destroyed in the
function from which the throw occurs.
Exceptions should never be thrown from a C language signal handler. The
result is undefined, and can cause program termination.
A catch argument causes an error if it is a value argument, and a copy of it
cannot be generated. Similarly, a throw expression causes an error if a copy
of the value of the expression being thrown cannot be generated.
Example of an Incorrect catch Argument
Related Information
C++ Exception Handling Overview
Formal and Informal Exception Handling
Transferring Control
Exception Specifications
unexpected() and terminate() Functions
ΓòÉΓòÉΓòÉ <hidden> Syntax of Exception Handling Keywords ΓòÉΓòÉΓòÉ
The syntax for the try and catch keywords is:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇtryΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓöÇstatementΓöÇΓö┤ΓöÇΓöÇ}ΓöÇΓöÇcatchΓöÇΓöÇ(ΓöÇΓöÇ>
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>ΓöÇΓöÇΓö¼ΓöÇ. . .ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ)ΓöÇΓöÇ{ΓöÇΓöÇΓöÇΓöÇstatementΓöÇΓö┤ΓöÇΓöÇ}ΓöÇΓöÇ><
Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé Γöé
ΓööΓöÇΓöÇΓöÇtype_specifierΓöÇΓö┤ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ
Γö£ΓöÇdeclaratorΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓööΓöÇabstract_declaratorΓöÇΓöÿ
The syntax for the throw keyword is:
>>ΓöÇΓöÇthrowΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇ><
ΓööΓöÇassignment_expressionΓöÇΓöÿ
ΓòÉΓòÉΓòÉ <hidden> Example of an Incorrect catch Argument ΓòÉΓòÉΓòÉ
A catch argument causes an error if it is a value argument, and a copy of it
cannot be generated. For example:
class B {
public:
B();
B(B&);
};
// the following catch block will cause an error
//
catch(const B x)
{
// ...
}
The catch block causes an error because the compiler does not know the type of
the object thrown at compile time. It assumes that the type of the thrown
object is the same as the type of the catch argument. In the above example, the
thrown object is assumed to be of type const B. The compiler uses a copy
constructor on the thrown argument to create the catch argument. Because there
is no copy constructor for class B that accepts const B as an input argument,
the compiler cannot perform the construction and an error occurs.
ΓòÉΓòÉΓòÉ 16.4. Transferring Control ΓòÉΓòÉΓòÉ
C++ implements the termination model of exception handling. In the termination
model, when an exception is thrown, control never returns to the throw point.
The throw point is the point in program execution where the exception occurred.
C++ exception handling does not implement the resumption model of exception
handling, which allows an exception handler to correct the exception and then
return to the throw point.
When an exception is thrown, control is passed out of the throw expression and
out of the try block that anticipated the exception. Control is passed to the
catch block whose exception type matches the object thrown. The catch block
handles the exception as appropriate. If the catch block ends normally, the
flow of control passes over all subsequent catch blocks.
When an exception is not thrown from within a try block, the flow of control
continues normally through the block, and passes over all catch blocks
following the try block.
An exception handler cannot return control to the source of the error by using
the return statement. A return issued in this context returns from the function
containing the catch block.
If an exception is thrown and no try block is active, or if a try block is
active and no catch block exception declaration matches the object thrown, a
call to terminate() is issued. A call to terminate() in turn calls abort() to
terminate the program. The abort() C library function is defined in the
standard header file <stdlib.h>.
Example of Basic Exception Handling
Related Information
Catching Exceptions
Nested Try Blocks
Rethrowing an Exception
Using a Conditional Expression in a Throw Expression
C++ Exception Handling Overview
Using Exception Handling
Exception Specifications
unexpected() and terminate() Functions
abort - Stop a Program
return
ΓòÉΓòÉΓòÉ <hidden> Example of Basic Exception Handling ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example illustrates the basic use of try, catch, and throw. The
program prompts for numerical input and determines the input's reciprocal.
Before it attempts to print the reciprocal to standard output, it checks that
the input value is nonzero, to avoid a division by zero. If the input is zero,
an exception is thrown, and the catch block catches the exception. If the input
is nonzero, the reciprocal is printed to standard output.
*
************************************************************************/
// This example illustrates the basic use of
// try, catch, and throw.
#include <iostream.h>
#include <stdlib.h>
class IsZero { /* ... */ };
void ZeroCheck( int i )
{
if (i==0)
throw IsZero();
}
void main()
{
double a;
cout << "Enter a number: ";
cin >> a;
try
{
ZeroCheck( a );
cout << "Reciprocal is " << 1.0/a << endl;
}
catch ( IsZero )
{
cout << "Zero input is not valid" << endl;
exit(1);
}
exit(0);
}
/************************************************************************
*
This example could have been coded more efficiently by using informal exception
handling. However, it provides a simple illustration of formal exception
handling.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 16.4.1. Catching Exceptions ΓòÉΓòÉΓòÉ
You can declare a handler to catch many types of exceptions. The allowable
objects that a function can catch are declared in the parentheses following the
catch keyword (the catch argument). You can catch objects of the fundamental
types, base and derived class objects, references, and pointers to all of these
types. You can also catch const and volatile types.
You can also use the catch(...) form of the handler to catch all thrown
exceptions that have not been caught by a previous catch block. The ellipsis in
the catch argument indicates that any exception thrown can be handled by this
handler.
If an exception is caught by a catch(...) block, there is no direct way to
access the object thrown. Information about an exception caught by catch(...)
is very limited. You can declare an optional variable name if you want to
access the thrown object in the catch block.
A catch block can only catch accessible objects. The object caught must have an
accessible copy constructor. For more information on access, see Member Access;
on copy constructors, see Copy by Initialization.
An argument in the catch argument of a handler matches an argument in the
expression of the throw expression (throw argument) if any of the following
conditions is met:
The catch argument type matches the type of the thrown object.
The catch argument is a public base class of the thrown class object.
The catch specifies a pointer type, and the thrown object is a pointer
type that can be converted to the pointer type of the catch argument by
standard pointer conversion. Pointer conversion is described on page
Pointer Conversions.
Note: If the type of the thrown object is const or volatile, the catch
argument must also be a const or volatile for a match to occur. However, a
const, volatile, or reference type catch argument can match a nonconstant,
nonvolatile, or nonreference object type. A nonreference catch argument type
matches a reference to an object of the same type.
Always place a catch block that catches a derived class before a catch block
that catches the base class of that derived class (following a try block). If
a catch block for objects of a base class is followed by a catch block for
objects of a derived class of that base class, the latter block is flagged as
an error.
A catch block of the form catch(...) must be the last catch block following a
try block or an error occurs. This placement ensures that the catch(...) block
does not prevent more specific catch blocks from catching exceptions intended
for them.
Related Information
C++ Exception Handling Overview
Using Exception Handling
Transferring Control
Exception Specifications
volatile and const Qualifiers
Nested Try Blocks
Rethrowing an Exception
ΓòÉΓòÉΓòÉ 16.4.2. Nested Try Blocks ΓòÉΓòÉΓòÉ
When try blocks are nested and a throw occurs in a function called by an inner
try block, control is transferred outward through the nested try blocks until
the first catch block is found whose argument matches the argument of the throw
expression.
For example:
try
{
func1();
try
{
func2();
}
catch (spec_err) { /* ... */ }
func3();
}
catch (type_err) { /* ... */ }
// if no throw is issued, control resumes here.
In the above example, if spec_err is thrown within the inner try block (in this
case, from func2()), the exception is caught by the inner catch block, and,
assuming this catch block does not transfer control, func3() is called. If
spec_err is thrown after the inner try block (for instance, by func3()), it is
not caught and the function terminate() is called.
If the exception thrown from func2() in the inner try block is type_err, the
program skips out of both try blocks to the second catch block without invoking
func3(), because no appropriate catch block exists following the inner try
block. If the entire try block in the example is in a function that has a throw
list and does not include spec_err on its throw list, unexpected() is called.
You can also nest a try block within a catch block.
Related Information
Catching Exceptions
Transferring Control
Using Exception Handling
Rethrowing an Exception
Exception Specifications
unexpected() and terminate() Functions
ΓòÉΓòÉΓòÉ 16.4.3. Rethrowing an Exception ΓòÉΓòÉΓòÉ
If a catch block cannot handle the particular exception it has caught, you can
rethrow the exception. The rethrow expression (throw with no argument) causes
the originally thrown object to be rethrown.
Because the exception has already been caught at the scope in which the rethrow
expression occurs, it is rethrown out to the next dynamically enclosing try
block. Therefore, it cannot be handled by catch blocks at the scope in which
the rethrow expression occurred. Any catch blocks following the dynamically
enclosing try block have an opportunity to catch the exception.
Example of Rethrowing an Exception
The rethrow expression can be caught by any catch whose argument matches the
argument of the exception originally thrown.
Related Information
Catching Exceptions
Transferring Control
Using Exception Handling
Nested Try Blocks
Using a Conditional Expression in a Throw Expression
Exception Specifications
ΓòÉΓòÉΓòÉ <hidden> Example of Rethrowing an Exception ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, catch(FileIO) catches any object of type FileIO and
any objects that are public base classes of the FileIO class. It then checks
for those exceptions it can handle. For any exception it cannot handle, it
issues a rethrow expression to rethrow the exception and allow another handler
in a dynamically enclosing try block to handle the exception.
*
************************************************************************/
// This example illustrates rethrowing an exception.
#include <iostream.h>
class FileIO
{
public:
int notfound;
int endfile;
FileIO(); // initialize data members
// the following member functions throw an exception
// if an input error occurs
void advance(int x);
void clear();
void put(int x, int y);
};
// .
// .
// .
void f()
{
FileIO fio;
try
{
// call member functions of FileIO class
fio.advance (1);
fio.clear();
fio.put(1,-1);
}
catch(FileIO fexc)
{
if (fexc.notfound)
cout << "File not Found" << endl;
else if (fexc.endfile)
cout << "End of File" << endl;
else
throw; // rethrow to outer handler
}
catch(...) { /* ... */ } // catch other exceptions
}
main()
{
try
{
f();
}
catch(FileIO) { cout << "Outer Handler" << endl; }
}
/************************************************************************
*
The rethrow expression can be caught by any catch whose argument matches the
argument of the exception originally thrown. Note that, in this example, the
catch(...) will not catch the rethrow expression because, when the rethrow
expression is issued, control passes out of the scope of the function f() into
the next dynamically enclosing block.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 16.4.4. Using a Conditional Expression in a Throw Expression ΓòÉΓòÉΓòÉ
You can use a conditional expression as a throw expression. as shown in the
following example:
// This example illustrates a conditional expresion
// used as a throw expression.
#include <iostream.h>
void main() {
int doit = 1;
int dont = 0;
float f = 8.9;
int i = 7;
int j = 6;
try { throw(doit ? i : f); }
catch (int x)
{
cout << "Caught int " << x << endl;
}
catch (float x)
{
cout << "Caught float " << x << endl;
}
catch (double x)
{
cout << "Caught double " << x << endl;
}
catch (...)
{
cout << "Caught something " << endl;
}
}
This example produces the following output because j is of type int:
Caught float 7
At first glance, it looks as if the block that catches integer values should do
the catch, but i is converted to a float value in the try block because it is
in a conditional expression with the float value f. If the try block in the
example is replaced with the following try block:
try { throw doit ? i : j; }
The following output is produced:
Caught int 7
Related Information
Catching Exceptions
Transferring Control
Using Exception Handling
Nested Try Blocks
Rethrowing an Exception
Exception Specifications
ΓòÉΓòÉΓòÉ 16.5. Constructors and Destructors in Exception Handling ΓòÉΓòÉΓòÉ
When an exception is thrown and control passes to a catch block following a try
block, destructors are called for all automatic objects constructed since the
beginning of the try block directly associated with that catch block. If an
exception is thrown during construction of an object consisting of subobjects
or array elements, destructors are only called for those subobjects or array
elements successfully constructed before the exception was thrown. A destructor
for a local static object will only be called if the object was successfully
constructed.
For more information on constructors and destructors, see Constructors and
Destructors Overview.
If a destructor detects an exception and issues a throw, the exception can be
caught if the caller of the destructor was contained within a try block and an
appropriate catch is coded.
If an exception is thrown by a function called from an inner try block, but
caught by an outer try block (because the inner try block did not have an
appropriate handler), all objects constructed within both the outer and all
inner try blocks are destroyed. If the thrown object has a destructor, the
destructor is not called until the exception is caught and handled.
Because a throw expression throws an object and a catch statement can catch an
object, the object thrown enables error-related information to be transferred
from the point at which an exception is detected to the exception's handler. If
you throw an object with a constructor, you can construct an object that
contains information relevant to the catch expression. The catch block can then
access information provided by the thrown object.
Example of Using Constructors in Exception Handling
Exception handling can be used in conjunction with constructors and destructors
to provide resource management that ensures that all locked resources are
unlocked when an exception is thrown.
Example of Managing Resources with Constructors and Destructors
Related Information
Constructors and Destructors Overview
C++ Exception Handling Overview
Using Exception Handling
Transferring Control
Exception Specifications
ΓòÉΓòÉΓòÉ <hidden> Managing Resources with Constructors and Destructors ΓòÉΓòÉΓòÉ
/************************************************************************
*
Exception handling can be used in conjunction with constructors and destructors
to provide resource management that ensures that all locked resources are
unlocked when an exception is thrown. For example:
*
************************************************************************/
class data
{
public:
void lock(); // prevent other users from
// changing the object
void unlock(); // allow other users to change
// the object
};
void q(data&), bar(data&);
// ...
main()
{
data important;
important.lock();
q(important);
bar(important);
important.unlock();
}
/************************************************************************
*
If q() or bar() throw an exception, important.unlock() will not be called and
the data will stay locked. This problem can be corrected by using a helper
class to write an exception-aware program for resource management.
*
************************************************************************/
class data
{
public:
void lock(); // prevent other users from
// changing the object
void unlock(); // allow other users to change
// the object
};
class locked_data // helper class
{
data& real_data;
public:
locked_data(data& d) : real_data(d)
{real_data.lock();}
~locked_data() {real_data.unlock();}
};
void q(data&), bar(data&);
// ...
main()
{
data important;
locked_data my_lock(important);
q(important);
bar(important);
}
/************************************************************************
*
In this case, if q() or bar() throws an exception, the destructor for my_lock
will be called, and the data will be unlocked.
*
************************************************************************/
ΓòÉΓòÉΓòÉ <hidden> Example of Using Constructors in Exception Handling ΓòÉΓòÉΓòÉ
/************************************************************************
*
In the following example, an object of class DivideByZero is thrown by the
function divide(). The constructor copies the string "Division by zero" into
the char array errname. Because DivideByZero is a derived class of class
Matherr, the catch block for Matherr catches the thrown exception. The catch
block can then access information provided by the thrown object, in this case
the text of an error message.
*
************************************************************************/
// This example illustrates constructors and
// destructors in exception handling.
#include <string.h> // needed for strcpy
#include <iostream.h>
class Matherr { public: char errname[30]; };
class DivideByZero : public Matherr
{
public:
DivideByZero() {strcpy (errname, "Division by zero");}
};
double divide(double a, double b)
{
if (b == 0) throw DivideByZero();
return a/b;
}
void main()
{
double a=7,b=0;
try {divide (a,b);}
catch (Matherr xx)
{
cout << xx.errname << endl;
}
}
ΓòÉΓòÉΓòÉ 16.6. Exception Specifications ΓòÉΓòÉΓòÉ
C++ provides a mechanism to ensure that a given function is limited to throwing
only a specified list of exceptions. An exception specification at the
beginning of any function acts as a guarantee to the function's caller that the
function will not directly or indirectly throw any exception not contained in
the exception specification. For example, a function:
void translate() throw(unknown_word,bad_grammar) { /* ... */ }
explicitly states that it will not throw any exception other than unknown_word
or bad_grammar. The function translate() must handle any exceptions thrown by
functions it might call, unless those exceptions are specified in the exception
specification of translate(). If an exception is thrown by a function called by
translate() and the exception is not handled by translate() or contained in the
exception specification of translate(), unexpected() is called.
Syntax of an Exception Specification
If an exception is thrown from a function that has not specified the thrown
exception in its exception specification, the result is a call to the function
unexpected(), which is discussed in unexpected() and terminate() Functions.
A function with an empty throw() specification guarantees that the function
will not throw any exceptions. A function without an exception specification
allows any object to be thrown from the function.
The compiler does not prevent an exception specification from defining a more
limited set of valid exceptions than the set of exceptions the function may
actually throw. Such an error is detected only at run time, and only if the
unspecified exception is thrown.
Example of Throwing an Unspecified Exception
If a function with an exception specification calls a subfunction with a less
restrictive exception specification (one that contains more objects than the
calling function's exception specification), any thrown objects from within the
subfunction that are not handled by the subfunction, and that are not part of
the outer function's specification list, must be handled within the outer
function. If the outer function fails to handle an exception not in its
exception specification, a call to unexpected() is made.
Related Information
unexpected() and terminate() Functions
C++ Exception Handling Overview
Using Exception Handling
Transferring Control
ΓòÉΓòÉΓòÉ <hidden> Syntax of an Exception Specification ΓòÉΓòÉΓòÉ
The syntax of the exception specification is:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé
>>ΓöÇΓöÇthrowΓöÇΓöÇ(ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ)ΓöÇΓöÇ><
ΓööΓöÇtypeΓöÇΓöÿ
The syntax of a function definition that includes an exception specification
is:
ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ ΓöîΓöÇ,ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé Γöé
>>ΓöÇΓöÇreturn_typeΓöÇΓöÇfunction_nameΓöÇΓöÇ(ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ)ΓöÇΓöÇthrowΓöÇΓöÇ(ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼Γö┤ΓöÇΓöÇ>
ΓööΓöÇargumentΓöÇΓöÿ ΓööΓöÇtypeΓöÇΓöÿ
>ΓöÇΓöÇ)ΓöÇΓöÇ{ΓöÇΓöÇfunction_bodyΓöÇΓöÇ}ΓöÇΓöÇ><
ΓòÉΓòÉΓòÉ <hidden> Example of Throwing an Unspecified Exception ΓòÉΓòÉΓòÉ
In the following example, NameTooShort is thrown from within a function that
explicitly states that it will only throw NameTooLong. This is a valid
function, although at run time, if NameTooShort is thrown, a call to
unexpected() will be made.
#include <string.h> // needed for strlen
class NameTooLong {};
class NameTooShort {};
void check(char* fname) throw (NameTooLong)
{
if ( strlen(fname)<4 ) throw NameTooShort();
}
ΓòÉΓòÉΓòÉ 16.7. unexpected() and terminate() Functions ΓòÉΓòÉΓòÉ
Not all thrown errors can be caught and successfully dealt with by a catch
block. In some situations, the best way to handle an exception is to terminate
the program. Two special library functions are implemented in C++ to process
exceptions not properly handled by catch blocks or exceptions thrown outside of
a valid try block. These functions are unexpected() and terminate().
When a function with an exception specification throws an exception that is not
listed in its exception specification, the function void unexpected() is
called. Next, unexpected() calls a function specified by the set_unexpected()
function. By default, unexpected() calls the function terminate().
In some cases, the exception handling mechanism fails and a call to void
terminate() is made. This terminate() call occurs in any of the following
situations:
When terminate() is explicitly called
When no catch can be matched to a thrown object
When the stack becomes corrupted during the exception-handling process
When a system defined unexpected() is called
The terminate() function calls a function specified by the set_terminate()
function. By default, terminate calls abort(), which exits from the program.
A terminate function cannot return to its caller, either by using return or by
throwing an exception.
Example of Using the Exception Handling Functions
Related Information
set_unexpected() and set_terminate() Functions
C++ Exception Handling Overview
Exception Specifications
Using Exception Handling
Transferring Control
abort - Stop a Program
ΓòÉΓòÉΓòÉ 16.8. set_unexpected() and set_terminate() Functions ΓòÉΓòÉΓòÉ
The function unexpected(), when invoked, calls the function most recently
supplied as an argument to set_unexpected(). If set_unexpected() has not yet
been called, unexpected() calls terminate().
The function terminate(), when invoked, calls the function most recently
supplied as an argument to set_terminate(). If set_terminate() has not yet been
called, terminate() calls abort(), which ends the program.
You can use set_unexpected() and set_terminate() to register functions you
define to be called by unexpected() and terminate(). set_unexpected() and
set_terminate() are included in the standard header files <unexpect.h>. and
<terminat.h>. Each of these functions has as its return type and its argument
type a pointer to function with a void return type and no arguments. The
pointer to function you supply as the argument becomes the function called by
the corresponding special function: the argument to set_unexpected() becomes
the function called by unexpected(), and the argument to set_terminate()
becomes the function called by terminate(). Both set_unexpected() and
set_terminate() return a pointer to the function that was previously called by
their respective special functions (unexpected() and terminate()). By saving
the return values, you can restore the original special functions later so that
unexpected() and terminate() will once again call terminate() and abort().
If you use set_terminate() to register your own function, the final action of
that program should be to exit from the program. If you attempt to return from
the function called by terminate(), abort() is called instead and the program
ends.
Note: Providing a call to longjmp() inside a user-defined terminate function
can transfer execution control to some other desired point. When you call
longjmp, objects existing at the time of a setjmp call will still exist, but
some objects constructed after the call to setjmp might not be destructed.
Example of Using the Exception Handling Functions
Related Information
unexpected() and terminate() Functions
C++ Exception Handling Overview
Using Exception Handling
Transferring Control
abort - Stop a Program
setjmp - Preserve Stack Environment
longjmp - Restore Stack Environment
ΓòÉΓòÉΓòÉ 16.9. _set_mt_unexpected() and _set_mt_terminate() Functions ΓòÉΓòÉΓòÉ
The function _set_mt_terminate() registers a terminate handler exactly the same
way set_terminate() does, except that it only affects the current thread. When
a terminate function needs to be called, the code first checks to see if a
thread terminate handler has been registered. If so, the thread terminate
handler is called. If not, the global terminate handler (the one registered
with set_terminate()) is called.
The function _set_mt_unexpected() registers an unexpected handler exactly the
same way set_unexpected() does, except that it only affects the current thread.
When an unexpected handler needs to be called, the code first checks to see if
a thread unexpected handler has been registered. If so, the thread unexpected
handler is called. If not, the global unexpected handler (the one registered
with set_unexpected()) is called.
Related Information
unexpected() and terminate() Functions
set_unexpected() and set_terminate() Functions
C++ Exception Handling Overview
Using Exception Handling
Transferring Control
Exception Specifications
ΓòÉΓòÉΓòÉ 16.10. Example of Using the Exception Handling Functions ΓòÉΓòÉΓòÉ
/************************************************************************
*
The following example shows the flow of control and special functions used in
exception handling:
*
************************************************************************/
#include <terminat.h>
#include <unexpect.h>
#include <iostream.h>
class X { /* ... */ };
class Y { /* ... */ };
class A { /* ... */ };
// pfv type is pointer to function returning void
typedef void (*pfv)();
void my_terminate()
{ cout << "Call to my terminate" << endl; }
void my_unexpected()
{ cout << "Call to my unexpected" << endl; }
void f() throw(X,Y) // f() is permitted to throw objects of class
// types X and Y only
{
A aobj;
throw(aobj); // error, f() throws a class A object
}
main()
{
pfv old_term = set_terminate(my_terminate);
pfv old_unex = set_unexpected(my_unexpected);
try{ f(); }
catch(X) { /* ... */ }
catch(Y) { /* ... */ }
catch (...) { /* ... */ }
set_unexpected(old_unex);
try { f();}
catch(X) { /* ... */ }
catch(Y) { /* ... */ }
catch (...) { /* ... */ }
}
/************************************************************************
*
At run time, this program behaves as follows:
1. The call to set_terminate() assigns to old_term the address of the
function last passed to set_terminate() when set_terminate() was
previously called.
2. The call to set_unexpected() assigns to old_unex the address of the
function last passed to set_unexpected() when set_unexpected() was
previously called.
3. Within a try block, function f() is called. Because f() throws an
unexpected exception, a call to unexpected() is made. unexpected() in
turn calls my_unexpected(), which prints a message to standard output and
returns.
4. The second call to set_unexpected() replaces the user-defined function
my_unexpected() with the saved pointer to the original function
(terminate()) called by unexpected().
5. Within a second try block, function f() is called once more. Because f()
throws an unexpected exception, a call to unexpected() is again made.
unexpected() automatically calls terminate(), which calls the function
my_terminate().
6. my_terminate() displays a message. It returns, and the system calls
abort(), which terminates the program.
At run time, the following information is displayed, and the program ends:
Call to my_unexpected
Call to my_terminate
Note: The catch blocks following the try block are not entered, because the
exception was handled by my_unexpected() as an unexpected throw, not as a
valid exception.
*
************************************************************************/
ΓòÉΓòÉΓòÉ 17. C and C++ Compatibility ΓòÉΓòÉΓòÉ
The differences between ISO/ANSI C and C++ fall into two categories:
Constructs found in C++ but not in ISO/ANSI C
Constructs found in both C++ and ISO/ANSI C, but treated differently in
the two languages
C++ contains many constructs that are not found in ISO/ANSI C:
Single line comments beginning with //
Scope operator (::)
Free store management using the operators new and delete
Linkage specification for functions
Reference types
Default arguments for functions
Inline functions
Classes
Anonymous unions
Overloaded operators and functions
Class templates and function templates
Exception handling
Note: The VisualAge C++ compiler also supports anonymous unions in C, but the
implementation is slightly different from C++. For more information, see
Anonymous Unions in C.
ΓòÉΓòÉΓòÉ 17.1. Constructs Treated Differently in C and C++ ΓòÉΓòÉΓòÉ
Because C++ is based on ISO/ANSI C, the two languages have many constructs in
common. The use of some of these shared constructs differs, as shown here.
Character Array Initialization
Character Constants
Class and typedef Names
Class and Scope Declarations
const Object Initialization
Definitions
Definitions within Return or Argument Types
Enumerator Type
Enumeration Type
Function Declarations
Functions with an Empty Argument List
Global Constant Linkage
Jump Statements
Keywords
main() Recursion
Names of Nested Classes
Pointers to void
Prototype Declarations
Return without Declared Value
__STDC__ Macro
typedefs in Class Declarations
Related Information
C and C++ Compatibility
ΓòÉΓòÉΓòÉ 17.1.1. Character Array Initialization ΓòÉΓòÉΓòÉ
In C++, when you initialize character arrays, a trailing '\0' (zero of type
char) is appended to the string initializer. You cannot initialize a character
array with more initializers than there are array elements.
In ISO/ANSI C, space for the trailing '\0' can be omitted in this type of
initialization.
The following initialization, for instance, is not valid in C++:
char v[3] = "asd"; // not valid in C++, valid in ISO/ANSI C
because four elements are required. This initialization produces an error
because there is no space for the implied trailing '\0' (zero of type char).
For more information, see Arrays.
ΓòÉΓòÉΓòÉ 17.1.2. Character Constants ΓòÉΓòÉΓòÉ
A character constant has type char in C++ and int in ISO/ANSI C.
For more information, see Character Constants.
ΓòÉΓòÉΓòÉ 17.1.3. Class and typedef Names ΓòÉΓòÉΓòÉ
In C++, a class and a typedef cannot both use the same name to refer to a
different type within the same scope (unless the typedef is a synonym for the
class name). In C, a typedef name and a struct tag name declared in the same
scope can have the same name because they have different name spaces. For
example:
void main ()
{
typedef double db;
struct db; // error in C++, valid in ISO/ANSI C
typedef struct st st; // valid ISO/ANSI C and C++
}
For more information on typedef, see typedef. For information on class types,
see C++ Classes. For information on structures, see Structures.
ΓòÉΓòÉΓòÉ 17.1.4. Class and Scope Declarations ΓòÉΓòÉΓòÉ
In C++, a class declaration introduces the class name into the scope where it
is declared and hides any object, function, or other declaration of that name
in an enclosing scope. In ISO/ANSI C, an inner scope declaration of a struct
name does not hide an object or function of that name in an outer scope. For
example:
double db;
void main ()
{
struct db // hides double object db in C++
{ char* str; };
int x = sizeof(db); // size of struct in C++
// size of double in ISO/ANSI C
}
For more information, see Scope of Class Names. For general information about
scope, see Scope in C++.
ΓòÉΓòÉΓòÉ 17.1.5. const Object Initialization ΓòÉΓòÉΓòÉ
In C++, const objects must be initialized. In ISO/ANSI C, they can be left
uninitialized.
For more information, see volatile and const Qualifiers.
ΓòÉΓòÉΓòÉ 17.1.6. Definitions ΓòÉΓòÉΓòÉ
An object declaration, for example:
int i;
is a definition in C++. In ISO/ANSI C, it is a tentative definition.
In C++, a global data object must be defined only once. In ISO/ANSI C, a global
data object can be declared several times without using the extern keyword.
In C++, multiple definitions for a single variable cause an error. A C
compilation unit can contain many identical tentative definitions for a
variable.
For more information, see Declarations.
ΓòÉΓòÉΓòÉ 17.1.7. Definitions within Return or Argument Types ΓòÉΓòÉΓòÉ
In C++, types may not be defined in return or argument types. ISO/ANSI C allows
such definitions. For example, the declarations:
void print(struct X { int i;} x); // error in C++
enum count{one, two, three} counter(); // error in C++
produce errors in C++, but are valid declarations in ISO/ANSI C.
For more information, see Function Declarations and Calling Functions and
Passing Arguments.
ΓòÉΓòÉΓòÉ 17.1.8. Enumerator Type ΓòÉΓòÉΓòÉ
An enumerator has the same type as its enumeration in C++. In ISO/ANSI C, an
enumeration has type int.
For more information on enumerators, see Enumerations.
ΓòÉΓòÉΓòÉ 17.1.9. Enumeration Type ΓòÉΓòÉΓòÉ
The assignment to an object of enumeration type with a value that is not of
that enumeration type produces an error in C++. In ISO/ANSI C, an object of
enumeration type can be assigned values of any integral type.
For more information, see Enumerations.
ΓòÉΓòÉΓòÉ 17.1.10. Function Declarations ΓòÉΓòÉΓòÉ
In C++, all declarations of a function must match the unique definition of a
function. ISO/ANSI C has no such restriction.
For more information, see Function Declarations.
ΓòÉΓòÉΓòÉ 17.1.11. Functions with an Empty Argument List ΓòÉΓòÉΓòÉ
Consider the following function declaration:
int f();
In C++, this function declaration means that the function takes no arguments.
In ISO/ANSI C, it could take any number of arguments, of any type.
For more information, see Function Declarations.
ΓòÉΓòÉΓòÉ 17.1.12. Global Constant Linkage ΓòÉΓòÉΓòÉ
In C++, an object declared const has internal linkage, unless it has previously
been given external linkage. In ISO/ANSI C, it has external linkage.
For more information, see Program Linkage.
ΓòÉΓòÉΓòÉ 17.1.13. Jump Statements ΓòÉΓòÉΓòÉ
C++ does not allow you to jump over declarations containing initializations.
ISO/ANSI C does allow you to use jump statements for this purpose.
For more information, see Initializers.
ΓòÉΓòÉΓòÉ 17.1.14. Keywords ΓòÉΓòÉΓòÉ
C++ contains some additional keywords not found in ISO/ANSI C. C programs that
use these keywords as identifiers are not valid C++ programs:
catch protected
class public
delete template
friend this
inline throw
new try
operator virtual
private
For more information, see Keywords.
ΓòÉΓòÉΓòÉ 17.1.15. main() Recursion ΓòÉΓòÉΓòÉ
In C++, main() cannot be called recursively and cannot have its address taken.
ISO/ANSI C allows recursive calls and allows pointers to hold the address of
main().
For more information, see The main() Function.
ΓòÉΓòÉΓòÉ 17.1.16. Names of Nested Classes ΓòÉΓòÉΓòÉ
In C++, the name of a nested class is local to its enclosing class. In ISO/ANSI
C, the name of the nested structure belongs to the same scope as the name of
the outermost enclosing structure.
For more information, see Nested Classes.
ΓòÉΓòÉΓòÉ 17.1.17. Pointers to void ΓòÉΓòÉΓòÉ
C++ allows void pointers to be assigned only to other void pointers. In
ISO/ANSI C, a pointer to void can be assigned to a pointer of any other type
without an explicit cast.
For more information, see void Type and Pointers.
ΓòÉΓòÉΓòÉ 17.1.18. Prototype Declarations ΓòÉΓòÉΓòÉ
C++ requires full prototype declarations. ISO/ANSI C allows nonprototyped
functions.
For more information, see Function Declarator.
ΓòÉΓòÉΓòÉ 17.1.19. Return without Declared Value ΓòÉΓòÉΓòÉ
In C++, a return (either explicit or implicit) from main() that is declared to
return a value results in an error if no value is returned. A return (either
explicit or implicit) from all other functions that is declared to return a
value must return a value. In ISO/ANSI C, a function that is declared to return
a value can return with no value, with unspecified results.
For more information, see Function Return Values.
ΓòÉΓòÉΓòÉ 17.1.20. __STDC__ Macro ΓòÉΓòÉΓòÉ
The predefined macro variable __STDC__ has the integer value 0 to indicate that
C++ does not conform to ISO/ANSI C. In ISO/ANSI C, __STDC__ has the integer
value 1.
For an example of the use of __STDC__ macro, see Example of Predefined Macros
For more information on macros, see Predefined Macro Names.
ΓòÉΓòÉΓòÉ 17.1.21. typedefs in Class Declarations ΓòÉΓòÉΓòÉ
In C++, a typedef name may not be redefined in a class declaration after being
used in the declaration. ISO/ANSI C allows such a declaration. For example:
void main ()
{
typedef double db;
struct st
{
db x;
double db; // error in C++, valid in ISO/ANSI C
};
}
For more information, see typedef.
ΓòÉΓòÉΓòÉ 18. Glossary ΓòÉΓòÉΓòÉ
This is a glossary of commonly used terms in the VisualAge C++ library. It
includes definitions developed by the American National Standards Institute
(ANSI) and entries from the IBM Dictionary of Computing (ZC20-1699).
A J S
B K T
C L U
D M V
E N W
F O X
G P Y
H Q Z
I R
ΓòÉΓòÉΓòÉ <hidden> A ΓòÉΓòÉΓòÉ
abstract class
A class with at least one pure virtual function. It is a C++ class used as a
base class for other classes. The abstract class represents a concept; classes
derived from it represent implementations of the concept. You cannot have a
direct object of an abstract class. (See also base class.)
abstraction (data)
A data type with a private representation and a public set of operations. The
C++ language uses the concept of classes to implement data abstraction.
access
Determines whether or not a class member is accessible in an expression or
declaration.
access declaration
Used to restore access to members of a base class.
access resolution
The process by which the accessibility of a particular class member is
determined.
access specifiers
One of the C++ keywords: public, private, and protected.
address
A name, label, or number identifying a location in storage, a device in a
system or network, or any other data source.
aggregate
An array or a class object with no private or protected members, no
constructors, no base classes, and no virtual functions.
alignment
See boundary alignment.
anonymous union
A union without a class name. It must not be followed by a declarator.
arithmetic object
An integral object or objects having the type float, double, or long double.
array
A variable that contains an ordered group of data objects. All data items (or
elements) in an array have the same data type.
array element
A single data item in an array.
assignment conversion
A change to the form of the right operand that makes the right operand have the
same data type as the left operand.
assignment expression
An operation that stores the value of the right operand in the storage location
specified by the left operand.
associativity
The order for grouping operands with an operator (either left-to-right or
right-to-left).
ΓòÉΓòÉΓòÉ <hidden> B ΓòÉΓòÉΓòÉ
base class
A class from which other classes are derived. A base class may itself be
derived from another base class. (See also abstract class.)
binary expression
An operation containing two operands and one operator.
bit field
A member of a structure or union that contains 0 or more bits.
block statement
Any number of data definitions, declarations, and statements that appear
between the symbols { (left brace) and } (right brace).
boundary alignment
The position in main storage of a fixed-length field (such as halfword or
doubleword) on an integral boundary for that unit of information. For example,
a word boundary is a storage address evenly divisible by four.
break statement
A language control statement that contains the word break and a semicolon. It
is used to end an iterative or a switch statement by exiting from it at any
point other than the logical end. Control is passed to the first statement
after the iteration or switch statement.
buffer flush
A process that removes the contents of a buffer. After a buffer flush, the
buffer is empty.
ΓòÉΓòÉΓòÉ <hidden> C ΓòÉΓòÉΓòÉ
C library
A system library that contains common C language subroutines for file access,
string operators, character operations, memory allocation, and other functions.
C++ class library
See class library.
C++ language statement
A C++ language statement contains zero or more expressions. All C++ language
statements, except block statements, end with a ; (semicolon) symbol. A block
statement begins with a { (left brace) symbol, ends with a } (right brace)
symbol, and contains any number of statements.
C++ library
A system library that contains common C++ language subroutines for file access,
memory allocation, and other functions.
case clause
In a switch statement, a case label followed by any number of statements.
case label
The word case followed by a constant expression and a colon. When the selector
evaluates the value of the constant expression, the statements following the
case label are processed.
cast expression
A cast expression explicitly converts its operand to a specified arithmetic,
scalar, or class type.
cast operator
The cast operator is used for explicit type conversions.
catch block
A block associated with a try block that receives control when an exception
matching its argument is thrown.
char specifier
A char is a built-in data type. In C++, char, signed char, and unsigned char
are all distinct data types.
character constant
A character or an escape sequence enclosed in single quotation marks.
character variable
A data object whose value can be changed during program execution and whose
data type is char, signed char, or unsigned char.
class
A class is a user-defined data type. A class data type can contain both data
representations (data members) and functions (member functions).
class key
One of the C++ keywords: class, struct and union.
class library
A collection of C++ classes.
class member operators
Used to access class members through class objects or pointers to class
objects. They are ., ->, .*, and ->*.
class name
A unique identifier of a class type that becomes a reserved word within its
scope.
class scope
The names of class members have class scope.
class tag
See class name.
class template
A blueprint describing how a set of related classes can be constructed.
client program
A program that uses a class. The program is said to be a client of the class.
comma expression
An expression that contains two operands separated by a comma. Although the
compiler evaluates both operands, the value of the expression is the value of
the right operand. If the left operand produces a value, the compiler discards
this value. Typically, the left operand of a comma expression is used to
produce side effects.
complete class name
The complete qualification of a nested class name including all enclosing class
names.
Complex Mathematics Library
A class library that provides the facilities to manipulate complex numbers and
perform standard mathematical operations on them.
complex number
A complex number is made up of two parts: a real part and an imaginary part. A
complex number can be represented by an ordered pair (a, b ), where a is the
value of the real part and b is the value of the imaginary part. The same
complex number could also be represented as a + bi, where i is the square root
of -1.
conditional compilation statement
A preprocessor statement that causes the preprocessor to process specified
source code in the file depending on the evaluation of a specific condition.
conditional expression
A compound expression that contains a condition (the first expression), an
expression to be evaluated if the condition has a nonzero value (the second
expression), and an expression to be evaluated if the condition has the value
zero (the third expression).
const
A keyword that allows you to define a variable whose value does not change.
constant expression
An expression having a value that can be determined during compilation and that
does not change during program execution.
constructor
A special class member function that has the same name as the class. It is
used to construct class objects and may initialize them.
control statement
A C or C++ language statement that changes the normal path of execution.
conversion
A change in the type of a value. For example, when you add values having
different data types, the compiler converts both values to the same type before
adding them.
conversion function
A member function that specifies a conversion from its class type to another
type.
copy constructor
A constructor used to make a copy of a class object from another class object
of the same class type.
ΓòÉΓòÉΓòÉ <hidden> D ΓòÉΓòÉΓòÉ
data abstraction
See abstraction (data).
data definition
A program statement that describes the features of, specifies relationships of,
or establishes the context of, data. A data definition can also provide an
initial value. Definitions appear outside a function (for example at file
scope) or within a block statement.
data member
See member.
data object
Anything that exists in storage and on which operations can be performed, such
as files, programs, classes, or arrays.
data type
A category that specifies the interpretation of a data object such as its
mathematical qualities and internal representation.
decimal constant
A number containing any digits 0 through 9 that does not begin with 0 (zero).
declaration
Establishes the names and characteristics of data objects and functions used in
a program.
declarator
Designates a data object or function declared. Initializations can be
performed in a declarator.
default arguments
Arguments that are declared with default values in a function prototype or
declaration. If a call to the function omits these arguments, default values
are used. Arguments with default values must be the trailing arguments in a
function prototype argument list.
default clause
In a switch statement, the keyword default followed by a colon, and one or more
statements. When the conditions of the specified case labels in the switch
statement do not hold, the default clause is chosen.
default constructor
A constructor that takes no arguments, or if it takes any arguments, all its
arguments have default values.
default initialization
The initial value assigned to a data object by the compiler if no initial value
is specified by the programmer. extern and static variables receive a default
initialization of zero, while the default initial value for auto and register
variables is undefined.
define statement
A preprocessor statement that causes the preprocessor to replace an identifier
or macro call with specified code.
definition
A declaration that allocates storage, and may initialize a data object or
specify the body of a function.
delete
1. The keyword delete identifies a free store deallocation operator.
2. The delete operator is used to destroy objects created by new. (See also
new.)
demangling
The conversion of mangled names back to their original source code names.
During compilation, identifiers such as function and static class member names
are mangled (encoded) with type and scoping information to ensure type-safe
linkage. These mangled names appear in the object file and the final
executable file. Demangling converts these names back to their original names
to make program debugging easier.
derivation
In C++, to derive a class, called a derived class, from an existing class,
called a base class.
derived class
A class that inherits the proper base class become members of a derived class
object. You can add additional data members and member functions to the
derived class. A derived class object can be manipulated as if it is a base
class object. The derived class can override virtual functions of the base
class.
destructor
A special member function of a class with the same name as the class with a
~(tilde) preceding the name. You cannot specify arguments or a return type for
this function. A destructor "cleans up" after an object by doing such things
as freeing any storage that was dynamically allocated when the object was
created. (See also constructor.)
digraph sequence
A combination of two keystrokes used to represent unavailable characters in a
C++ source program. Digraphs are read as tokens during the preprocessor phase.
Distributed SOM (DSOM)
A model in which SOM objects can be shared remotely, so that a server on one
machine provides objects and services to a client program on another machine.
DSOM allows for transparent distribution of objects between a client machine
and one or more servers.
do statement
A looping statement that contains the word do followed by a statement (the
action), the word while, and an expression in parentheses (the condition).
double precision
The use of two computer words to represent a floating-point value in
accordance with the required precision.
DSOM
See Distributed SOM.
dynamic binding
Resolution of a call to a virtual member function at run time.
ΓòÉΓòÉΓòÉ <hidden> E ΓòÉΓòÉΓòÉ
elaborated type specifier
Typically used in an incomplete class declaration or to qualify types that are
otherwise hidden.
element
The component of an array, subrange, enumeration, or set.
else clause
The part of an if statement that contains the word else followed by a
statement. The else clause provides an action that is executed when the if
condition evaluates to zero (false).
encapsulation
Hiding the internal representation of data objects and implementation details
of functions from the client program. This enables the end user to focus on the
use of data objects and functions without having to know about their
representation or implementation.
enumeration constant
An identifier (that has an associated integer value) defined by an enumeration
type. You can use an enumeration constant anywhere an integer constant is
allowed.
enumeration tag
The identifier that names an enumeration data type.
enumeration type
An enumeration type defines a set of enumeration constants. In C++, an
enumeration type is a distinct data type that is not an integral type.
enumerator
An enumeration constant and its associated value.
escape sequence
A representation of a nonprintable character in a character or string literal.
An escape sequence contains the \ symbol, followed by one of the characters: a,
b, f, n, r, t, v, ', ?, or \, or followed by one to three octal digits or \
followed by an x followed by any number of hexadecimal digits.
exception
Any user, logic, or system error detected by a function that does not itself
deal with the error but passes the error on to a handling routine. In C++,
passing this error is called throwing an exception.
exception handler
Exception handlers are catch blocks in C++. catch blocks catch exceptions when
they are thrown from a function enclosed in a try block. try blocks, catch
blocks and throw expressions are the constructs used to implement formal
exception handling in C++.
exception handling
A type of error handling that allows control and information to be passed to an
exception handler when an exception occurs. try blocks, catch blocks and throw
expressions are the constructs used to implement formal exception handling in
C++.
expression
A representation of a value. For example, variables and constants appearing
alone or in combination with operators are expressions.
external data definition
A definition appearing outside a function. The defined object is accessible to
all functions that follow the definition and are located within the same source
file as the definition.
ΓòÉΓòÉΓòÉ <hidden> F ΓòÉΓòÉΓòÉ
file scope
A name declared outside all blocks and classes has file scope and can be used
after the point of declaration in a source file.
float constant
A number containing a decimal point, an exponent, or both a decimal point and
an exponent. The exponent contains an e or E, an optional sign (+ or -), and
one or more digits (0 through 9).
for statement
A looping statement that contains the word for followed by a list of
expressions enclosed in parentheses (the condition) and a statement (the
action). Each expression in the parenthesized list is separated by a
semicolon. You can omit any of the expressions, but you cannot omit the
semicolons.
free store
Dynamically allocates memory. New and delete are used to allocate and
deallocate free store.
friend class
A class in which all the member functions are granted access to the private and
protected members of another class. It is named in the declaration of another
class and uses the keyword friend as a prefix to the class. For example:
class me {
friend class you;
// ...
};
makes all the functions in class you friends of class me.
friend function
A function that is granted access to the private and protected parts of a
class. It is named in the declaration of the class and uses the keyword friend
as a prefix.
function
A named group of statements that can be invoked and evaluated and can return a
value to the calling statement.
function call
An expression that moves the path of execution from the current function to a
specified function and evaluates to the return value provided by the called
function. A function call contains the name of the function to which control
moves and a parenthesized list of arguments.
function declarator
The part of a function definition that names the function, provides additional
information about the return value of the function, and lists the function
parameters.
function definition
The complete description of a function. A function definition contains an
optional storage class specifier, an optional type specifier, a function
declarator, parameter declarations, and a block statement (the function body).
function prototype
A function declaration that provides type information for each parameter. It
is the first line of the function (header) followed by a ; (semicolon). It is
required by the compiler when the function will be declared later so type
checking can occur.
function scope
Labels that are declared in a function have function scope and can be used
anywhere in that function.
function template
Provides a blueprint describing how a set of related individual functions can
be constructed.
ΓòÉΓòÉΓòÉ <hidden> G ΓòÉΓòÉΓòÉ
generic class
See class templates.
global scope
See file scope.
global variable
A symbol defined in one program module that is used in other independently
compiled program modules.
ΓòÉΓòÉΓòÉ <hidden> H ΓòÉΓòÉΓòÉ
header file
A file that contains declarations used by a group of functions or users.
hexadecimal
A system of numbers to the base sixteen; hexadecimal digits range from 0 (zero)
through 9 (nine) and A (ten) through F (fifteen).
hexadecimal constant
A constant, usually starting with special characters, that contains only
hexadecimal digits. The special characters are \x, 0x, or 0X.
ΓòÉΓòÉΓòÉ <hidden> I ΓòÉΓòÉΓòÉ
I/O Stream Library
A class library that provides the facilities to deal with many varieties of
input and output.
identifier
A name that refers to a data object. An identifier contains some combination
of letters, digits, and underscores, but its first character cannot be a digit.
if statement
A conditional statement that contains the word if followed by an expression in
parentheses (the condition), a statement (the action), and an optional else
clause (the alternative action).
include file
A text file that contains declarations used by a group of functions, programs,
or users. Also known as a header file.
include statement
A preprocessor statement that causes the preprocessor to replace the statement
with the contents of a specified file.
incomplete class declaration
A class declaration that does not define any members of a class. Until a class
is fully declared, or defined, you can only use the class name where the size
of the class is not required. Typically, an incomplete class declaration is
used as a forward declaration.
inheritance
An object-oriented programming technique that allows you to use existing
classes as bases for creating other classes.
initialize
To set the starting value of a data object.
initializer
An expression used to initialize data objects. In C++, there are three types of
initializers:
An expression followed by an assignment operator is used to initialize
fundamental data type objects or class objects that have copy
constructors.
An expression enclosed in braces ( {} ) is used to initialize aggregates.
A parenthesized expression list is used to initialize base classes and
members using constructors.
inline function
A function declared and defined simultaneously in a class definition. You can
also explicitly declare a function inline by using the keyword inline, which
is a hint to the compiler to perform inline expansion of the body of a
function member. Both member and nonmember functions can be inlined.
instance
An object-oriented programming term synonymous with 'object'. An instance is a
particular instantiation of a data type. It is simply a region of storage that
contains a value or group of values. For example, if a class box is previously
defined, two instances of a class box could be instantiated with the
declaration:
box box1, box2;
instantiate
To create or generate a particular instance (or object) of a data type or
template. For example, an instance box1 of class box could be instantiated
with the declaration:
box box1;
instruction
A program statement that specifies an operation to be performed by the
computer, along with the values or locations of operands. This statement
represents the programmer's request to the processor to perform a specific
operation.
integer constant
A decimal, octal, or hexadecimal constant.
integral object
A character object, an object having variations of the type int, or an object
that is a bit field.
internal data definition
A description of a variable appearing in a block that directs the system to
allocate storage for that variable and makes that variable accessible to the
current block after its point of declaration.
ΓòÉΓòÉΓòÉ <hidden> K ΓòÉΓòÉΓòÉ
keyword
A reserved C or C++ language identifier.
ΓòÉΓòÉΓòÉ <hidden> L ΓòÉΓòÉΓòÉ
label
An identifier followed by a colon, used to identify a statement in a program.
Usually the target of a goto or switch statement.
labeled statement
A possibly empty statement immediately preceded by a label.
late binding
See dynamic binding.
link
To interconnect items of data or portions of one or more computer programs,
such as linking object programs by a linkage editor or linking data items by
pointers.
linkage editor
A program that resolves cross-references between separately compiled object
modules and then assigns final addresses to create a single relocatable load
module. If a single object module is linked, the linkage editor simply makes
it relocatable.
literal
See constant.
load module
A computer program in a form suitable for loading into main storage for
execution.
local
Pertaining to information that is defined and used only in one subdivision of a
computer program.
local scope
A name declared in a block has local scope and can only be used in that block.
long constant
An integer constant followed by the letter l (el) or L.
lvalue
An expression that represents an object. A modifiable Ivalue can be both
examined and changed.
ΓòÉΓòÉΓòÉ <hidden> M ΓòÉΓòÉΓòÉ
macro call
An identifier followed by a parenthetical list of arguments that the
preprocessor replaces with the replacement code located in a preprocessor
define statement.
main function
An external function that has the identifier main. Each program must have
exactly one external function named main( ). Program execution begins with this
function.
mangling
The encoding, during compilation, of identifiers such as function and variable
names to include type and scoping information. The linker uses these mangled
names to ensure type-safe linkage.
manipulator
A value that can be inserted into streams or extracted from streams to affect
or query the behavior of the stream.
member
A data object or function in a structure, class, or union. Members can also be
classes, enumerations, bit fields and type names.
member function
Operators and functions that are declared as members of a class. A member
function has access to the private and protected data members and member
functions of an object of its class. Member functions are also called methods.
method
Method is an object-oriented programming term synonymous with member function.
multiple inheritance
An object-oriented programming technique implemented in C++ through derivation,
in which the derived class inherits members from more than one base class. (See
also inheritance.)
ΓòÉΓòÉΓòÉ <hidden> N ΓòÉΓòÉΓòÉ
name
In C++, a name is commonly referred to as an identifier. However,
syntactically, a name can be an identifier, operator function name, conversion
function name, destructor name or qualified name.
nested class
A class defined within the scope of another class.
new
A keyword identifying a free store allocation operator. The new operator may be
used to create class objects. (See also delete.)
new-line character
A control character that causes the print or display position to move to the
first position on the next line. This character is represented by '\n' in C and
C++.
NULL
A pointer that has a value 0 is guaranteed not to point to any data object. The
pointer can be converted to any pointer type.
null character (NUL)
The character hex 00, used to represent the absence of a printed or displayed
character.
null statement
A C or C++ statement that consists solely of a semicolon.
ΓòÉΓòÉΓòÉ <hidden> O ΓòÉΓòÉΓòÉ
object
A region of storage. An object is created when a variable is defined or new is
invoked. An object is destroyed when it goes out of scope. (See also
instance.)
object code
Machine-executable instructions, usually generated by a compiler from source
code written in a higher level language. For programs that must be linked,
object code consists of relocatable machine code.
object-oriented programming
A programming approach based on the concepts of data abstraction and
inheritance. Unlike procedural programming techniques, object-oriented
programming concentrates not on how something is accomplished but instead on
what data objects comprise the problem and how they are manipulated.
octal
A base eight numbering system.
octal constant
The digit 0 (zero) followed by any digits 0 through 7.
operand
An entity on which an operation is performed.
operator
A symbol (such as +, -, *) that represents an operation (in this case,
addition, subtraction, multiplication).
operator function
An overloaded operator that is either a member of a class, or takes at least
one argument that is a class type or a pointer or a reference to a class type.
overflow
That portion of an operation's result that exceeds the capacity of the intended
unit of storage.
overflow condition
A condition that occurs when a portion of the result of an operation exceeds
the capacity of the intended unit of storage.
overloading
Allows you to redefine functions and most standard C++ operators when the
functions and operators are used with class types.
ΓòÉΓòÉΓòÉ <hidden> P ΓòÉΓòÉΓòÉ
pad
To fill unused positions in a field with dummy data, usually zeros, ones, or
blanks.
parameter declaration
A description of a value that a function receives. A parameter declaration
determines the storage class and the data type of the value.
pointer
A variable that holds the address of a data object or function.
pointer to member
Used to access the address of nonstatic members of a class.
polymorphic functions
Functions that can be applied to objects of more than one data type. C++
implements polymorphic functions in two ways:
1. Overloaded functions (calls are resolved at compile time)
2. Virtual functions (calls are resolved at run time).
precedence
The priority system for grouping different types of operators with their
operands.
precision
A measure of the ability to distinguish between nearly equal values. (See also
single precision and double precision.)
preprocessor
A program that examines the source program for preprocessor statements that
are then interpreted, resulting in the alteration of the source program.
preprocessor statement
A statement that begins with the pound sign (#) and contains instructions that
the preprocessor interprets.
primary expression
Literals, names, and names qualified by the :: (scope resolution) operator.
private
A private member of a class is only accessible to member functions and friends
of that class.
protected
A protected member of a class is accessible to member functions and friends of
that class, or member functions and friends of classes derived from that
class.
prototype
See function prototype.
public
A public member of a class is accessible to all functions.
pure virtual function
A virtual function defined with '=0;'. (See also abstract classes.)
ΓòÉΓòÉΓòÉ <hidden> Q ΓòÉΓòÉΓòÉ
qualified class name
Any class name qualified with one or more :: (scope resolution) operators.
qualified name
Used to qualify a nonclass type name such as a member by its class name.
qualified type name
Used to reduce complex class name syntax by using typedefs to represent
qualified class names.
ΓòÉΓòÉΓòÉ <hidden> R ΓòÉΓòÉΓòÉ
register
A storage area commonly associated with fast-access storage, capable of storing
a specified amount of data such as a bit or an address.
register variable
A variable defined with the register storage class specifier. Register
variables have automatic storage.
ΓòÉΓòÉΓòÉ <hidden> S ΓòÉΓòÉΓòÉ
scalar
An arithmetic object, or a pointer, or a reference to an object of any type.
scope
That part of a source program in which a variable is visible.
scope resolution operator (::)
Defines the scope for the right argument. If the left argument is blank, the
scope is global. If the left argument is a class name, then the scope is within
that class. Also called the scope resolution operator.
single-byte character set
A set of characters in which each character is represented by 1 byte of
storage.
single precision
Pertaining to the use of one computer word to represent a number, in accordance
with the required precision.
SOM
See System Object Model
source program
A set of instructions written in a programming language that must be translated
to machine language before the program can be run.
specifiers
Used in declarations to indicate storage class, fundamental data type and other
properties of the object or function being declared.
statement
An instruction that ends with a semicolon (;) or one or more instructions
enclosed in braces ({}).
static
A keyword used for defining the scope and linkage of variables and functions.
For internal variables, the variable has block scope and retains its value
between function calls. For external values, the variable has file scope and
retains its value within the source file. For class variables, the variable is
shared by all objects of the class and retains its value within the entire
program.
static binding
Binding that occurs at compilation time based on the resolution of overloaded
functions.
storage class specifier
One of: auto, register, static, or extern.
stream buffer
A stream buffer is a buffer between the ultimate consumer and the I/O Stream
Library functions that format data. It is implemented in the I/O Stream Library
by the streambuf class and the classes derived from streambuf.
string literal
Zero or more characters enclosed in double quotation marks.
structure
A class data type that contains an ordered group of data objects and member
functions. Unlike an array, the data objects within a structure can have varied
data types. A structure can be used in all places a class is used. The initial
projection is public.
structure tag
The identifier that names a structure data type.
subscript
One or more expressions, each enclosed in brackets, that follow an array name.
A subscript refers to an element in an array.
switch expression
The controlling expression of a switch statement.
switch statement
A C or C++ language statement that causes control to be transferred to one of
several statements depending on the value of an expression.
System Object Model (SOM)
An object-oriented software model that provides a common programming interface
for building and using objects. SOM-compliant class definitions can be created
in one language, and objects of those classes can be used by client programs
written in another language. SOM also enables upward binary compatibility of
object libraries without requiring client programs to be recompiled.
ΓòÉΓòÉΓòÉ <hidden> T ΓòÉΓòÉΓòÉ
template
A family of classes or functions with variable types.
template class
A class instance generated by a class template.
template function
A function generated by a function template.
this
A keyword that identifies a special type of pointer that references in a member
function the class object with which the member function was invoked.
throw expression
An argument to the exception being thrown.
token
The smallest independent unit of meaning of a program as defined either by a
parser or a lexical analyzer. A token can contain data, a language keyword, an
identifier, or other parts of language syntax.
trigraph sequence
A combination of three keystrokes used to represent unavailable characters in a
C or C++ source program. Before preprocessing, each trigraph sequence in a
string or a literal is replaced by the single character that it represents.
try block
A block in which a known exception is passed to a handler.
type
The description of the data and the operations that can be performed on or by
the data.
type balancing
A conversion that makes both operands have the same data type. If the operands
do not have the same size data type, the compiler converts the value of the
operand with the smaller type to a value having the larger type.
type conversion
See boundary alignment.
type definition
A definition of a data type.
type specifier
Used to indicate the data type of an object or function being declared.
ΓòÉΓòÉΓòÉ <hidden> U ΓòÉΓòÉΓòÉ
unary expression
An expression that contains one operand.
union
A variable that can hold any one of several data types, but only one data type
at a time.
union tag
The identifier that names a union data type.
ΓòÉΓòÉΓòÉ <hidden> V ΓòÉΓòÉΓòÉ
variable
An object that can take different values at different times.
virtual function
A function of a class, declared with the keyword virtual. The implementation
that is executed when you make a call to a virtual function depends on the type
of the pointer or reference through which the member function is applied. This
is determined at run time.
visible
Visibility of identifiers is based on scoping rules and is independent of
access.
ΓòÉΓòÉΓòÉ <hidden> W ΓòÉΓòÉΓòÉ
while statement
A looping statement that contains the word while followed by an expression in
parentheses (the condition) and a statement (the action).
white space
Space characters, tab characters, form feed characters, new-line characters,
and (when referring to source code) comments.
ΓòÉΓòÉΓòÉ <hidden> Z ΓòÉΓòÉΓòÉ
zero suppression
The removal of, or substitution of blanks for, leading zeros in a number. For
example, 00057 becomes 57 when using zero suppression.