Borland_C++_Version_3.1_Programmers_Guide_1992 Borland C Version 3.1 Programmers Guide 1992

Borland_C++_Version_3.1_Programmers_Guide_1992 Borland_C++_Version_3.1_Programmers_Guide_1992

User Manual: Borland_C++_Version_3.1_Programmers_Guide_1992

Open the PDF directly: View PDF PDF.
Page Count: 483

DownloadBorland_C++_Version_3.1_Programmers_Guide_1992 Borland C  Version 3.1 Programmers Guide 1992
Open PDF In BrowserView PDF
3.1

PROGRAMMER'S GUIDE
• LANGUAGE STRUCTURE
• CLASS LIBRARIES
• ADVANCED PROGRAMMING TECHNIQUES
• ANSI CIMPLEMENTATION

BORLAND

Borland C++
Version 3.1

Programmer's Guide

BORLAND INTERNATIONAL INC. 1800 GREEN HILLS ROAD
P.O. BOX 660001, SCOTTS VALLEY, CA 95067-0001

Copyright © 1991, 1992 by Borland International. All rights reserved.
All Borland products are trademarks or registered trademarks of
Borland International, Inc. Other brand and product names are
trademarks or registered trademarks of their respective holders.
Windows, as used in this manual, refers to Microsoft's
im plementation of a windows system.

PRINTED IN THE USA.

Rl

10 9 8 7 6 5 4

c

o

N

T

Introduction
What's in this book ....................
An introduction to the formal definitions.
Syntax and terminology .............

1
1
2
3

Chapter 1 Lexical elements
5
Whitespace .......................... 6
Line splicing with \ ................. 6
Comments ......................... 7
C comments . . . . . . . . . . . . . . . . . . . . .. 7
Nested comments ................. 7
C++ comments ................... 8
Comment delimiters and whitespace . 8
Tokens .............................. 8
Keywords .......................... 9
Identifiers . . . . . . . . . . . . . . . . . . . . . . . .. 10
Naming and length restrictions .... 10
Identifiers and case sensitivity ..... 10
Uniqueness and scope ............ 11
Constants . . . . . . . . . . . . . . . . . . . . . . . .. 11
Integer constants . . . . . . . . . . . . . . . .. 11
Decimal constants . . . . . . . . . . . . .. 11
Octal constants ................ 12
Hexadecimal constants ......... 13
Long and unsigned suffixes ..... 13
Character constants .............. 14
Escape sequences .............. 14
Borland C++ special two-character
constants ..................... 15
Signed and unsigned char ....... 15
Wide character constants ........ 16
Floating-point constants .......... 16
Floating-point constants-data
types ......................... 16
Enumeration constants ......... 17
String literals ... . . . . . . . . . . . . . . . .. 18

E

N

T

s

Constants and internal
representation ................... 19
.Constant expressions .. . . . . . . . . . .. 20
Punctuators ....................... 21
Brackets ........................ 21
Parentheses ..................... 21
Braces .......................... 21
Comma ......................... 22
Semicolon. . . . . . . . . . . . . . . . . . . . . .. 22
Colon .......................... 23
Ellipsis ......................... 23
Asterisk (pointer declaration) ...... 23
Equal sign (initializer) ............ 23
Pound sign (preprocessor directive) .24
Chapter 2 Language structure
Declarations .........................
Objects ...........................
Lvalues ...........................
Rvalues .........................
Types and storage classes ...........
Scope ............................
Block scope .....................
Function scope ..................
Function prototype scope .........
File scope .......................
Class scope (C++) ................
Scope and name spaces ...........
Visibility. . . . . . . . . . . . . . . . . . . . . . . . ..
Duration ..........................
Static duration ...................
Local duration . . . . . . . . . . . . . . . . . ..
Dynamic duration ...............
Translation units ...................
Linkage ...........................
N arne mangling .................

25
25
25
26
27
27
27
28
28
28
28
28
28
29
29
29
30
30
31
31
32

Declaration syntax ................... 33
Tentative definitions ............... 33
Possible declarations ............... 34
External declarations and definitions . 36
Type specifiers .................... 38
Type taxonomy . . . . . . . . . . . . . . . . . . .. 38
Type void . . . . . . . . . . . . . . . . . . . . . .. 39
The fundamental types ............. 39
Integral types .................... 40
Floating-point types .............. 41
Standard conversions . . . . . . . . . . . .. 41
Special char, int, and enum
conver~ons ..................... 42
Initialization ...................... 42
Arrays, structures, and unions ..... 43
Simple declarations ................ 44
Storage class specifiers . . . . . . . . . . . . .. 45
Use of storage class specifier auto .. 45
Use of storage class specifier extern. 45
Use of storage class specifier
register ......................... 45
Use of storage class specifier static .. 46
Use of storage class specifier
typedef ......................... 46
Modifiers ......................... 47
The const modifier ............... 47
The interrupt function modifier .... 49
The volatile modifier ............. 49
The cdecl and pascal modifiers . . . .. 50
pascal ........................ 50
cdecl ......................... 50
The pointer modifiers ............. 51
Function type modifiers . . . . . . . . . .. 52
Complex declarations and declarators .53
Pointers ............................ 54
Pointers to objects . . . . . . . . . . . . . . . . .. 55
Pointers to functions ............... 55
Pointer declarations ................ 56
Pointers and constants . . . . . . . . . . . . .. 57
Pointer arithmetic . . . . . . . . . . . . . . . . .. 58
Pointer conversions ................ 59
C++ reference declarations .......... 59
Arrays .............................. 59
Functions ........................... 60

Declarations and definitions .........
Declarations and prototypes . . . . . . . ..
Definitions ........................
Formal parameter declarations. . . . . ..
Function calls and argument
conver~ons .......................
Structures . . . . . . . . . . . . . . . . . . . . . . . . . ..
Untagged structures and typedefs ....
Structure member declarations ......
Structures and functions ............
Structure member access . . . . . . . . . . ..
Structure word alignment . . . . . . . . . ..
Structure name spaces ..............
Incomplete declarations .............
Bit fields . . . . . . . . . . . . . . . . . . . . . . . . ..
Unions .............................
Anonymous unions (C++ only) ......
Union declarations .................
Enumerations .......................
Expressions .........................
Expressions and C++ ...............
Evaluation order ...................
Errors and overflows ...............
Operator semantics ..................
Operator descriptions ................
Unary operators ............. ,.....
Binaryoperators ...................
Additive operators . . . . . . . . . . . . . ..
Multiplicative operators ..........
Shift opera tors . . . . . . . . . . . . . . . . . ..
Bitwise operators ................
Logical operators ................
Assignment operators ............
Relational operators ..............
Equality operators ...............
Component selection operators ....
Class-member operators ..........
Conditional operator .............
Comma operator .................
Postfix and prefix operators .........
Array subscript operator [ ] ........
Function call operators () .........
Structure/union member operator
. (dot) ..........................

60
61
63
64
64
65
66
66
67
67
69
69
70
70
71
72
73
73
75
78
78
79
79
79
81
81
81
81
81
81
81
81
82
82
82
82
82
82
82
82
83
83

Structure/union pointer
operator -> .....................
Postfix increment operator ++ .....
Postfix decrement operator - - .....
Increment and decrement operators ..
Prefix increment operator .........
Prefix decrement operator ..... -... ,
Unary operators ...................
Address operator & ..............
Indirection operator * ............ ,
Unary plus operator + ............
Unary minus operator - .......... ,
Bitwise complement operator ~ ....
Logical negation operator! ........
The sizeof operator. . . . . . . . . . . . . . . ..
Multiplicative operators ............
Additive operators .................
The addition operator + ...........
The subtraction operator - ........
Bitwise shift operators ....... . . . . . ..
Bitwise shift operators «< and ») .
Relational operators ................
The less-than operator < ..........
The greater-than operator> .......
The less-than or equal-to operator
<= ... ~ .........................
The greater-than or equal-to
operator >= .....................
Equalityoperators .................
The equal-to operator == ..........
The inequality operator != .........
Bitwise AND operator & ............
Bitwise exclusive OR operator /\ .....
Bitwise inclusive OR operator I ......
Logical AND operator && ..........
Logical OR operator I I .............
Conditional operator? : . . . . . . . . . . . ..
Assignment operators ..............
The simple assignment operator
The compound assignment
operators .......................
Comma operator ......... , .. , ..... ,
C++ operators .....................
Statements ..........................

= ..

Blocks
98
Labeled statements ................ . 98
Expression statements ............. . 99
Selection statements ............... . 99
if statements ................... . 99
switch statements .. , ........... . 100
Iteration statements .............. . 101
while statements ............... . 101
do while statements ............ . 101
for statements ................. . 102
Jump statements ................. . 103
break statements ............... . 103
continue statements ............ . 103
goto statements ................ . 103
return statements .............. . 104

83
84
84
84
84
84
85
85
86
86
86
86
86
87
87
88
88
89
89
89
90
90
91

Chapter 3 C++ specifics
Referencing ........................
Simple references .................
Reference arguments ..............
Scope access operator ............... ,
The new and delete operators ........
Handling errors .............,.....
The operator new with arrays ......
The operator delete with arrays .. , ..
The ::operator new ................
Initializers with the new operator ...
Classes ............................
Class names . . . . . . . . . . . . . . . . . . . . ..
Class types . . . . . . . . . . . . . . . . . . . . . ..
Class name scope .................
Class objects .....................
Class member list. . . . . . . . . . . . . . . ..
Member functions ................
The keyword this .................
Inline functions . . . . . . . . . . . . . . . . . ..
Static members ...................
Member scope . . . . . . . . . . . . . . . . . . ..
Nested types ...................
Member access control. . . . . . . . . ..
Base and derived class access .......
Virtual base classes ..................
Friends of classes ...................
Constructors and destructors .........

91
91
91
91
92
92
93
93
93
94
94
95
95
96
96
97
97

iii

105
105
106
106
108
108
109
109
109
110
110
111
111
111
112
113
113
113
113
114
115
116
117
118
120
122
122
124

Constructors .......................
Constructor defaults ...............
The copy constructor ............. ,
Overloading constructors ..........
Order of calling constructors .......
Class initialization ................
Destructors ........................
When destructors are invoked ......
atexit, #pragma exit, and destructors.
exit and destructors ...............
abort and destructors ..............
Virtual destructors ................
Overloaded operators ...............
Operator functions .......... . . . . . . ..
Overloaded operators and
inheritance .......................
Overloading new and delete. . . . . . ..
Overloading unary operators .......
Overloading binary operators ......
Overloading the assignment
operator = .......................
Overloading the function call
operator 0 .......................
Overloading the subscript operator ..
Overloading the class member access
operator ..........................
Virtual functions . . . . . . . . . . . . . . . . . . ..
Abstract classes . . . . . . . . . . . . . . . . . . . ..
C++ scope .........................
Class scope . . . . . . . . . . . . . . . . . . . . . ..
Hiding ..........................
C++ scoping rules summary . . . . . . ..
Templates ..........................
Function templates . . . . . . . . . . . . . . ..
Overriding a template function .. ,
Implicit and explicit template
functions ......................
Class templates ...................
Arguments .....................
Angle brackets ..................
Type-safe generic lists ...........
Eliminating pointers .............
Template compiler switches ........
Using template switches .........

Chapter 4 The preprocessor
157
Null directive # . . . . . . . . . . . . . . . . . . . .. 159
The #define and #Undef directives . . . .. 159
Simple #define macros ............. 159
The #Undef directive .............. 160
The -D and -U options ............ 161
The Define option . . . . . . . . . . . . . . . .. 161
Keywords and protected words . . . .. 162
Macros with parameters ........... 162
File inclusion with #include .......... 165
Header file search with
 .................. 166
Header file search with
"header_name" ................... 166
Conditional compilation ............. 166
The #if, #elif, #else, and #endif conditional
directives ........................ 167
The operator defined ............ 167
The #ifdef and #ifndef conditional
directives ........................ 168
The #line line control directive ........ 169
The #error directive ................. 170
The #pragma directive . . . . . . . . . . . . . .. 171
#pragma argsused ................ 171
#pragma exit and #pragma startup .. 171
#pragma hdrfile .................. 172
#pragma hdrstop ................. 173
#pragma inline ................... 173
#pragma intrinsic ................. 173
#pragma option . . . . . . . . . . . . . . . . . .. 173
#pragma saveregs ................. 175
#pragma warn . . . . . . . . . . . . . . . . . . .. 175
Predefined macros .................. 175
__BCPLUSPLUS__ ............... 175
__BORLANDC__ ................ 176
__CDECL__ ..................... 176
__cplusplus . . . . . . . . . . . . . . . . . . . . .. 176
__DATE__ ...................... 177
__DLL__ ........................ 177
__FILE__ ....................... 177
__LINE
....................... 177
__MSDOS __ ..................... 177
__OVERLAY__ .................. 178
__PASCAL__ .................... 178

125
126
127
127
128
129
132
132
133
133
133
134
135
136
136
137
138
139
139
140
140
140
140
142
143
144
144
144
145
146
148
148
149
150
150
151
152
152
153

iv

__STDC_ _ 000000 0000000000000000
__TCPLUSPLUS__ 000000000000000
__TEMPLATES__ 00000000·00000000
__TIME__ 00000000000000000000000
__TURBOC
0000000000000000000
_Windows 00000000000000000000000

178
178
178
178
179
179

Chapter 5 Using C++ streams
What is a stream? 0000000000000000000
The iostream library 00000000000000000
The streambuf class 000000000000000
The ios class 0000000000000000000000
Output 0000000000000000000000000000
Fundamental types 0000000000000000
Output formatting 0000000000000000
Manipulators 000000000000000000000
Filling and padding 000000000000000
Input 000000000000000000000000000000
I/O of user-defined types 000000000000
Simple file I/O 000000000000000000000
String stream processing 0000000000000
Screen output streams 0000.0000000000
Stream class reference 000000000000000
conbuf 00. 00000000000000000000000000
Member functions 00000000000000000
constream 00000000000000000000000000
Member functions 00000000000000000
filebuf 00 000000000000000000000000000
Member functions 00000000000000000
fstream 0000000000000000 000000000000
Member functions 00000000000000000
fstreambase 000000000000000000000000
Member functions 00000000000000000
ifstream 000000000000000000000000000
Member functions 00000000000000000
ios 000000000000000000000000000000 00
Data members 00000000000000000000
Member functions 00000000000000000
iostreamo 00000000000000000000000000
iostream_withassign 0000000000000000
Member functions 00000000000000000
istream 0000000000000000000000000000
Member functions 00000000000000000
istream_withassign 000000000000000000

181
181
182
182
182
183
184
184
185
187
187
188
189
190
192
193
194
194
195
195
196
196
197
197
197
198
198
199
199
199
200
202
202
202
202
203
204

v

Member functions 0000000000000000
istrstream 00000 0000000000 0000000 0000
ofstream 000000000000: 00000000000000
Member functions 00. 0000000000000
ostream 00000000000000. 0000000000000
Member functions 00.. 0.... 0. . . . ..
ostream_withassign .0 .. 00 .... '0, ... 0
Member functions ................
ostrstream ...... 0............ 00... 0
Member functions ............. 0"
streambuf 0... 00... 00.. 0...... 0... o.
Member functions ....... 00... 0.. 0
strstreambase 000 0. 000. 000. 000. 00000
Member functions 00. 0. 0. 000000. 00
strstreambuf 000000000. 000. 0. 0000. 00
Member functions 00. 0... 0.. 000. 00
strstream 000000 .. 000.. 00.. 000000.. 0
Member function 000. 000000. 000000

204
204
205
205
205
206
206
206
206
207
207
207
210
210
210
211
211
212

Chapter 6 The container class
libraries
What's new since version 200? 000000000
Why two sets of libraries? 000000000000
Container basics 000 000. 0000000. 00000
Object-based and other classes 0000
Class categories 00000' 0000000000000
Non-container classes 0000000000000
Error class 000000000000. 00000. 00
Sortable class 000000 000000. 000000
Associa tion class 00. 0000000000000
Container classes 00 o. 0000000000000
Containers and ownership 00.000000
Container iterators 000000. 000. 00000
Sequence classes 00000000. 000. 0.. 00
Collections 000. 0000000000. 00000000
Unordered collections 0000.000.00
Ordered collections 0000. 00000000
The BIDS template library 000000000000
Templates, classes, and containers. 00
Container implementation 000000.. 0
The template solution 000000000·0000
ADTs and FDSs .. 000000000000000
Class templates 000000.0000000.00
Container class compatibility 0000. 00

213
214
215
216
218
218
218
218
219
219
219
220
222
223
223
224
224
224
225
225
226
226
227
229

Header files ......................
Tuning an application .............
FDS implementation ..............
ADT implementation . . . . . . . . . . . . ..
The class library directory . . . . . . . . . . ..
The INCLUDE directory ....... , ...
The SOURCE directory ............
The LIB directory .................
The EXAMPLES directory . . . . . . . . ..
Preconditions and checks ............
Container class reference ........... "
AbstractArray ......................
Data members . . . . . . . . . . . . . . . . . . ..
Member functions ............... "
Friends ..........................
Array .............................
Example .........................
Member functions. . . . . . . . . . . . . . . ..
Arraylterator . . . . . . . . . . . . . . . . . . . . . ..
Member functions. . . . . . . . . . . . . . . ..
Association ........................
Member functions. . . . . . . . . . . . . . . ..
Example . . . . . . . . . . . . . . . . . . . . . . . ..
Bag ...............................
Member functions. . . . . . . . . . . . . . . ..
BaseDate . . . . . . . . . . . . . . . . . . . . . . . . . ..
Member functions. . . . . . . . . . . . . . . ..
BaseTime ..........................
Member functions . . . . . . . . . . . . . . . ..
Btree ..............................
Member functions. . . . . . . . . . . . . . . ..
Friends ..........................
BtreeIterator ........................
Member functions. . . . . . . . . . . . . . . ..
Collection ........................ "
Member functions. . . . . . . . . . . . . . . ..
Container ... . . . . . . . . . . . . . . . . . . . . . ..
Member functions . . . . . . . . . . . . . . . ..
Friends ..........................
ContainerIterator ...................
Member functions. . . . . . . . . . . . . . . ..
Date ...............................
Member functions . . . . . . . . . . . . . . . ..
Deque .............................

230
231
231
235
238
238
239
239
240
240
241
242
242
243
245
245
245
246
247
247
248
248
249
250
250
252
252
253
253
255
255
257
257
257
258
259
259
261
263
263
263
264
264
265

Example . . . . . . . . . . . . . . . . . . . . . . . ..
Member functions ................
Dictionary .........................
Member functions ................
DoubleList . . . . . . . . . . . . . . . . . . . . . . . ..
Member functions ................
Friends ........................ "
DoubleListIterator ..................
Member functions ................
Error ..............................
Member functions ................
HashTable .........................
Member functions ................
Friends ..........................
HashTableIterator ................. "
Member functions ................
List .......... '.' ...................
Member functions ................
Friends ..........................
ListIterator . . . . . . . . . . . . . . . . . . . . . . . ..
Member functions ................
MemBlocks ........................
MemStack .........................
Object ........... '" .... , .. , .......
Data member. . . . . . . . . . . . . . . . . . . ..
Member functions ................
Friends ..........................
Related functions .................
PriorityQueue .................... "
Member functions ................
Queue .............................
Exam pIe . . . . . . . . . . . . . . . . . . . . . . . ..
Member functions ................
Set ......................... " . " ..
Member functions ................
Sortable ...........................
Member functions ................
Related functions .................
SortedArray ...................... "
Stack .. " .... , .....................
Example . . . . . . . . . . . . . . . . . . . . . . . ..
Member functions ................
String .............................
Member functions ................

vi

265
266
267
268
268
268
270
270
270
271
271
272
273
274
274
274
275
275
276
276
276
277
278
279
279
279
281
282
282
283
284
284
285
285
286
286
288
288
289
289
290
291
292
292

Example . . . . . . . . . . . . . . . . . . . . . . . ..
Time ..............................
Member functions . . . . . . . . . . . . . . . ..
Timer .............................
Member functions. . . . . . . . . . . . . . . ..
TShouldDelete . . . . . . . . . . . . . . . . . . . . ..
Member functions. . . . . . . . . . . . . . . ..

Windows Explicit Functions Exported
(-WE) ........................... 323
Windows Smart Callbacks (-WS) .... 323
Windows DLL All Functions Exportable
(-WD) ........................... 324
Windows DLL Explicit Functions
Exported (-WDE) ................. 324
The _export keyword ............. , 324
Prologs, epilogs, and exports: a
summary ........................ 325
Memory models .................... 326
Module definition files ............... 326
A quick example . . . . . . . . . . . . . . . . .. 327
Linking for Windows . . . . . . . . . . . . . . .. 328
Linking in the IDE ................ 329
Linking with TLINK .............. 329
Linker options . . . . . . . . . . . . . . . . .. 329
Linking .OBJ and .LIB files ....... 330
Linking .OBJ and .LIB files for
DLLs .......................... 331
Dynamic link libraries ............... 332
Compiling and linking a DLL within the
IDE ............................. 332
Compiling and linking a DLL from the
command line ............... . . . .. 332
Module definition files ........... 333
Import libraries ................. 333
Creating DLLs .................... 333
LibMain and WEP .............. 334
Pointers and memory. . . . . . . . . . .. 335
Static data in DLLs ............ 336
C++ classes and pointers ......... 336

293
294
294
295
295
296
296

Chapter 7 Converting from Microsoft
C
299
Environment and tools .............. 299
Paths for .h and .LIB files ........... 300
MAKE ........................... 301
Command-line compiler ........... 302
Command-line options and libraries. 306
Linker ................... '" ..... 306
Source-level compatibility ........... , 308
__MSC macro .................... 308
Header files ...................... 308
Memory models .................. 309
Keywords. . . . . . . . . . . . . . . . . . . . . . .. 310
Floating-point return values ....... , 310
Structures returned by value ....... 310
Conversion hints. . . . . . . . . . . . . . . . . . .. 311
Chapter 8 Building a Windows
application
Compiling and linking within the IDE
Understanding resource files .......
Understanding module definition
files .............................
Compiling and linking WHELLO ...
Using the project manager .......
Setting compile and link options ..
WinMain ..........................
Compiling and linking from the
command line ......................
Compiling from the command line ..
Linking from the command line . . . ..
Using a makefile ..................
Another makefile for Windows ...
Prologs and epilogs .................
Windows All Functions Exportable
(-W) .............................

313
314
315
315
315
316
317
318

Chapter 9 DOS memory management
Running out of memory .............
Memory models ....................
The 8086 registers . . . . . . . . . . . . . . . ..
General-purpose registers ........
Segment registers ...............
Special-purpose registers .........
The flags register . . . . . . . . . . . . . . ..
Memory segmentation .............
Address calculation .............

318
319
320
321
322
322
323

vii

339
339
339
340
340
341
341
341
342
343

Pointers ......................... 344
Near pointers ................... 344
Far pointers .................... 344
Huge pointers ................... 345
The six memory models. . . . . . . . . . .. 346
Mixed-model programming: Addressing
modifiers .......................... 350
Segment pointers ................. 351
Declaring far objects ............... 352
Declaring functions to be near or far . 352
Declaring pointers to be near, far, or
huge ............................ 353
Pointing to a given segment:offset
address ........................ 355
Using library files ................. 355
Linking mixed modules . . . . . . . . . . .. 355
Overlays (VROOMM) for DOS. . . . . . .. 357
How overlays work ............... 357
Getting the best out of Borland C++
overlays ....................... 359
Requirements .................... 359
Using overlays ................... 360
Overlay example ................ 360
Overlaying in the IDE ........... 361
Overlaid programs . . . . . . . . . . . . . . .. 361
The far call requirement. . . . . . . . .. 361
Buffer size ..................... 362
What not to overlay ............. 362
Debugging overlays . . . . . . . . . . . .. 362
External routines in overlays ..... 363
Swapping . . . . . . . . . . . . . . . . . . . . . . .. 364
Expanded memory . . . . . . . . . . . . .. 364
Extended memory .............. 364

Chapter 10 Math
Floating-point options ...............
Emulating the 80x87 chip ..........
Using 80x87 code .................
No floating-point code .............
Fast floating-point option ....... "...
The 87 environment variable .......
Registers and the 80x87 ............
Disabling floating-point exceptions ..

367
367
368
368
368
368
369
370
370

viii

Using complex math ................
Using BCD math ..................
Converting BCD numbers . . . . . . ..
Number of decimal digits ........

371
372
373
373

Chapter 11 Video functions
Some words about video modes ......
Some words about windows and
viewports ..........................
What is a window? ................
What is a viewport? ...............
Coordinates ......................
Programming in text mode . . . . . . . . . ..
The console I/O functions ..........
Text output and manipulation ....
Window and mode control .. . . . ..
Attribute control . . . . . . . . . . . . . . ..
State query .....................
Cursor shape ...................
Text windows ....................
An example ....................
The text_modes type ...............
Textcolors .......................
High-performance output ..........
Programming in graphics mode. . . . . ..
The graphics library functions ......
Graphics system control .........
A more detailed discussion . . . . . ..
Drawing and filling .............
Manipulating the screen and
viewport . . . . . . . . . . . . . . . . . . . . . ..
Text output in graphics mode .....
Color control . . . . . . . . . . . . . . . . . ..
Pixels and ralettes ..............
Background and drawing color ...
Color control on a CGA . . . . . . . . ..
CGA low resolution ...........
CGA high resolution ..........
CGA palette routines ..........
Color control on the EGA and
VGA ..........................
Error handling in graphics mode ..
State query .....................

375
375
376
376
377
377
377
377
377
379
379
380
380
380
381
381
382
383
384
385
385
387
387
389
390
392
392
393
393
393
394
395
395
395
396

Chapter 12 BASM and inline
assembly
399
Inline assembly language .......... 399
BASM ......................... 400
Inline syntax ................... 400
Opcodes ....................... 402
String instructions ............ 403
Prefixes . . . . . . . . . . . . . . . . . . . . .. 403
Jump instructions ............. 403
Assembly directives ........... 404
Inline assembly references to data and
functions ...................... 404

Inline assembly and register
variables . . . . . . . . . . . . . . . . . . . .. 404
Inline assembly, offsets, and size
overrides .................... 404
Using C structure members ...... 405
Using jump instructions and labels. 406
Interrupt functions . . . . . . . . . . . . . . .. 406
Using low-Iev,el practices .......... 408

ix

Appendix A ANSI implementationspecific standards

411

Index

423

T

A

B

L

E

s

2.13: Borland C++ statements ........... 98
4.1: Borland C++ preprocessing directives
syntax ........................... 158
5.1: Stream manipulators .............. 186
5.2: File modes ....................... 190
5.3: Console stream manipulators ...... 192
6.1: ADTs as fundamental data
structures ........................ 226
6.2: FDS class templates ............... 227
6.3: Abbreviations in CLASSLIB names .228
6.4: ADT class templates .............. 228
6.5: Object-based FDS classes .......... 229
6.6: Class debugging modes ........... 241
7.1: CL and BCC options compared ..... 302
7.2: LINK and TLINK options
compared ....................... 307
8.1: Compiler options and the _export
keyword ........................ 325
8.2: Startup and library files for DLLs ... 331
9.1: Memory models .................. 349
9.2: Pointer results ................... 351
11.1: Graphics mode state query
functions ....................... 397
12.1: Opcode mnemonics .............. 402
12.2: String instructions ............... 403
12.3: Jump instructions ............... 404
A.1: Identifying diagnostics in C++ ..... 411

1.1: All Borland C++ keywords ........... 9
1.2: Borland C++ extensions to C ......... 9
1.3: Keywords specific to C++ ........... 9
1.4: Borland C++ register
pseudovariables ................... 10
1.5: Constants-formal definitions ....... 12
1.6: Borland C++ integer constants without
Lor U ............................ 13
1.7: Borland C++ escape sequences ...... 15
1.8: Borland C++ floating constant sizes and
ranges ........................... 17
1.9: Data types, sizes, and ranges ........ 19
2.1: Borland C++ declaration syntax ..... 35
2.2: Borland C++ declarator syntax ...... 36
2.3: Borland C++ class declarations (C++
only) ............................. 37
2.4: Declaring types ................... 39
2.5: Integral types ..................... 40
2.6: Methods used in standard arithmetic
conversions; ...................... 42
2.7: Borland C++ modifiers ............. 47
2.8: Complex declarations .............. 54
2.9: External function definitions ........ 63
2.10: Associativity and precedence of
Borland C++ operators ............ 76
2.11: Borland C++ expressions .......... 77
2.12: Bitwise operators truth table ....... 93

x

F

G

u

R

E

s

9.3: Tiny model memory segmentation .. 347
9.4: Small model memory segmentation .347
9.5: Medium model memory
segmentation .................... 348
9.6: Compact model memory
segmentation .................... 348
9.7: Large model memory segmentation .348
9.8: Huge model memory segmentation .349
9.9: Memory maps for overlays ........ 359
11.1: A window in 80x25 text mode ..... 381

1.1: Internal representations of data types .20
5.1: Class streambuf and its derived
classes .......................... 182
5.2: Class ios and its derived classes .. , .183
6.1: Class hierarchies in CLASSLIB ..... 217
6.2: TShouldDelete hierarchy .......... 236
6.3: Class hierarchies in CLASSLIB ..... 296
8.1: Compiling and linking
a Windows program .............. 314
9.1: 8086 registers .................... 340
9.2: Flags register of the 8086 .......... 342

xi

N

T

o

R

To get an overview of the
Borland C++ documentation
set, start with the User's
Guide. Read the introduction
and Chapter 7 in that book
for information on how to
most effectively use the
Borland C++ manuals.

D

u

c

T

o

N

This manual contains materials for the advanced programmer. If
you already know how to program well (whether in C, C++, or
another language), this manual is for you. It provides a language
reference, and programming information on C++ streams, object
container classes, converting from Microsoft C, Windows
applications, memory models, floating point, overlays, video
functions, BASM, inline assembly, and ANSI implementation.
Code examples have a main function. EasyWin makes all these
examples work in Windows so you don't need Win Main and its
complicated parameters.
Typefaces and icons used in these books are described in the

User's Guide.

What's in this book
Chapters 1 through 4: Lexical elements, Language structure, C++
specifics, and The preprocessor, describe the Borland C++

language. Any extensions to the ANSI C standard are noted in
these chapters. These chapters provide a formal language
definition, reference, and syntax for both the C and C++ aspects of
Borland C++. Some overall information for Chapters 1 through 4
is included in the next section of this introduction.
Chapter 5: Using C++ streams tells you how to use the C++

version 2.1 stream library.
Chapter 6: The container class library tells you how to use the
Borland C++ object container classes (including templates) in
your programs.

Introduction

Chapter 7: Converting from Microsoft C provides some

guidelines on converting your Microsoft C programs to Borland
C++.
Chapter 8: Building a Windows application gets you started in

Windows programming.
Chapter 9: DOS memory management covers memory models,
overlays, and mixed-model programming.
Chapter 10: Math covers floating point and BCD math.
Chapter 11: Video functions is devoted to handling text and

graphics in Borland C++.
Chapter 12: BASM and inline assembly tells how to write
assembly language programs so they work well when called from
Borland C++ programs. It includes information on the built-in
assembler in the IDE.
Appendix A: ANSI implementation-specific standards describes
those aspects of the ANSI C standard that have been left loosely
defined or undefined by ANSI. This appendix tells how Borland
C++ operates in respect to each of these aspects.

An introduction to the formal definitions
Chapters 1 through 4 constitute a formal description of the C and
C++ languages as implemented in Borland c++. Together, these
chapters describe the Borland C++ language; they provide a
formal language definition, reference, and syntax for both the
C++ and C aspects of Borland C++. These chapters do not provide
a language tutorial. We've used a modified Backus-Naur form
notation to indicate syntax, supplemented where necessary by
brief explanations and program examples. They are organized in
this manner:
• Chapter 1, "Lexical elements," shows how the lexical tokens for
Borland C++ are categorized. Lexical elements is concerned
with the different categories of word-like units, known as
tokens, recognized by a language.
II Chapter 2, "Language structure," explains how to use the
elements of Borland C++. Language structure details the legal
ways in which tokens can be grouped together to form
expressions, statements, and other significant units.

2

Borland C++ Programmer's Guide

Chapter 3, "C++ specifics," covers those aspects specific to C++ .
• Chapter 4, "The preprocessor," covers the preprocessor,
including macros, includes, and pragmas, as well as many other
easy yet useful items.
II

Borland C++ is a full implementation of AT&T's C++ version 2.1,
the object-oriented superset of C developed by Bjarne Stroustrup
of AT&T Bell Laboratories. This manual refers to AT&T's previous
version as C++ 2.0. In addition to offering many new features and
capabilities, C++ often veers from C by small or large amounts.
We've made note of these differences throughout these chapters.
All the Borland C++ language features derived from C++ are
discussed in greater detail in Chapter 3.
Borland C++ also fully implements the ANSI C standard, with
several extensions as indicated in the text. You can set options in
the compiler to warn you if any such extensions are encountered.
You can also set the compiler to treat the Borland C++ extension
keywords as normal identifiers (see Chapter 5, "The commandline compiler," in the User's Guide).
There are also "conforming" extensions provided via the #pragma
directives offered by ANSI C for handling nonstandard, implementation-dependent features.

Syntax and
terminology Syntactic definitions consist of the name of the nonterminal token
or symbol being defined, followed by a colon (:). Alternatives
usually follow on separate lines, but a single line of alternatives
can be used if prefixed by the phrase "one of." For example,

external-definition:
function-definition
declaration
octal-digit: one of
01234567
Optional elements in a construct are printed within angle
brackets:

integer-suffix:
unsigned-suffix 
Throughout these chapters, the word "argument" is used to mean
the actual value passed in a call to a function. "Parameter" is used

Introduction

3

to mean the variable defined in the function header to hold the
value.

4

Borland C++ Programmer's Guide

c

H

A

p

T

R

E

1
Lexical elements
This chapter provides a formal definition of the Borland C++
lexical elements. It is concerned with the different categories of
word-like units, known as tokens, recognized by a language. By
contrast, language structure (covered in Chapter 2) details the
legal ways in which tokens can be grouped together to form
expressions, statements, and other significant units.
The tokens in Borland C++ are derived from a series of operations
performed on your programs by the compiler and its built-in preprocessor.
A Borland C++ program starts life as a sequence of ASCII
characters representing the source code, created by keystrokes
using a suitable text editor (such as the Borland C++ editor). The
basic program unit in Borland C++ is the file. This usually
corresponds to a named DOS file located in RAM or on disk and
having the extension .C or .CPP.
The preprocessor first scans the program text for special preprocessor directives (see page 157). For example, the directive #include
 adds (or includes) the contents of the file inc_file to the
program before the compilation phase. The preprocessor also
expands any macros found in the program and include files.

Chapter 7, Lexical elements

5

Whitespace
In the tokenizing phase of compilation, the source code file is
parsed (that is, broken down) into tokens and whitespace. Whitespace is the collective name given to spaces (blanks), horizontal
and vertical tabs, newline characters, and comments. Whitespace
can serve to indicate where tokens start and end, but beyond this
function, any surplus whitespace is discarded. For example, the
two sequences
int

i; float f;

and
int i
float

f;

are lexically equivalent and parse identically to give the six
tokens:
1. int

2.

3.
4. float

5. f
6. ;

The ASCII characters representing whitespace can occur within
literal strings, in which case they are protected from the normal
parsing process; in other words, they remain as part of the string:
char narne[]

= "Borland International";

parses to seven tokens, including the single literal-string token
"Borland International".

Line splicing
with \

A special case occurs if the final newline character encountered is
preceded by a backslash (\). The backslash and new line are both
discarded, allowing two physical lines of text to be treated as one
unit.
"Borland \
International"

6

Borland C++ Programmer's Guide

is parsed as "Borland International" (see page 18, "String literals,"
for more information).

Comments
Comments are pieces of text used to annotate a program. Comments are for the programmer's use only; they are stripped from
the source text before parsing.
There are two ways to delineate comments: the Cmethod and the
C++ method. Both are supported by Borland C++, with an additional, optional extension permitting nested comments. You can
mix and match either kind of comment in both C and C++
programs.
C comments

See page 163 for a
description of token pasting.

A C comment is any sequence of characters placed after the
symbol pair 1*. The comment terminates at the first occurrence of
the pair */ following the initial/*. The entire sequence, including
the four comment delimiter symbols, is replaced by one space
after macro expansion. Note that some C implementations remove
comments without space replacements.
Borland C++ does not support the nonportable token pasting
strategy using 1**/. Token pasting in Borland C++ is performed
with the ANSI-specified pair ##, as follows:
#define VAR(i,j)
#define VAR(i,j)
#define VAR (i, j )

(i/**/j)
(i##j)
(i ## j)

1* won't work *1
1* OK in Borland
1* Also OK *1

ett

*1

In Borland C++,
int 1* declaration *1

1* counter *1;

parses as
int

to give the three tokens: int i ;
Nested comments

ANSI C doesn't allow nested comments. Attempting to comment
out the preceding line with
1*

int 1* declaration *1 i 1* counter *1; *1

fails, since the scope of the first 1* ends at the first */. This gives
i ;' * I

which would generate a syntax error.

Chapter 7, Lexical elements

7

By default, Borland C++ won't allow nested comments, but you
can override this with compiler options. You can enable nested
comments via the Source Options dialog box (0 I C I Source) in the
IDE or with the -C option (for the command-line compiler).
c++ comments
You can also use / / to create
comments in C code. This is
specific to Borland C++.

C++ allows a single-line comment using two adjacent slashes
U I). The comment can start in any position, and extends until the
next new line:
class X { II this is a comment
... }i

Comment delimiters
and whitespace

In rare cases, some whitespace before /* and II, and after */,
although not syntactically mandatory, can avoid portability
problems. For example, this C++ code
int i = jll* divide by k*/ki
tIDi

parses as int i = j
int i

tffi;

not as

= j/ki

tIDi

as expected under the C convention. The more legible
int i

= jl 1* divide

by k*1 ki

tIDi

avoids this problem.

Tokens
Borland C++ recognizes six classes of tokens. The formal
definition of a token is as follows:

token:
keyword
identifier
constant
string-literal
operator
punctuator
Punctuators are also known as separators.

8

Borland C++ Programmer's Guide

As the source code is parsed, tokens are extracted in such a way
that the longest possible token from the character sequence is
selected. For example, external would be parsed as a single
identifier, rather than as the keyword extern followed by the
identifier al.

Keywords
Keywords are words reserved for special purposes and must not be
used as normal identifier names. The following two tables list the
Borland C++ keywords. You can use options in the IDE (or
command-line compiler options) to select ANSI keywords only,
UNIX keywords, and so on; see Chapter 2, "IDE Basics" and
Chapter 5, "The command-line compiler," in the User's Guide, for
information on these options.
_asm
asm
auto
break
case
_cdecl
cdecl
char
class
const
continue
_cs
default
delete
do
double

_ds
else
enum
_es
_export
extern
_far
far
_fastcall
float
for
friend
goto
_huge
huge
if
inline

Table 1.2
Borland C++ extensions to C

_cdecl
cdecl
_cs
_ds

_es
_export
_far
far
_fastcall

Table 1.3
Keywords specific to C++

asm
class
delete
friend
inline
new

Table 1.1
All Borland C++ keywords

Chapter 7, Lexical elements

int
_interrupt
interrupt
_Ioadds
long
near
near
new
operator
_pascal
pascal
private
protected
public
register
return
_saveregs

huge
interrupt
_Ioadds
near
near

_seg
short
signed
sizeof
_ss
static
struct
switch
template
this
typedef
union
unsigned
virtual
void
volatile
while

_pascal
pascal
_saveregs
_seg
_ss

operator
private
protected
public
template
this
virtual

9

Table 1,4
Borland C++ register
pseudovariables

AH
AL
- AX
BH
BL

BP
BX
CH
- CL
- CS

-

-

-

CX
OH
01

OL
OS

OX
ES
FLAGS
- SI
- SP
- SS

Identifiers
The formal definition of an identifier is as follows:

identifier:
l10ndigit
identifier l10ndigit
identifier digit
nondigit: one of
abcdefghijklmnopqrstuvwxyz_
ABC 0 E FG H IJ K L M N 0 P Q RS T U V W X Y Z

digit: one of

o1
Naming and length
restrictions

2 3 4 5 6 789

Identifiers are arbitrary names of any length given to classes, objects, functions, variables, user-defined data types, and so on.
Identifiers can contain the letters A to Z and a to 2, the underscore
character <-), and the digits 0 to 9. There are only two restrictions:
1. The first character Inust be a letter or an underscore.

Identifiers in C++ programs
are significant to any lenfjth,

Identifiers and case
sensitivity

2. By default, Borland C++ recognizes only the first 32 characters
as significant. The number of significant characters can be
reduced by Inenu and comlnand-line options, but not increased. Use the -in command-line option (where 1 <= n
<= 32) or Identifier Length in the Source Options dialog box
(0 I C I Source).
Borland c++ identifiers are case sensitive, so that Sum, sum, and

sliM are distinct identifiers.
Global identifiers imported from other nlodules follow the same
naming and significance rules as normal identifiers. However,
Borland C++ offers the option of suspending case sensitivity to
allow conlpatibility when linking with case-insensitive languages.
By checking Case-sensitive Link in the Linker dialog box

10

Borland C++ Programmer's Guide

(Options I Linker I Settings), or using the Ie cOll1111and-line switch
with TLINK, you can ensure that global identifiers are case
insensitive. Under this regime, the globals Slim and sum are
considered identical, resulting in a possible "Duplicate symbol"
warning during linking.
An exception to these rules is that identifiers of type pascal are
always converted to all uppercase for linking purposes.

Uniqueness and scope

Although identifier names are arbit:cary (within the rules stated),
errors result if the same name is used for more than one identifier
within the same scope and sharing the same name space. Duplicate
names are always legal for different name spaces regardless of
scope. The rules are covered in the discussion on scope starting on
page 27.

Constants
Constants are tokens representing fixed numeric or character
values. Borland C++ supports four classes of constants: floating
point, integer, enumeration, and character.
The data type of a constant is deduced by the cOlnpiler using such
clues as numeric value and the format used in the source code.
The formal definition of a constant is shown in Table 1.5.

Integer constants

Integer constants can be decimal (base 10), octal (base 8) or hexadecimal (base 16). In the absence of any overriding suffixes, the
data type of an integer constant is derived from its value, as
shown in Table 1.6. Note that the rules vary between decilnal and
nondecimal constants.
Decimal constants

Decimal constants from 0 to 4,294,967,295 are allowed. Constants
exceeding this limit will be truncated. Decin1al constants Inust not
use an initial zero. An integer constant that has an initial zero is
interpreted as an octal constant. Thus,
int i = 10 ; /*decimal 10 */
int i = 010; /*decimal 8 */
/*decimal 0 = octal 0 */
int i = 0;

Chapter 7, Lexical elements

11

Table 1.5: Constants-formal definitions

oX hexadecimal-digit
hexadecimal-constant hexadecimal-digit

constant:
floating-constant
integer-constant
enumeration-constant
character-constant

nonzero-digit: one of
1 234 5 6 789

floating-constant:
fractional-constant  
digit-sequence exponent-part 
fractional-constant:
 . digit-sequence
digit-sequence .
exponent-part:
e  digit-sequence
E  digit-sequence

octal-digit: one of

o1

234 5 6 7

hexadecimal-digit: one of

o 1 234 5 6 789
abcdef
ABCDEF

integer-suffix:
unsigned-suffix 
long-suffix 
unsigned-suffix: one of
uU

sign: one of
+ -

long-suffix: one of
1L

digit-sequence:
digit
digit-sequence digit

enumeration-constant:
identifier

floating-SUffix: one of
f I F L

character-constant:
c-char-sequence

integer-constant:
decimal-constant 
octal-constant 
hexadecimal-constant 

c-char-sequence:
c-char
c-char-sequence c-char

decimal-constant:
nonzero-digit
decimal-constant digit

c-char:
Any character in the source character set except
the single-quote ('), backslash (\), or newline
character escape-sequence.

octal-constant:

escape-sequence: one of

o

octal-constant octal-digit
hexadecimal-constant:
ox hexadecimal-digit

\"
\a

\'
\b

\?
\f

\0

\00

\000

\\
\n
\r

\t

\v

\Xh ...

\xh ...

Octal constants
All constants with an initial zero are taken to be octal. If an octal
constant contains the illegal digits 8 or 9, an error is reported.
Octal constants exceeding 037777777777 will be truncated.

12

Borland c++ Programmer's Guide

Hexadecimal constants

All constants starting with Ox (or OX) are taken to be hexadecimal.
Hexadecimal constants exceeding OxFFFFFFFF will be truncated.
Long and unsigned suffixes

The suffix L (or 1) attached to any constant forces it to be represented as a long. Similarly, the suffix U (or u) forces the constant
to be unsigned. It is unsigned long if the value of the number
itself is greater than decimal 65,535, regardless of which base is
used. You can use both Land U suffixes on the same constant in
any order or case: uI, Iu, UL, and so on.
Table 1.6
Borland C++ integer
constants without L or U

Decimal constants

oto 32,767
32,768 to 2,147,483,647
2,147,483,648 to 4,294,967,295
> 4294967295

int
long
unsigned long

truncated

Octal constants

00
0100000
02000000
020000000000

to 077777
to 0177777
to 017777777777
to 037777777777

> 037777777777

int
unsigned int
long
unsigned long

truncated

Hexadecimal constants

OxOOOO
Ox8000
Oxl0000
Ox80000000

to Ox7FFF
to OxFFFF
to Ox7FFFFFFF
to OxFFFFFFFF

int
unsigned int
long
unsigned long

> OxFFFFFFFF

truncated

The data type of a constant in the absence of any suffix (U, U, L, or
1) is the first of the following types that can accommodate its
value:
decimal

int, long int, unsigned long int

octal

int, unsigned int, long int, unsigned long int

hexadecimal

int, unsigned int, long int, unsigned long int

If the constant has a U or u suffix, its data type will be the first of
unsigned int, unsigned long int that can accommodate its value.

Chapter 7, Lexical elements

13

If the constant has an L or I suffix, its data type will be the first of
long int, unsigned long int that can accommodate its value.
If the constant has both u and I suffixes (ul, lu, Ul, IU, uL, Lu, LU,
or UL), its data type will be unsigned long int.

Table 1.6 summarizes the representations of integer constants in
all three bases. The data types indicated assume no overriding L
or U suffix has been used.

Character constants

A character constant is one or more characters enclosed in single
quotes, such as ' A', ' =' , ' \n' . In C, single character constants
have data type int; they are represented internally with 16 bits,
with the upper byte zero or sign-extended. In C++, a character
constant has type char. Multicharacter constants in both C and
C++ have data type int.
Escape sequences

The backslash character (\) is used to introduce an escape sequence,
allowing the visual representation of certain nongraphic characters. For example, the constant \n is used for the single newline
character.
A backslash is used with octal or hexadecimal numbers to represent the ASCII symbol or control code corresponding to that value; for example, ' \03' for Ctrl-C or ' \x3F' for the question mark.
You can use any string of up to three octal or any number of
hexadecimal numbers in an escape sequence, provided that the
value is within legal range for data type char (0 to Oxff for Borland
C++). Larger numbers generate the compiler error, "Numeric constant too large." For example, the octal number \777 is larger than
the maximum value allowed, \377, and will generate an error.
The first nonoctal or nonhexadecimal character encountered in an
octal or hexadecimal escape sequence marks the end of the
sequence.
Originally, Turbo C allowed only three digits in a hexadecimal
escape sequence. The ANSI C rules adopted in Borland C++
might cause problems with old code that assumes only the first
three characters are converted. For example, using Turbo C l.x to
define a string with a bell (ASCII 7) followed by numeric
characters, a programmer might write:
printf("\x0072.1A Simple Operating System");

14

Borland C++ Prdgrammer's Guide

This is intended to be interpreted as \x007 and I/2.1A Simple
Operating System". However, Borland C++ compiles it as the
hexadecimal number \x0072 and the literal string I/.1A Simple
Operating System".
To avoid such problems, rewrite your code like this:
printf("\x007" "2.1A Simple Operating System");

Ambiguities may also arise if an octal escape sequence is followed
by a nonoctal digit. For example, because 8 and 9 are not legal octal digits, the constant \258 would be interpreted as a twocharacter constant made up of the characters \25 and 8.
The next table shows the available escape sequences.
Table 1.7
Borland C++ escape
sequences
The \ \ must be used to
represent a real ASCII
backslash, as used in DOS
paths.

Sequence

\a
\b
\f
\n
\r
\t
\v
\\
\,
\"
\?
\0
\xH
\XH

Value

Ox07
Ox08
OxOC
OxOA
OxOD
Ox09
OxOB
Ox5c
Ox27
Ox22
Ox3F

Char

What it does

BEL
BS
FF
LF
CR
HT
VT
\

Audible bell
Backspace
Formfeed
Newline (linefeed)
Carriage return
Tab (horizontal)
Vertical tab
Backslash
Single quote (apostrophe)
Double quote
Question mark
o = a string of up to three octal
digits
H = a string of hex digits
H = a string of hex digits

?
any
any
any

Borland C++ special two-character constants

Borland C++ also supports two-character constants (for example,
, An', ' \n \t', and' \007\007'). These constants are represented
as 16-bit int values, with the first character in the low-order byte
and the second character in the high-order byte. These constants
are not portable to other C compilers.
Signed and unsigned char

In C, one-character constants, such as ' A', ' \ t' , and ' \007' , are
also represented as 16-bit int values. In this case, the low-order
byte is sign extended into the high byte; that is, if the value is
greater than 127 (base 10), the upper byte is set to -1 (=OxFF). This

Chapter 7, Lexical elements

15

can be disabled by declaring that the default char type is unsigned (use the -K command-line compiler option or choose
Unsigned Characters in the Options I Compiler I Code Generation
dialog box), which forces the high byte to be zero regardless of the
value of the low byte.
Wide character constants

A character constant preceded by an L is a wide-character constant of data type wchar_t (an integral type defined in stddef.h).
For example,
x

Floating-point
constants

= L

I

A';

A floating constant consists of:
•
•
•
•

decimal integer
decimal point
decimal fraction
e or E and a signed integer exponent (optional)
iii type suffix: for For 1or L (optional)

You can omit either the decimal integer or the decimal fraction
(but not both). You can omit either the decimal point or the letter e
(or E) and the signed integer exponent (but not both). These rules
allow for conventional and scientific (exponent) notations.
Negative floating constants are taken as positive constants with
the unary operator minus (-) prefixed.
Examples:
Constant

Value

23.45e6

23.45 X 106

.0

o.

1.
-1.23
2e-5
3E+10
.09E34

o
o

1.0 x 100 = 1.0
-1.23
2.0 x 10-5
3.0 X 1010
0.09 X 1034

Floating-point constants-data types

In the absence of any suffixes, floating-point constants are of type
double. However, you can coerce a floating constant to be of type

16

Borland C++ Programmer's Guide

float by adding an f or F suffix to the constant. Similarly, the suffix
1or L forces the constant to be data type long double. The next
table shows the ranges available for float, double, and long
double.
Table 1.8
Borland C++ floating
constant sizes and ranges

Type

Size (bits)

Range

float

32

3.4 x 10-38 to 3.4 X 1038

double

64

1.7 x 10-308 to 1.7 X 10308

long double

80

3.4 x 10-4932 to 1.1

X

104932

Enumeration constants
Enumeration constants are identifiers defined in enum type declarations. The identifiers are usually chosen as mnemonics to
assist legibility. Enumeration constants are integer data types.
They can be used in any expression where integer constants are
valid. The identifiers used must be unique within the scope of the
enum declaration. Negative initializers are allowed.
See page 73 for a detailed
look at enum dec/a rations.

The values acquired by enumeration constants depend on the format of the enumeration declaration and the presence of optional
initializers. In this example,
enum team { giants, cubs, dodgers };

giants, cubs, and dodgers are enumeration constants of type
team that can be assigned to any variables of type team or to any

other variable of integer type. The values acquired by the
enumeration constants are
giants = 0, cubs = 1, dodgers = 2

in the absence of explicit initializers. In the following example,
enum team { giants, cubs=3, dodgers = giants + 1 };

the constants are set as follows:
giants = 0, cubs = 3, dodgers = 1

The constant values need not be unique:
enum team { giants, cubs = 1, dodgers = cubs - 1 };

Chapter 7, Lexical elements

17

String literals

String literals, also known as string constants, form a special category of constants used to handle fixed sequences of characters. A
string literal is of data type array of char and storage class static,
written as a sequence of any number of characters surrounded by
double quotes:
"This is literally a string!"

The null (empty) string is written" ".
The characters inside the double quotes can include escape
sequences (see page 14). This code, for example,
"\t\t\"Name\"\\\tAddress\n\n"

prints out like this:
"Name"\

Address

"Name" is preceded by two tabs; Address is preceded by one tab.
The line is followed by two new lines. The \" provides interior
double quotes.
A literal string is stored internally as the given sequence of characters plus a final null character ('\0'). A null string is stored as a
single \0 character.
I

I

Adjacent string literals separated only by whitespace are concatenated during the parsing phase. In the following example,
#include 
int main ()
{

char
p

*p;

"This is an example of how Borland Ctt"
" will automatically\ndo the concatenation for"
" you on very long strings, \nresulting in nicer"
" looking programs.";
printf (p) ;
return(O) ;
=

The output of the program is
This is an example of how Borland Ctt will automatically
do the concatenation for you on very long strings,
resulting in nicer looking.programs.

18

Borland C++ Programmer's Guide

You can also use the backslash (\) as a continuation character in
order to extend a string constant across line boundaries:
puts("This is really \
a one-line string");

Constants and internal
representation

ANSI C acknowledges that the size and numeric range of the
basic data types (and their various permutations) are implementation specific and usually derive from the architecture of the host
computer. For Borland C++, the target platform is the IBM PC
family (and compatibles), so the architecture of the Intel 8088 and
80x86 microprocessors governs the choices of inner representations for the various data types. The next table lists the sizes and
resulting ranges of the data types for Borland C++; see page 39 for
more information on these data types. Figure 1.1 shows how these
types are represented internally.

Table 1.9: Data types, sizes, and ranges

Size
(bits)

Range

Sample applications

unsigned char

8

o to 255

Small numbers and full PC character set

char

8

-128 to 127

Very small numbers and ASCII characters

Type

enum

16

-32,768 to 32,767

Ordered sets of values

unsigned int

16

o to 65,535

Larger numbers and loops

short int

16

-32,768 to 32,767

Counting, small numbers, loop control

int

16

-32,768 to 32,767

Counting, small numbers, loop control

unsigned long

32

o to 4,294,967,295

Astronomical distances

long

32

-2,147,483,648 to 2,147,483,647

Large numbers, populations

float

32

3.4 x 10-38 to 3.4 X 1038

Scientific (7-digit precision)

double

64

1.7 x 10-308 to 1.7 X 10 308

Scientific (15-digit precision)

10-4932

long double

80

3.4 x

near pointer

16

Not applicable

Manipulating memory addresses

Not applicable

Manipulating addresses outside current
segment

far pointer

32

Chapter 1, Lexical elements

to 1.1 x

104932

Financial (19-digit precision)

19

Figure 1.1
Internal representations of
data types

-

Increasing significance

(2's complement)

long int

s~l___

,:--1

m_a_g_nit_ud_e_ _.-.JI (2's complement)

31

double

lsi
63

long double

e~~~sne~nt

i1
significand

I

51

lsi e~~~s';~nt

significand

111

7L9L-----~ML6~3--------------------------------~

s

Sign bit (0 = positive, 1 = negative)
Position of implicit binary point
Integer bit of significand:
Stored in long double
Implicit (always 1) in float, double

Exponent bias (normalized values):

float :
127 (7FH)
double :
1023 (3FFH)
long double: 16,383 (3FFFH)

Constant expressions

A constant expression is an expression that always evaluates to a
constant (and it must evaluate to a constant that is in the range of
representable values for its type). Constant expressions are evaluated just as regular expressions are. You can use a constant
expression anywhere that a constant is legal. The syntax for constant expressions is

constant-expression:
Conditional-expression

20

Borland C++ Programmer's Guide

Constant expressions cannot contain any of the following
operators, unless the operators are contained within the operand
of a sizeof operator:
assignment
comma
III decrement
III function call
II increment
II

III

Punctuators
The punctuators (also known as separators) in Borland C++ are
defined as follows:

punctuator: one of
[](){},;:

Brackets

[ ] (open and close brackets) indicate single and multidimensional
array subscripts:
char ch, str [l
int mat[3] [4];
ch = str[3];

Parentheses

... * = #

"Stan" ;
/* 3 x 4 matrix */
/* 4th element */

() (open and close parentheses) group expressions, isolate condi-

tional expressions, and indicate function calls and function
parameters:
d = c * (a + b);

/* override normal precedence */

if (d

/* essential with conditional statement */

==

z)

ttX;

func ();
int (*fptr) () ;
fptr = func;

/* function call, no args */
/* function pointer declaration */
/* no () means func pointer */

void func2(int n);

/* function declaration with args */

Parentheses are recommended in macro definitions to avoid potential precedence problems during expansion:
#define CUBE (x) ((x) * (x) * (x))

The use of parentheses to alter the normal operator precedence
and associativity rules is covered on page 79.

Chapter 7, Lexical elements

21

Braces

{} (open and close braces) indicate the start and end of a compound statement:
if (d

==

z)

{

ttX;

func() ;

The closing brace serves as a terminator for the compound statement, so a ; (semicolon) is not required after the }, except in
structure or class declarations. Often, the semicolon is illegal, as in
if (statement)
/*illegal semicolon*/

{};

else

Comma

The comma (,) separates the elements of a function argument list:
void func(int n , float f, char ch);

The comma is also used as an operator in comma expressions.
Mixing the two uses of comma is legal, but you must use
parentheses to distinguish them:
func (i j);
func ( (expl, exp2)
I

Semicolon

I

(exp3

I

exp4

I

/ * call func wi th two args */
exp5)); / * also calls func
with two args! */

The semicolon (;) is a statement terminator. Any legal C or C++
expression (including the empty expression) followed by ; is
interpreted as a statement, known as an expression statement. The
expression is evaluated and its value is discarded. If the expression statement has no side effects, Borland C++ may ignore it.
a t b;
tta;

/* maybe evaluate a t b, but discard value */
/* side effect on a, but discard value of tta */
/* empty expression = null statement */

Semicolons are often used to create an empty statement:
for (i

= 0; i

<

n; itt)

{

22

Borland C++ Programmer's Guide

Colon

Use the colon (:) to indicate a labeled statement:
start:

x=O;

goto start;
switch (a)
case 1: puts("One");
break;
case 2: puts("Two");
break;
default:

puts("None of the above!");
break;

Labels are covered on page 98.

Ellipsis

Ellipsis (. .. ) are three successive periods with no whitespace intervening. Ellipsis are used in the formal argument lists of function
prototypes to indicate a variable number of arguments, or arguments with varying types:
void func(int n, char ch, ... );

This declaration indicates that func will be defined in such a way
that calls must have at least two arguments, an int and a char, but
can also have any number of additional arguments.
~

Asterisk (pointer
declaration)

In C++, you can omit the comma preceding the ellipsis.
The * (asterisk) in a variable declaration denotes the creation of a
pointer to a type:
char *char-ptr;

/* a pointer to char is declared */

Pointers with multiple levels of indirection can be declared by indicating a pertinent number of asterisks:
int **int-ptr;
double ***double-ptr;

/* a pointer to a pointer to an int */
/* a pointer to a pointer to a pointer
to doubles */

You can also use the asterisk as an operator to either dereference a
pointer or as the multiplication operator:
i
a

Chapter 7, Lexical elements

= *int-ptr;
= b * 3.14;

23

Equal sign (initializer)

The =(equal sign) separates variable declarations from initialization lists:
char array [5] = { 1, 2, 3, 4, 5 };
int x = 5;

In C++, declarations of any type can appear (with some
restrictions) at any point within the code. In a C function, no code
can precede any variable declarations.
In a C++ function argument list, the equal sign indicates the
default value for a parameter:
int f (int i = 0) { ... }

/* parameter i has default value of
zero */

The equal sign is also used as the assignment operator in
expressions:
a

= b + c;
= farmalloc(sizeof(float)*100);

ptr

Pound sign
(preprocessor
directive)

The # (pound sign) indicates a preprocessor directive when it
occurs as the first nonwhitespace character on a line. It signifies a
compiler action, not necessarily associated with code generation.
See page 157 for more on the preprocessor directives.
# ~md ## (double pound signs) are also used as operators to

perform token replacement and merging during the preprocessor
scanning phase.

24

Bar/and C++ Programmer's Guide

c

H

A

p

T

R

E

2

Language structure
This chapter provides a formal definition of Borland C++'s
language structure. It details the legal ways in which tokens can
be grouped together to form expressions, statements, and other
significant units. By contrast, lexical elements (described in
Chapter 1) are concerned with the different categories of wordlike units, known as tokens, recognized by a language.

Declarations
Scope is discussed starting on
page 27; visibility on page
29; duration on page 29; and
linkage on page 31.

This section briefly reviews concepts related to declarations:
objects, types, storage classes, scope, visibility, duration, and
linkage. A general knowledge of these is essential before tackling
the full declaration syntax. Scope, visibility, duration, and linkage
determine those portions of a program that can make legal
references to an identifier in order to access its object.

Objects
An object is an identifiable region of memory that can hold a fixed
or variable value (or set of values). (This use of the word object is
not to be confused with the more general term used in objectoriented languages.) Each value has an associated name and type
(also known as a data type). The name is used to access the object.
This name can be a simple identifier, or it can be a complex
expression that uniquely "points" to the object. The type is used

Chapter 2, Language structure

25

• to determine the correct memory allocation required initially
• to interpret the bit patterns found in the object during
subsequent accesses
• in many type-checking situations, to ensure that illegal
assignments are trapped
Borland C++ supports many standard (predefined) and userdefined data types, including signed and unsigned integers in
various sizes, floating-point numbers in various precisions,
structures, unions, arrays, and classes. In addition, pointers to
most of these objects can be established and manipulated in
various memory models.
The Borland c++ standard libraries and your own program and
header files must provide unambiguous identifiers (or expressions derived from them) and types so that Borland C++ can
consistently access, interpret, and (possibly) change the bit
patterns in memory corresponding to each active object in your
program.
Declarations establish the necessary mapping between identifiers
and objects. Each declaration associates an identifier with a data
type. Most declarations, known as defining declarations, also
establish the creation (where and when) of the object, that is, the
allocation of physical memory and its possible initialization.
Other declarations, known as referencing declarations, simply make
their identifiers and types known to the compiler. There can be
many referencing declarations for the same identifier, especially
in a multifile program, but only one defining declaration for that
identifier is allowed.
Generally speaking, an identifier cannot be legally used in a
program before its declaration point in the source code. Legal
exceptions to this rule, known as forward references, are labels, calls
to undeclared functions, and class, struct, or union tags.

Lvalues
An lvalue is an object locator: An expression that designates an
object. An example of an lvalue expression is *P, where P is any
expression evaluating to a nonnull pointer. A modifiable lvalue is an
identifier or expression that relates to an object that can be
accessed and legally changed in memory. A const pointer to a
constant, for example, is not a modifiable lvalue. A pointer to a
constant can be changed (but its dereferenced value cannot).

26

Borland C++ Programmer's Guide

Historically, the I stood for "left," meaning that an lvalue could legally stand on the left (the receiving end) of an assignment statement. Now only modifiable lvalues can legally stand to the left of
an assignment statement. For example, if a and bare nonconstant
integer identifiers with properly allocated memory storage, they
are both modifiable lvalues, and assignments such as a = 1; and b
=a + b are legal.
Rvalues

Types and
storage classes

The expression a + b is not an lvalue: a + b = a is illegal because the
expression on the left is not related to an object. Such expressions
are often called rvalues (short for right values).

Associating identifiers with objects requires that each identifier
has at least two attributes: storage class and type (sometimes
referred to as data type). The Borland c++ compiler deduces
these attributes from implicit or explicit declarations in the source
code.
Storage class dictates the location (data segment, register, heap, or
stack) of the object and its duration or lifetime (the entire running
time of the program, or during execution of some blocks of code).
Storage class can be established by the syntax of the declaration,
by its placement in the source code, or by both of these factors.
The type, as explained earlier, determines how much memory is
allocated to an object and how the program will interpret the bit
patterns found in the object's storage allocation. A given data type
can be viewed as the set of values (often implementation-dependent) that identifiers of that type can assume, together with the set
of operations allowed on those values. The special compile-time
operator, sizeof, lets you determine the size in bytes of any
standard or user-defined type; see page 87 for more on this
operator.

Scope
The scope of an identifier is that part of the program in which the
identifier can be used to access its object. There are five categories
of scope: block (or local), function, function prototype, file, and class
(C++ only). These depend on how and where identifiers are
declared.

Chapter 2, Language structure

27

Block scope

The scope of an identifier with block (or local) scope starts at the
declaration point and ends at the end of the block containing the
declaration (such a block is known as the enclosing block).
Parameter declarations with a function definition also have block
scope, limited to the scope of the block that defines the function.

Function scope

The only identifiers having function scope are statement labels.
Label names can be used with gata statements anywhere in the
function in which the label is declared. Labels are declared implicitly by writing label_name: followed by a statement. Label names
must be unique within a function.

Function prototype
scope

Identifiers declared within the list of parameter declarations in a
function prototype (not part of a function definition) have
function prototype scope. This scope ends at the end of the
function prototype.

File scope

File scope identifiers, also known as globals, are declared outside
of all blocks and classes; their scope is from the point of
declaration to the end of the source file.

Class scope (C++)

For now, think of a class as a named collection of members, including data structures and functions that act on them. Class
scope applies to the names of the members of a particular class.
Classes and their objects have many special access and scoping
rules; see pages 111 to 124.

Scope and name
spaces

Name space is the scope within which an identifier must be unique.
There are four distinct classes of identifiers in C:

Structures, classes, and
enumerations are in the
same name space in C++.

28

1. gata label names. These must be unique within the function in
which they are declared.
2. Structure, union, and enumeration tags. These must be unique
within the block in which they are defined. Tags declared outside of any function must be unique within all tags defined
externally.
3. Structure and union member names. These must be unique
within the structure or union in which they are defined. There
is no restriction on the type or offset of members with the
same member name in different structures.

Borland C++ Programmer's Guide

4. Variables, typedefs, functions, and enumeration members.
These must be unique within the scope in which they are
defined. Externally declared identifiers must be unique among
externally declared variables.

Visibility
The visibility of an identifier is that region of the program source
code from which legal access can be made to the identifier's associated object.
Scope and visibility usually coincide, though there are circumstances under which an object becomes temporarily hidden by the
appearance of a duplicate identifier: The object still exists but the
original identifier cannot be used to access it until the scope of the
duplicate identifier is ended.
Visibility cannot exceed
scope, but scope can
exceed visibility.

int ii char
i = 3i

double
i

ch

Chi

ii

3.0e3i

=

=

+= Ii

II auto by default
II int i and char ch in scope and visible

'A' i

II double i in scope and visible
II int i=3 in scope but hidden
II char ch in scope and visible
II double i out of scope
II int i visible and = 4
II char ch still in scope & visible = 'A'
II int i and char ch out of scope

~

Again, special rules apply to hidden class names and class
member names: Special C++ operators allow hidden identifiers to
be accessed under certain conditions (see page 112).

Duration
Duration, closely related to storage class, defines the period
during which the declared identifiers have real, physical objects
allocated in memory. We also distinguish between compile-time
and run-time objects. Variables, for instance, unlike typedefs and
types, have real memory allocated during run time. There are
three kinds of duration: static, local, and dynamic.

Chapter 2, Language structure

29

Static duration

Objects with static duration are allocated memory as soon as execution is underway; this storage allocation lasts until the program
terminates. Static duration objects usually reside in fixed data
segments allocated according to the memory model in force. All
functions, wherever defined, are objects with static duration. All
variables with file scope have static duration. Other variables can
be given static duration by using the explicit static or extern
storage class specifiers.
Static duration objects are initialized to zero (or null) in the
absence of any explicit initializer or, in C++, constructor.
Static duration must not be confused with file or global scope. An
object can have static duration and local scope.

Local duration

An object with local duration
a/so has local scope, since it
does not exist outside of its
enclosing block. The converse is not true: A local
scope object can have
static duration.

Local duration objects, also known as automatic objects, lead a
more precarious existence. They are created on the stack (or in a
register) when the enclosing block or function is entered. They are
deallocated when the program exits that block or function. Local
duration objects must be explicitly initialized; otherwise, their
contents are unpredictable. Local duration objects always must
have local or function scope. The storage class specifier auto may
be used when declaring local duration variables, but is usually
redundant, since auto is the default for variables declared within
a block.
When declaring variables (for example, int, char, float), the
storage class specifier register also implies auto; but a request (or
hint) is passed to the compiler that the object be allocated a
register if possible. Borland C++ can be set to allocate a register to
a local integral or pointer variable, if one is free. If no register is
free, the variable is allocated as an auto, local object with no
warning or error.

Dynamic duration

Dynamic duration objects are created and destroyed by specific
function calls during a program. They are allocated storage from a
special memory reserve known as the heap, using either standard
library functions such as malloc, or by using the C++ operator
new. The corresponding dealloca tians are made using free or
delete.

30

Borland C++ Programmer's Guide

Translation units
The term translation unit refers to a source code file together with
any included files, but less any source lines omitted by conditional preprocessor directives. Syntactically, a translation unit is
defined as a sequence of external declarations:

transla tion-un it:
external-declaration
translation-unit external-declaration
external-declaration
fu nction-defin itioll
declaration
For more details, see
"External dec/orations and
definitions" on page 36.

The word external has several connotations in C; here it refers to
declarations made outside of any function, and which therefore
have file scope. (External linkage is a distinct property; see the
following section, "Linkage.") Any declaration that also reserves
storage for an object or function is called a definition (or defining
declaration).

Linkage
An executable program is usually created by compiling several independent translation units, then linking the resulting object files
with preexisting libraries. A problem arises when the same identifier is declared in different scopes (for example, in different files),
or declared more than once in the same scope. Linkage is the
process that allows each instance of an identifier to be associated
correctly with one particular object or function. All identifiers
have one of three linkage attributes, closely related to their scope:
external linkage, internal linkage, or no linkage. These attributes
are determined by the placement and format of your declarations,
together with the explicit (or implicit by default) use of the
storage class specifier static or extern.
Each instance of a particular identifier with external linkage represents the same object or function throughout the entire set of files
and libraries making up the program. Each instance of a particular identifier with internal linkage represents the same object or
function only within one file. Identifiers with no linkage represent
unique entities.
External and internal linkage rules are as follows:

Chapter 2, Language structure

31

1. Any object or file identifier having file scope will have internal
linkage if its declaration contains the storage class specifier
static.

For C++, if the same identifier appears with both internal and
external linkage within the same file, the identifier will have
external linkage. In C, it will have internal linkage.
2. If the declaration of an object or function identifier contains
the storage class specifier extern, the identifier has the same
linkage as any visible declaration of the identifier with file
scope. If there is no such visible declaration, the identifier has
external linkage.
3. If a function is declared without a storage class specifier, its
linkage is determined as if the storage class specifier extern
had been used.
4. If an object identifier with file scope is declared without a
storage class specifier, the identifier has external linkage.
The following identifiers have no linkage attribute:
1. any identifier declared to be other than an object or a function
(for example, a typedef identifier)
2. function parameters
3. block scope identifiers for objects declared without the storage
class specifier extern

Name mangling

When a C++ module is compiled, the compiler generates function
names that include an encoding of the function's argument types.
This is known as name mangling. It makes overloaded functions
possible, and helps the linker catch errors in calls to functions in
other modules. However, there are times when you won't want
name mangling. When compiling a C++ module to be linked with
a module that does not have mangled names, the C++ compiler
has to be told not to mangle the names of the functions from the
other module. This situation typically arises when linking with
libraries or .OBJ files compiled with a C compiler.
To tell the C++ compiler not to mangle the name of a function,
simply declare the function as extern "C", like this:
extern "CO void Cfunc( int );

This declaration tells the compiler that references to the function
Cfunc should not be mangled.

32

Borland C++ Programmer's Guide

You can also apply the extern "e" declaration to a block of names:
extern "C" {
void Cfuncl( int ) i
void Cfunc2( int ) i
void Cfunc3( int )i
}i

As with the declaration for a single function, this declaration tells
the compiler that references to the functions Cfunc1, Cfunc2, and
Cfunc3 should not be mangled. You can also use this form of
block declaration when the block of function names is contained
in a header file:
extern "C" {
#include "locallib.h"
}i

Declaration syntax
All six interrelated attributes (storage class, type, scope, visibility,
duration, and linkage) are determined in diverse ways by

declarations.
Declarations can be defining declarations (also known simply as definitions) or referencing declarations (sometimes known as nondefining declarations). A defining declaration, as the name implies,
performs both the duties of declaring and defining; the nondefining declarations require a definition to be added somewhere in
the program. A referencing declaration simply introduces one or
more identifier names into a program. A definition actually
allocates memory to an object and associates an identifier with
that object.

Tentative
definitions

The ANSI C standard introduces a new concept: that of the
tentative definition. Any external data declaration that has no
storage class specifier and no initializer is considered a tentative
definition. If the identifier declared appears in a later definition,
then the tentative definition is treated as if the extern storage class
specifier were present. In other words, the tentative definition
becomes a simple referencing declaration.

Chapter 2, Language structure

33

If the end of the translation unit is reached and no definition has
appeared with an initializer for the identifier, then the tentative
definition becomes a full definition, and the object defined has
uninitialized (zero-filled) space reserved for it. For example,
int
int

Xi

/*legal, one copy of

Xi

int Yi
int y = 4i
int z
int z

-..

Possible
declarations

= 5i
= 6i

X

is reserved */

/* legal, y is initialized to 4 */
/* not legal, both are initialized definitions */

Unlike ANSI C, C++ doesn't have the concept of a tentative
declaration; an external data declaration without a storage class
specifier is always a definition.

The range of objects that can be declared includes
• variables
functions
II classes and class members (C++)
• types
• structure, union, and enumeration tags
• structure members
II union members
• arrays of other types
• enumeration constants
• statement labels
1:11 preprocessor macros
II

The full syntax for declarations is shown in the following tables.
The recursive nature of the declarator syntax allows complex declarators. We encourage the use of typedefs to improve legibility.

34

Borland C++ Programmer's Guide

Table 2.1
Borland C++ declaration
syntax

declaration:
 ;
asm-declaration
Junction-declaration
linkage-specification
decl-specifier:
s tora ge-class-specifier
type-specifier
Jct-specifier
friend (C++ specific)
typedef

decl-specifiers:
 decl-specifier
storage-class-specifier:
auto
register
static
extern

Jct-specifier: (C++ specific)
inline
virtual

type-specifier:
simple-type-name
class-specifier
enum-specifier
elaborated-type-specifier
const
volatile

simple-type-name:
class-name
typedef-name
char
short

int
long
signed
unsigned
float
double
void

elaborated-type-specifier:
class-key identifier
class-key class-name
enum enum-name
class-key: (C++ specific)
class
struct
union

enum-specifier:
enum  {  }
mum-list:
enumerator
enumerator-list, enumerator
enumerator:
identifier
identifier =constant-expression
cons tan t-expression:
conditional-expression
linkage-specification: (C++ specific)
extern string {  }
extern string declaration
declaration-list:
declaration
declaration-list; declaration

For the following table, note that there are restrictions on the
number and order of modifiers and qualifiers. Also, the modifiers
listed are the only addition to the declarator syntax that are not
ANSI C or C++. These modifiers are each discussed in greater
detail starting on page 47.

Chapter 2, Language structure

35

Table 2.2: Borland C++ declarator syntax
declarator-list:
in it-declarator
declarator-list , in it-declarator

class-name (C++ specific)
- class-lzame (C++ specific)
typedef-lzame

in it-declarator:
declarator 

type-name:
type-specifier 

declarator:
dname
modifier-list
ptr-operator declarator
declarator ( parameter-declaration-list ) 
(The  is for c++ only.)
declarator [  I
( declarator)

abstract-declarator:
ptr-operator 
 ( arglllnent-declaration-list ) 
 [  I
(abstract-declarator)

modifier-list:
modifier
modifier-list modifier

arg-declaratiOll-list:
argumellt-declaratiOll
arg-declaration-list , argument-declaration

modifier:
cdecl
pascal
interrupt
near
far
huge

argulllent-declaration:
decl-specifiers declarator
decl-specifiers declarator =expression (C++ specific)
decl-specifiers 
decl-specifiers  =expression (C++ specific)

ptr-operator:
* 
&  (C++ specific)
class-name :: *  (C++ specific)
cv-qualifier-list:
cv-qllalifier 
cv-qualifier
canst
volatile

dllame:
name

External
declarations and
definitions

argllment-declaratioll-list:

arg-declaratioll-list, .. .
 ... (C++ specific)

fct-defiIllHoll:
 declarator  fct-body
fct-body:
compoulUt-statemellt
initializer:
=expression
={initializer-list I
( expression-list) (C++ specific)
initializer-list:
expression
initializer-list , expression
( illitializer-list <,> I

The storage class specifiers auto and register cannot appear in an
external declaration (see "Translation units," page 31). For each
identifier in a translation unit declared with internal linkage, there
can be no more than one external definition.
An external definition is an external declaration that also defines
an object or function; that is, it also allocates storage. If an
identifier declared with external linkage is used in an expression
(other than as part of the operand of sizeof), there must be exactly
one external definition of that identifier somewhere in the entire
program.

36

Borland C++ Programmer's Guide

Borland c++ allows later re-declarations of external names, such
as arrays, structures, and unions, to add information to earlier
declarations. For example,
int a [] i
struct mystructi

II no size
II tag only, no member declarators

int a[3] = {l, 2, 3}i II supply size and initialize
struct mystruct {
int i, ji
II add member declarators
}i

The following table covers class declaration syntax. Page 105
covers C++ reference types (closely related to pointer types) in
detail.
Table 2,3: Borland C++ class declarations (C++ only)

class-specifier:
class-head {  }
class-head:
class-key  
class-key class-name 
member-list:
member-declaration 
access-specifier: 
member-declaration:
  ;
function-definition <;>
qualified-l1ame ;
member-declarator-list:
member-declarator
member-declarator-list, member-declarator
member-declarator:
declarator 
 : c011stant-expression
pure-specifier:
=0

base-spec:
: base-list
base-list:
base-specifier
base-list, base-specifier
base-specifier:
class-name
virtual  class-11ame

Chapter 2, Language structure

access-specifier  class-name
access-specifier:
private
protected
public

conversio11-function-name:
operator conversion-type-name
conversion-type-name:
type-specifiers 
ctor-initializer:
: 111e111-initializer-list
111em-in itializer-l ist:
me111-i nitia Iizer
mem-initializer, 111em-initializer-list
me111- in itia Iizer:
class name (  )
identifier (  )
operator-function-name:
operator operator
operator: one of
new delete sizeof

+
&
+=
&=
-++

1

-=
1=
!=

*=
«
<=

1=

»
>=
->*

%
=
%=
»=
&&
->

1\

<>
1\=

«=
II
()

[]

37

Type specifiers
The type specifier with one or more optional modifiers is used to
specify the type of the declared identifier:
int ii
unsigned char chl, ch2i

II declare i as a signed integer
II declare two unsigned chars

By long-standing tradition, if the type specifier is omitted, type
signed int (or equivalently, int) is the assumed default. However,

in C++ there are some situations where a missing type specifier
leads to syntactic ambiguity, so C++ practice uses the explicit
entry of all int type specifiers.

Type taxonomy
There are four basic type categories: void, scalar, function, and
aggregate. The scalar and aggregate types can be further divided
as follows:
1:1

Scalar: arithmetic, enumeration, pointer, and reference types
(C++)

c Aggregate: array, structure, union, and class types (C++)

Types can also be divided into fundamental and derived types. The
fundamental types are void, char, int, float, and double, together
with short, long, signed, and unsigned variants of some of these.
The derived types include pointers and references to other types,
arrays of other types, function types, class types, structures, and
unions.
~

.A class object, for example, can hold a number of objects of
different types together with functions for manipulating these
objects, plus a mechanism to control access and inheritance from
other classes.
Given any nonvoid type type (with some provisos), you can
declare derived types as follows:

38

Borland C++ Programmer's Guide

Table 2.4
Declaring types

Note that type& var, type
&var, and type & var are all
equivalent.

type t;

An object of type type

type army[lO];

Ten types: array[O] - army[9]

type *ptr;

ptr is a pointer to type

type &ref = t;

ref is a reference to type (C++)

type func(void);

func returns value of type type

void func1 (type t);

func1 takes a type type parameter

struct st {type t1; type t2J;

structure st holds two types

And here's how you could declare derived types in a class:
II class ct holds ptr to type plus a function
II taking a type parameter (ett)

class ct {
type *ptr;
public:
void func(type*);

Type void

void is a special type specifier indicating the absence of any

values. It is used in the following situations:
c++ handles func() in a
special manner. See
"Declarations and
prototypes" on page 61 and
code examples on page 62.

[J

An empty parameter list in a function declaration:
int func(void);

l'l

When the declared function does not return a value:

C

As a generic pointer: A pointer to void is a generic pointer to
anything:

I'J

In typecasting expressions:

void func(int n); II return value

void *ptr;

The fundamental
types

II func takes no arguments

II ptr can later be set to point to any object

extern int errfunc();

II returns an error code

(void) errfunc();

II discard return value

The fundamental type specifiers are built from the following
keywords:

signed and unsigned are
modifiers that can be
applied to the integral types.

Chapter 2, Language structure

char
double
float

int
long
short

signed
unsigned

39

From these keywords, you can build the integral and floatingpoint types, which are together known as the arithmetic types. The
include file limits.h contains definitions of the value ranges for all
the fundamental types.
Integral types

char, short, int, and long, together with their unsigned variants,

are all considered integral data types. The integral type specifiers
are as follows, with synonyms listed on the same line:
Table 2.5
Integral types

char, signed char .
Synonyms if default char set to signed
unsigned char
Synonyms if default char set to unsigned
char, unsigned char
signed char
int, signed int
unsigned, unsigned int
short, short int, signed short int
unsigned short, unsigned short int
long, long int, signed long int
unsigned long, unsigned long int

At most, one of signed and unsigned can be used with char,
short, int, or long. If you use the keywords signed and unsigned
on their own, they mean signed int and unsigned int,
respectively.
In the absence of unsigned, signed is usually assumed. An exception arises with char. Borland C++ lets you set the default for char
to be signed or unsigned. (The default, if you don't set it yourself,
is signed.) If the default is set to unsigned, then the decla:r:ation
char ch declares ch as unsigned. You would need to use signed
char ch to override the default. Similarly, with a signed default for
char, you would need an explicit unsigned char ch to declare an
unsigned char.

At most, one of long and short can be used with int. The
keywords long and short used on their own mean long int and
short int.

ANSI C does not dictate the sizes or internal representations of
these types, except to insist that short, int, and long form a nondecreasing sequence with "short <= int <= long." All three types
can legally be the same. This is important if you want to write
portable code aimed at other platforms.
In Borland C++, the types int and short are equivalent, both being
16 bits. long is a 32-bit object. The signed varieties are all stored in
2's complement format using the most significant bit (MSB) as a

40

Borland C++ Programmer's Guide

sign bit: 0 for positive, 1 for negative (which explains the ranges
shown in Table 1.9 on page 19). In the unsigned versions, all bits
are used to give a range of 0 - (2 11 - 1), where n is 8, 16, or 32.
Floating-point types

The representations and sets of values for the floating-point types
are implementation dependent; that is, each implementation of C
is free to define them. Borland C++ uses the IEEE floating-point
formats. (Appendix A, "ANSI implementation-specific
standards," tells more about implementation-specific items.)
float and double are 32- and 64-bit floating-point data types, respectively. long can be used with double to declare an 80-bit precision floating-point identifier: long double test_case, for example.

Table 1.9 on page 19 indicates the storage allocations for the
floating-point types.
Standard conversions

When you use an arithmetic expression, such as a + b, where a
and b are different arithmetic types, Borland C++ performs certain
internal conversions before the expression is evaluated. These
standard conversions include promotions of "lower" types to
"higher" types in the interests of accuracy and consistency.
Here are the steps Borland C++ uses to convert the operands in an
arithmetic expression:
1. Any small integral types are converted as shown in Table 2.6.
After this, any two values associated with an operator are
either int (including the long and unsigned modifiers, double,
float, or long double).

2. If either operand is of type long double, the other operand is
converted to long double.
3. Otherwise, if either operand is of type double, the other
operand is converted to double.
4. Otherwise, if either operand is of type float, the other operand
is converted to float.
5. Otherwise, if either operand is of type unsigned long, the
other operand is converted to unsigned long.
6. Otherwise, if either operand is of type long, then the other operand is converted to long.
7. Otherwise, if either operand is of type unsigned, then the
other operand is converted to unsigned.
8. Otherwise, both operands are of type int.

Chapter 2, Language structure

41

The result of the expression is the same type as that of the two
operands.
Table 2.6
Methods used in standard
arithmetic conversions

Special char, int, and
enum conversions
The conversions discussed in
this section are specific to
Borland C++.

Type

Converts to

Method

char

int

unsigned char
signed char
short
unsigned short
enum

int
int
int
unsigned int
int

Zero or sign-extended
(depends on default char type)
Zero-filled high byte (always)
Sign-extended (always)
Same value
Same value
Same value

Assigning a signed character object (such as a variable) to an
integral object results in automatic sign extension. Objects of type
signed char always use sign extension; objects of type unsigned
char always set the high byte to zero when converted to int.
Converting a longer integral type to a shorter type truncates the
higher order bits and leaves low-order bits unchanged.
Converting a shorter integral type to a longer type either sign
extends or zero fills the extra bits of the new value, depending on
whether the shorter type is signed or unsigned, respectively.

Initialization
Initializers set the initial value that is stored in an object (variables,
arrays, structures, and so on). If you don't initialize an object, and
it has static duration, it will be initialized by default in the
following manner:
If it has automatic storage
duration, its value is
indeterminate.

to zero if it is of an arithmetic type
.. to null if it is a pointer type

II

The syntax for initializers is as follows:

initializer
= expression
= {initializer-list} <,>}
(expression list)
initializer-list
expression
initializer-list, expression
{initializer-list} <,>}
Rules governing initializers are

42

Borland C++ Programmer's Guide

1. The number of initializers in the initializer list cannot be larger
than the number of objects to be initialized.
2. The item to be initialized must be an object type or an array of
unknown size.
3. For C (not required for C++), all expressions must be constants
if they appear in one of these places:
a. in an initializer for an object that has static duration
b. in an initializer list for an array, structure, or union (expressions using sizeof are also allowed)
4. If a declaration for an identifier has block scope, and the
identifier has external or internal linkage, the declaration
cannot have an initializer for the identifier.
5. If there are fewer initializers in a brace-enclosed list than there
are members of a structure, the remainder of the structure is
initialized implicitly in the same way as objects with static
storage duration.
Scalar types are initialized with a single expression, which can optionally be enclo'sed in braces. The initial value of the object is that
of the expression; the same constraints for type and conversions
apply as for simple assignments.
For unions, a brace-enclosed initializer initializes the member that
first appears in the union's declaration list. For structures or
unions with automatic storage duration, the initializer must be
one of the following:

Arrays, structures, and
unions

IJ

an initializer list as described in the following section

fl

a single expression with compatible union or structure type. In
this case, the initial value of the object is that of the expression.

You initialize arrays and structures (at declaration time, if you
like) with a brace-enclosed list of initializers for the members or
elements of the object in question. The initializers are given in
increasing array subscript or member order. You initialize unions
with a brace-enclosed initializer for the first member of the union.
For example, you could declare an array days, intended to count
how many times each day of the week appears in a month (and
assuming that each day will appear at least once), as follows:
int days [7] = { 1

Chapter 2, Language structure

f

1

f

1

f

1 1, 1
f

f

1 }

43

Use these rules to initialize character arrays and wide character
arrays:
1. You can initialize arrays of character type with a literal string,
optionally enclosed in braces. Each character in the string, including the null terminator, initializes successive elements in
the array. For example, you could declare
char name [] = { "Unknown" };

which sets up an eight-element array, whose elements are 'U'
(for name[O]), 'n' (for name[1]), and so on (and including a null
termina tor).
2. You can initialize a wide character array (one that is
compatible with wchar_t) by using a wide string literal,
optionally enclosed in braces. As with character arrays, the
codes of the wide string literal initialize successive elements of
the array.
Here is an example of a structure initialization:
struct mystruct {
int i;
char str[21];
double d;
s = { 20, "Borland", 3.141 };

Complex members of a structure, such as arrays or structures, can
be initialized with suitable expressions inside nested braces. You
can eliminate the braces, but you must follow certain rules, and it
isn't recommended practice.

Simple
declarations Simple declarations of variable identifiers have the following
pattern:

data-type varl <=initl>, var2 <=init2>, ... ;
where varl, var2, ... are any sequence of distinct identifiers with
optional initializers. Each of the variables is declared to be of type
data-type. For example,
int x = I, y = 2;

creates two integer variables called x and y (and initializes them
to the values 1 and 2, respectively).

44

Borland C++ Programmer's Guide

These are all defining declarations; storage is allocated and any
optional initializers are applied.
The initializer for an automatic object can be any legal expression
that evaluates to an assignment-compatible value for the type of
the variable involved. Initializers for static objects must be
constants or constant expressions.
~

Storage class
specifiers

In C++, an initializer for a static object can be any expression involving constants and previously declared variables and
functions.

A storage class specifier, or a type specifier, must be present in a
declaration. The storage class specifiers can be one of the
following:
auto
extern

register
static

typedef

Use of storage class
specifier auto

The storage class specifier auto is used only with local scope
variable declarations. It conveys local (automatic) duration, but
since this is the default for all local scope variable declarations, its
use is rare.

Use of storage class
specifier extern

The storage class specifier extern can be used with function and
variable file scope and local scope declarations to indicate external
linkage. With file scope variables, the default storage class
specifier is extern. When used with variables, extern indicates
that the variable has static duration. (Remember that functions
always have static duration.) See page 32 for information on using
extern to prevent name mangling when combining C and C++
code.

Use of storage class
specifier regis"ter

The storage class specifier register is allowed only for local
variable and function parameter declarations. It is equivalent to
auto, but it makes a request to the compiler that the variable
should be allocated to a register if possible. The allocation of a
register can significantly reduce the size and improve the performance of programs in many situations. However, since
Borland C++ does a good job of placing variables in registers, it is
rarely necessary to use the register keyword.

Chapter 2, Language structure

45

Borland C++ lets you select register variable options from the
Options I Compiler I Optimizations Options dialog box. If you
check Automatic, Borland C++ will try to allocate registers even if
you have not used the register storage class specifiers.

Use of storage class
specifier static

~

Use of storage class
specifier typedef

The storage class specifier static can be used with function and
variable file scope and local scope declarations to indicate internal
linkage. static also indicates that the variable has static duration.
In the absence of constructors or explicit initializers, static
variables are initialized with a or null.
In C++, a static data member of a class has the same value for all
instances of a class. A static member function of a class can be
invoked independently of any class instance.
The keyword typedef indicates that you are defining a new data
type specifier rather than declaring an object. typedef is included
as a storage class specifier because of syntactical rather than
functional similarities.
static long int biggy;
typedef long int BIGGY;

The first declaration creates a 32-bit, long intI static-duration
object called biggy. The second declaration establishes the
identifier BlGGY as a new type specifier, but does not create any
run-time object. BlGGY can be used in any subsequent declaration
where a type specifier would be legal. For example,
extern BIGGY salary;

has the same effect as
extern long int salarYi

Although this simple example can be achieved by #define BIGGY
long intI more complex typedef applications achieve more than is
possible with textual substitutions.
Important!

typedef does not create new data types; it merely creates useful

mnemonic synonyms or aliases for existing types. It is especially
valuable in simplifying complex declarations:
typedef double (*PFD) ()i
PFD array-pfd[10];
/* array-pfd is an array of 10 pointers to functions
returning double */

46

Borland C++ Programmer's Guide

You can't use typedef identifiers with other data-type specifiers:
1* ILLEGAL *1

unsigned BIGGY pay;

Modifiers
In addition to the storage class specifier keywords, a declaration
can use certain modifiers to alter some aspect of the identifier /
object mapping. The modifiers available with Borland C++ are
summarized in Table 2.7.

The canst modifier

The const modifier prevents any assignments to the object or any
other side effects, such as increment or decrement. A const
pointer cannot be modified, though the object to which it points
can be. Consider the following examples:

The modifier const used
by itself is equivalent to

const
const
char
char

const into

float
*const
const

pi
maxint
str
*str2

= 3.1415926;
= 32767;
= "Hello,

world" ; II A constant pointer

= "Hello, world" ; 1* A pointer to a constant
char *1

Given these, the following statements are illegal:
pi

= 3.0;

1* Assigns a value to a canst *1
1* Increments a canst *1
1* Points str to something else *1

maxinttt;
str = "Hi, there! ";
=

Note, however, that the function call strcpy (str, "Hi there!") is
legal, since it does a character-by-character copy from the string
literal "Hi, there!" into the memory locations pointed to by str.
I

~

In C++, const also hides the const object and prevents external
linkage. You need to use extern const. A pointer to a const can't
be assigned to a pointer to a non-const (otherwise, the const
value could be assigned to using the non-const pointer). For
example,
char *str3

=

str2

1* disallowed *1

Only const member functions can be called for a const object.
Table 2.7
Borland C++ modifiers

c++ extends const and
volatile to include classes
and member functions.

Use with

Use

canst

Variables only

Prevents changes to object.

volatile

Variables only

Prevents register allocation and some
optimization. Warns compiler that

Modifier

Chapter 2, Language structure

47

Table 2,7: Borland C++ modifiers (continued)

object may be subject to outside
change during evaluation.
Borland C++ extensions

cdecl

Functions

Forces C argument-passing
convention. Affects Linker and linktime names.

cdecl

Variables

Forces global identifier case-sensitivity
and leading underscores.

pascal

Functions

Forces Pascal argument-passing
convention. Affects Linker and linktime names.

pascal

Variables

Forces global identifier caseinsensitivity with no leading
underscores.

interrupt

Functions

Function compiles with the additional
register-housekeeping code needed
when writing interrupt handlers.

near,
far,
huge

Pointer types

Overrides the default pointer
type specified by the current
memory model.

_cs,
_ds,
_es,
_seg,
- ss

Pointer types

Segment pointers.
See page 350.

near,
far,

Functions

Overrides the default function
type specified by the current
memory

huge
model.
near,
far

Variables

_export

Functions/ classes Tells the compiler which functions or
classes to export.

loadds

Functions

Sets DS to point to the current
data segment.

_saveregs

Functions

fastcall

Functions

Preserves all register values
(except for return values)
during execution of the function.
Forces register parameter passing
convention. Affects the linker and
link-time names.

-

-

48

Directs the placement of
the object in memory.

Borland C++ Programmer's Guide

The interrupt function
modifier

The interrupt modifier is specific to Borland C++. interrupt
functions are designed to be used with the 8086/8088 interrupt
vectors. Borland C++ will compile an interrupt function with extra
function entry and exit code so that registers AX, BX, CX, DX, SI,
DI, ES, and DS are preserved. The other registers (BP, SP, SS, CS,
and IP) are preserved as part of the C-calling sequence or as part
of the interrupt handling itself. The function will use an iret
instruction to return, so that the function can be used to service
hardware or software interrupts. Here is an example of a typical
interrupt definition:
void interrupt myhandler()
{

You should declare interrupt functions to be of type void.
Interrupt functions can be declared in any memory model. For all
memory models except huge, DS is set to the program data
segment. For the huge model, DS is set to the module's data
segment.

The volatile modifier
In C++, volatile has a special
meaning for class member
functions. If you've declared
a volatile object, you can
only use its volatile member
functions.

The volatile modifier indicates that the object may be modified;
not only by you, but also by something outside of your program,
such as an interrupt routine or an I/O port. Declaring an object to
be volatile warns the compiler not to make assumptions concerning the value of the object while evaluating expressions containing it, since the value could (in theory) change at any moment. It
also prevents the compiler from making the variable a register
variable.
volatile int ticks;
interrupt
timer ()
{

ticks++ ;
wait(int interval)
{

ticks = 0;
while (ticks < interval);

II Do nothing

These routines (assuming timer has been properly associated with
a hardware clock interrupt) implement a timed wait of ticks

Chapter 2, Language structure

49

specified by the argument interval. A highly optimizing compiler
might not load the value of ticks inside the test of the while loop,
since the loop doesn't change the value of ticks.

The cdecl and pascal
modifiers
Page 31 tells how to use
extern, which allows C
names to be referenced
from a C++ program.

Borland C++ allows your programs to easily call routines written
in other languages, and vice versa. When you mix languages like
this, you have to deal with two important issues: identifiers and
parameter passing.
In Borland C++, all global identifiers are saved in their original
case (lower, upper, or mixed) with an underscore C) prepended to
the front of the identifier, unless you have selected the -u - option
or unchecked the Generate Underbars box in the Options I
Compiler I Advanced Code Generation dialog box.

pascal
In Pascal, global identifiers are not saved in their original case,
nor are underscores prep ended to them. Borland C++ lets you
declare any identifier to be of type pascal; the identifier is converted to uppercase, and no underscore is prepended. (If the identifier is a function, this also affects the parameter-passing sequence used; see "Function type modifiers," page 52, for more
details.)
The -p compiler option or
Calling Convention Pascal in
the Options I Compiler I
Entry I Exit Code dialog box
causes all functions (and
pointers to those functions)
to be treated as if they were
of type pascal.

The pascal modifier is specific to Borland C++; it is intended for
functions (and pointers to functions) that use the Pascal parameter-passing sequence. Also, functions declared to be of type
pascal can still be called from C routines, so long as the C routine
sees that the function is of type pascal.
pascal putnums(int i, int j, int k)
{

printf("And the answers are:

%d, %d, and %d\n",i,j,k);

Functions of type pascal cannot take a variable number of
arguments, unlike functions such as printf. For this reason, you
cannot use an ellipsis (. .. ) in a pascal function definition.
..

Most of the Windows API functions are pascal functions.

cdecl
Once you have compiled with Pascal calling convention turned
on (using the -p option or IDE Options I Compiler I Entry/Exit

50

. Borland C++ Programmer's Guide

Code), you may want to ensure that certain identifiers have their
case preserved and keep the underscore on the front, especially if
they're C identifiers from another file. You can do so by declaring
those identifiers to be cdecl. (This also has an effect on parameter
passing for functions).
main must be declared as
cdec/: this is because the C
start-up code a/ways tries to
call main with the C calling
convention.

Like pascal, the cdecl modifier is specific to Borland C++. It is
used with functions and pointers to functions. It overrides the -p
option or IDE Options I Compiler I Entry /Exit Code compiler directive and allows a function to be called as a regular C function.
For example, if you were to compile the previous program with
the Pascal calling option set but wanted to use printf, you might
do something like this:
extern cdecl printf();
void putnums(int i, int j, int k);
cdecl main ()
{

putnums(l,4,9);
void putnums(int i, int j, int k)
{

printf("And the answers are:

%d, %d, and %d\n",i,j,k);

If you compile a program with the -p option or IDE Options I
Compiler I Entry /Exit Code, all functions used from the run-time
library will need to have cdecl declarations. If you look at the
header files (such as stdio.h), you'll see that every function is
explicitly defined as cdecl in anticipation of this.

The pointer modifiers

Borland C++ has eight modifiers that affect the pointer declarator
(*); that is, they modify pointers to data. These are near, far, huge,
_cs, _ds, _es, _seg, and _ss.
C lets you compile using one of several memory models. The
model you use determines (among other things) the internal
format of pointers. For example, if you use a small data model
(tiny, small, medium), all data pointers contain a 16-bit offset from
the data segment (OS) register. If you use a large data model
(compact,large, huge), all pointers to data are 32 bits long and
give both a segment address and an offset.
Sometimes, when using one size of data model, you want to
declare a pointer to be of a different size or format than the
current default. You do so using the pointer modifiers.

Chapter 2, Language structure

51

See the discussion starting on page 344 in Chapter 9 for an indepth explanation of near, far, and huge pointers, and page 345
for a description of normalized pointers. Also see the discussion
starting on page 350 for more on _cs, _ds, _es, _seg, and _ss.
Function type modifiers

The near, far, and huge modifiers can also be used as function
type modifiers; that is, they can modify functions and function
pointers as well as data pointers. In addition, you can use the
_export, _Ioadds, and _saveregs modifiers to modify functions.
The near, far, and huge function modifiers can be combined with
cdecl or pascal, but not with interrupt.
Functions of type huge are useful when interfacing with code in
assembly language that doesn't use the same memory allocation
as Borland C++.
A non-interrupt function can be declared to be near, far, or huge
in order to override the default settings for the current memory
model.
A near function uses near calls; a far or huge function uses far call
instructions.
In the tiny, small, and compact memory models, an unqualified
function defaults to type near. In the medium and large models,
an unqualified function defaults to type far. In the huge memory
model, it defaults to type huge.
A huge function is the same as a far function, except that the OS
register is set to the data segment address of the source module
when a huge function is entered, but left unset for a far function.

Br
~

The _export modifier makes the function exportable from
Windows. It's used in an executable (if you don't use smart
callbacks) or in a OLL; see page 323 of Chapter 8 for details. The
_export lTIodifier has no significance for DOS programs.
The _Ioadds modifier indicates that a function should set the OS
register, just as a huge function does, but does not imply near or
far calls. Thus, _Ioadds far is equivalent to huge.
The _saveregs modifier causes the function to preserve all
register values and restore them before returning (except for
explicit return values passed in registers such as AX or OX).
The _Ioadds and _saveregs modifiers are useful for writing lowlevel interface routines, such as mouse support routines.

52

Borland C++ Programmer's Guide

The _fastcall modifier is documented in Appendix A, "The
Optimizer" in the User's Guide.

Complex
declarations and
declarators
See Table 2.1 on page 35 for
the declarator syntax. The
definition covers both
identifier ond function
declorators.

Simple declarations have a list of comma-delimited identifiers
following the optional storage class specifiers, type specifiers, and
other modifiers.
A complex declaration uses a comma-delimited list of declarators
following the :various specifiers and modifiers. Within each declarator, there exists just one identifier, namely the identifier being
declared. Each of the de clara tors in the list is associated with the
leading storage class and type specifier.
The format of the declarator indicates how the declared dname is
to be interpreted when used in an expression. If type is any type,
and storage dass specifier is any storage class specifier, and if 01
and 02 are any two declarators, then the declaration

storage-dass-specifier type 01,02;
indicates that each occurrence of 01 or 02 in an expression will be
treated as an object of type type and storage class storage dass
specifier. The type of the dname embedded in the declarator will be
some phrase containing type, such as "type," "pointer to type,"
"array of type," "function returning type," or "pointer to function
returning type," and so on.
For example, in the declarations
int n, nao[], naf[3], *pn, *apn[], (*pan) [], &nr=n;
int f (void), *fnp,(void), (*pfn) (void);

each of the de clara tors could be used as rvalues (or possibly
lvalues in some cases) in expressions where a single int object
would be appropriate. The types of the embedded identifiers are
derived from their declarators as follows:

Chapter 2, Language structure

53

Table 2.8: Complex declarations
Declarator
syntax

Implied type of name

Example

type name;

type

int count;

type name [ ) ;

(open) array of type

int count[) ;

type name [3) ;

Fixed array of three elements, all of type
(name[O], name[1], and name[2])

int count [3);

type *name;

Pointer to type

int *count;

type *name [) ;

(open) array of pointers to type

int *count[) ;

type * (name [) ) ;

Same as above

int * (count [ ) ) ;

type (*name) [) ;

Pointer to an (open) array of type

int (*count)

type &name;

Reference to type (C++ only)

int &count;

type name ( ) ;

Function returning type

int count();

type *name() ;

Function returning pointer to type

int *count () ;

type * (name () ) ;

Same as above

int * (count ( ) ) ;

type (*name) () ;

Pointer to function returning type

int (*count)

[) ;

() ;

Note the need for parentheses in (*name)[] and (*name)O,·since the
precedence of both the array declarator [ ] and the function
declarator ( ) is higher than the 'pointer declarator *. The
parentheses in *(name[]) are optional.

Pointers
See page 85 for a discussion
of referencing and dereferencing.

Pointers fall into two main categories: pointers to objects and
pointers to functions. Both types of pointers are special objects for
holding memory addresses.
The two pointer classes have distinct properties, purposes, and
rules for manipulation, although they do share certain-Borland
C++ operations. Generally speaking, pointers to functions are
used to access functions and to pass functions as arguments to
other functions; performing arithmetic on pointers to functions is
not allowed. Pointers to objects, on the other hand, are regularly
incremented and decremented as you scan arrays or more
complex data structures in memory.

54

Borland C++ Programmer's Guide

Although pointers contain numbers with most of the characteristics of unsigned integers, they have their own rules and restrictions for assignments, conversions, and arithmetic. The examples
in the next few sections illustrate these rules and restrictions.

Pointers to
objects

A pointer of type "pointer to object of type" holds the address of
(that is, points to) an object of type. Since pointers are objects, you
can have a pointer pointing to a pointer (and so on). Other objects
commonly pointed at include arrays, structures, unions, and
classes.
The size of pointers to objects is dependent on the memory model
and the size and disposition of your data segments, possibly influenced by the optional pointer modifiers (discussed starting on
page 51).

Pointers to
functions

A pointer to a function is best thought of as an address, usually in
a code segment, where that function's executable code is stored;
that is, the address to which control is transferred when that function is called. The size and disposition of your code segments is
determined by the memory model in force, which in turn dictates
the size of the function pointers needed to call your functions.
A pointer to a function has a type called "pointer to function returning type," where type is the function's return type.

~

Under C++, which has stronger type checking, a pointer to a
function has type "pointer to function taking argument types type
and returning type." In fact, under C, a function defined with
argument types will also have this narrower type. For example,
void (*func)()i

In C, this is a pointer to a function returning nothing. In C++, it's a
pointer to a function taking no arguments and returning nothing.
In this example,
void (*func) (int) i

*func is a pointer to a function taking an int argument and returning nothing.

Chapter 2, Language structure

55

Pointer
declarations
See page 39 for details on
void.

A pointer must be declared as pointing to some particular type,
even if that type is void (which really means a pointer to
anything). Once declared, though, a pointer can usually be
reassigned so that it points to an object of another type. Borland
C++ lets you reassign pointers like this without typecasting, but
the compiler will warn you unless the pointer was originally
declared to be of type pointer to void. And in C, but not C++, you
can assign a void* pointer to a non-void* pointer.
If type is any predefined or user-defined type, including void, the
declara tion

Warning! You need to
initialize pointers before using
them.

type *ptr;

/* Danger--uninitialized pointer */

declares ptr to be of type "pointer to type." All the scoping,
duration, and visibility rules apply to the ptr object just declared.
A null pointer value is an address that is guaranteed to be
different from any valid pointer in use in a program. Assigning
the integer constant 0 to a pointer assigns a null pointer value to
it.
The mnemonic NULL (defined in the standard library header files,
such as stdio.h) can be used for legibility. All pointers can be
successfully tested for equality or inequality to NULL.
The pointer type "pointer to void" must not be confused with the
null pointer. The declaration
void *vptr;

declares that vptr is a generic pointer capable of being assigned to
by any "pointer to type" value, including null, without complaint.
Assignments without proper casting between a "pointer to type1"
and a "pointer to type2," where type1 and type2 are different
types, can invoke a compiler warning or error. If type1 is a
function and type2 isn't (or vice versa), pointer assignments are
illegal. If type1 is a pointer to void, no cast is needed. Under C, if
type2 is a pointer to void, no cast is needed.
Assignment restrictions also apply to pointers of different sizes
(near, far, and huge). You can assign a smaller pointer to a larger
one without error, but you can't assign a larger pointer to a
smaller one unless you are using an explicit cast. For example,

56

Borland C++ Programmer's Guide

char
char
char
fcp
hcp
fcp
ncp
ncp

Pointers and
constants

near *ncp;
far *fcp;
huge *hcp;
ncp;
fcp;
hcp;
fcp;
(char near*)fcp;

II
II
II
II
II

legal
legal
not legal
not legal
now legal

A pointer or the pointed-at object can be declared with the canst
modifier. Anything declared as a canst cannot be assigned to. It is
also illegal to create a pointer that might violate the nonassignability of a constant object. Consider the following examples:
int i·,

II i is an int

int * pi;
(uninitialized)

II pi is a pointer to int

int * const cp
const int ci

&i;

= 7',

const int * pci;
const int * const cpc

II cp is a constant pointer to int.

II ci is a constant int
II pci is a pointer to constant int

= &ci; II

cpc is a constant pointer to a

II constant int

The following assignments are legal:
i

=

*cp

ci;
=

ci;

=

II Assign const-int to
Ilobject-pointed-at-by-a-const-pointer
II Increment a pointer-to-const

ttpci;
pci

II Assign const-int to int

cpc;

II Assign a const-pointer-to-a-const to a
II pointer-to-const

The following assignments are illegal:
ci = 0;

II NO--cannot assign to a const-int

ci--;

II NO--cannot change a const-int

*pci = 3 ,.

II NO--cannot assign to an object
II pointed at by pointer-to-const

cp

II NO--cannot assign to a const-pointer,
II even if value would be unchanged

&ci;

CpCtt ;

Chapter 2, Language structure

II NO--cannot change const-pointer

57

pi

= pci;

II NO--if this assignment were allowed,
II you would be able to assign to *pci
II (a const value) by assigning to *pi.

Similar rules apply to the volatile modifier. Note that canst and
volatile can both appear as modifiers to the same identifier.

Pointer arithmetic
The internal arithmetic
performed on pointers
depends on the memory
model in force and the
presence of any overriding
pointer modifiers.

The difference between two
pointers only has meaning if
both pointers point into the
same array.

Pointer arithmetic is limited to addition, subtraction, and comparison. Arithmetical operations on object pointers of type "pointer
to type" automatically take into account the size of type; that is,
the number of bytes needed to store a type object.
When performing arithmetic with pointers, it is assumed that the
pointer points to an array of objects. Thus, if a pointer is declared
to point to type, adding an integral value to the pointer advances
the pointer by that number of objects of type. If type has size 10
bytes, then adding an integer 5 to a pointer to type advances the
pointer 50 bytes in memory. The difference has as its value the
number of array elements separating the two pointer values. For
example, if ptrl points to the third element of an array, and ptr2
points to the tenth element, then the result of ptr2 - ptrl would
be7.
When an integral value is added to or subtracted from a "pointer
to type," the result is also of type "pointer to type."
There is no such element as "one past the last element", of course,
but a pointer is allowed to assume such a value. If P points to the
last array element, P + 1 is legal, but P + 2 is undefined. If P points
to one past the last array element, P - 1 is legal, giving a pointer to
the last element. However, applying the indirection operator * to a
"pointer to one past the last element" leads to undefined behavior.
Informally, you can think of P + n as advancing the pointer by
(n * sizeof(type» bytes, as long as the pointer remains within the
legal range (first element to one beyond the last element).
Subtracting two pointers to elements of the same array object
gives an integral value of type ptrdiff_t defined in stddef.h (signed
long for huge and far pointers; signed int for all others). This
value represents the difference between the subscripts of the two
referenced elements, provided it is in the range of ptrdiff_t. In the
expression Pl - P2, where Pl and P2 are of type pointer to type
(or pointer to qualified type), Pl and P2 must point to existing.
elements or to one past the last element. If Pl points to the i-th

,/
58

Borland C++ Programmer's Guide

element, and P2 points to the j-th element, Pl - P2 has the value
(i - j).

Pointer
conversions

Pointer types can be converted to other pointer types using the
typecasting mechanism:
char *stri
int *ipi
str = (char *)ipi

More generally, the cast (type*) will convert a pointer to type
"pointer to type."

c++ reference
declarations

c++ reference types are closely related to pointer types. Reference
types create aliases for objects and let you pass arguments to functions by reference. C passes arguments only by value. In C++ you
can pass arguments by value or by reference. See page 105,
"Referencing," for complete details.

Arrays
The declaration
type declarator []

declares an array composed of elements of type. An array consists
of a contiguous region of storage exactly large enough to hold all
of its elements.
If an expression is given in an array declarator, it must evaluate to
a positive constant integer. The value is the number of elements in
the array. Each of the elements of an array is numbered from 0
through the number of elements minus one.

Multidimensional arrays are constructed by declaring arrays of
array type. Thus, a two-dimensional array of five rows and seven
columns called alpha is declared as
type

alpha [5] [7]

i

In certain contexts, the first array declarator of a series may have
no expression inside the brackets. Such an array is of indeter-

Chapter 2, Language structure

59

minate size. The contexts where this is legitimate are ones in
which the size of the array is not needed to reserve space.
For example, an extern declaration of an array object does not
need the exact dimension of the array, nor does an array function
parameter. As a special extension to ANSI C, Borland C++ also
allows an array of indeterminate size as the final member of a
structure. Such an array does not increase the size of the structure,
except that padding can be added to ensure that the array is
properly aligned. These structures are normally used in dynamic
allocation, and the size of the actual array needed must be
explicitly added to the size of the structure in order to properly
reserve space.
Except when it is the operand of a sizeof or & operator, an array
type expression is converted to a pointer to the first element of the
array.

Functions
Functions are central to C and C++ programming. Languages
such as Pascal distinguish between procedure and function.
Borland C++ functions play both roles.

Declarations and
definitions Each program must have a single external function named

main

marking the entry point of the program. Functions are usually declared as prototypes in standard or user-supplied header files, or
within program files. Functions are external by default and are
normally accessible from any file in the program. They can be re- ,
stricted by using the static storage class specifier (see page 31).
Functions are defined in your source files or made available by
linking precompiled libraries.
/n c++ you must a/ways use
function prototypes. We
recommend that you a/so
use them in C.

A given function can be declared several times in a program, provided the declarations are compatible. Nondefining function
declarations using the function prototype format provide Borland
C++ with detailed parameter information, allowing better control
over argument number and type checking, and type conversions.
Excluding C++ function overloading, only one definition of any
given function is allowed. The declarations, if any, must also
match this definition. (The essential difference between a

60

Borland C++ Programmer's Guide

definition and a declaration is that the definition has a function
body.)

Declarations and
prototypes

In the original Kernighan and Ritchie style of declaration, a
function could be implicitly declared by its appearance in a
function call, or explicitly declared as follows:

In c++, this declaration
means  func(void)

 funcO

where type is the optional return type defaulting to int. A function
can be declared to return any type except an array or function
type. This approach does not allow the compiler to check that the
type or number of arguments used in a function call match the
declaration.
You can enable a warning
within the IDE or with the
command-line compiler:
.. Function called without a
prototype. "

This problem was eased by the introduction of function
prototypes with the following declaration syntax:
 func(parameter-declarator-list);

Declarators specify the type of each function parameter. The compiler uses this information to check function calls for validity. The
compiler is also able to coerce arguments to the proper type.
Suppose you have the following code fragment:
extern long lmax(long vI, long v2); /* prototype */
faa ()
{

int limit = 32;
char ch = -' A' ;
long mval;
mval

=

lmax(limit,ch);

/* function call */

Since it has the function prototype for Imax, this program
converts limit and ch to long, using the standard rules of
assignment, before it places them on the stack for the call to Imax.
Without the function prototype, limit and ch would have been
placed on the stack as an integer and a character, respectively; in
that case, the stack passed to Imax would not match in size or
content what Imax was expecting, leading to problems. The classic
declaration style does not allow any checking of parameter type
or number, so using function prototypes aids greatly in tracking
down programming errors.

Chapter 2, Language structure

61

Function prototypes also aid in documenting code. For example,
the function strcpy takes two parameters: a source string and a
destination string. The question is, which is which? The function
prototype
char *strcpy(char *dest, const char *source};

makes it clear. If a header file contains function prototypes, then
you can print that file to get most of the information you need for
writing programs that call those functions. If you include an
identifier in a prototype parameter, it is only used for any later
error messages involving that parameter; it has no other effect.
A function declarator with parentheses containing the single
word void indicates a function that takes no arguments at all:
func(void);
~

stdarg.h contains macros
that you can use in userdefined functions with
variable numbers of
parameters.

In C++, funcO also declares a function taking no arguments.
A function prototype normally declares a function as accepting a
fixed number of parameters. For functions that accept a variable
number of parameters (such as printf), a function prototype can
end with an ellipsis (. .. ), like this:
f(int *count, long total, ... }

With this form of prototype, the fixed parameters are checked at
compile time, and the variable parameters are passed with no
type checking.
Here are some more examples of function declarators and
prototypes:
int

f () ;

/* In C, a function returning an int with no
information about parameters. This is the K&R
"classic style." */

int f () ;

/* In

int

f (void) ;

/* A function returning an int that takes no
parameters. */

int

p(int,long};

/* A function returning an int that accepts two
parameters: the first, an inti the second, a
long. */

int

pascal q(void}; /* A pascal function returning an int that takes
no parameters at all. */

Ctt,

a function taking no arguments */

char far *s(char *source, int kind}; /* A function returning a far
pointer to a char and accepting two parameters: the
first, a pointer to a char; the second, an into */

62

Borland C++ Programmer's Guide

int printf(char *format, ... ); /* A function returning an int and
accepting a pointer to a char fixed parameter and
any number of additional parameters of unknown
type. */
int

(*fp) (int);

/* A pointer to a function returning an int and
accepting a single int parameter. */

Definitions
The general syntax for external function definitions is given in the
following table:
Table 2,9
External function definitions

file
external-definition
file external-definition
external-definition:
function-definition
declaration
asm-statement
function-definition:
 declarator 
compound-statement

In general, a function definition consists of the following sections
(the grammar allows for more complicated cases):
You can mix elements from 1
and 2,

1. Optional storage class specifiers: extern or static. The default
is extern.
2. A return type, possibly void. The default is int.
3. Optional modifiers: pascal, cdecl, interrupt, near, far, huge,
_export, _Ioadds, _saveregs. The defaults depend on the
memory model and compiler option settings.

4. The name of the function.
5. A parameter declaration list, possibly empty, enclosed in parentheses. In C, the preferred way of showing an empty list is
func(void). The old style of funcO is legal in C but antiquated
and possibly unsafe.
6. A function body representing the code to be executed when
the function is called.

Chapter 2, Language structure

63

Formal parameter
declarations

The formal parameter declaration list follows a similar syntax to
that of the declarators found in normal identifier declarations.
Here are a few examples:
int func(void) (

II no args

int func(Tl tl, T2 t2, T3 t3=1) ( II three simple parameters, one
II with default argument
int func(Tl* ptrl, T2& tref)
int func(register int i) (

II a pointer and a reference arg
II request register for arg

int func(char *str, ... ) (
1* one string arg with a variable
number of other args, or with a fixed number of args with
varying types *1
~

In C++, you can give default arguments as shown. Parameters
with default values must be the last arguments in the parameter
list. The arguments' types can be scalars, structures, unions, enumerations; pointers or references to structures and unions; or
pointers to functions or classes.
The ellipsis (. .. ) indicates that the function will be called with different sets of arguments on different occasions. The ellipsis can
follow a sublist of known argument declarations. This form of
prototype reduces the amount of checking the compiler can make.
The parameters declared all enjoy automatic scope and duration
for the duration of the function. The only legal storage class
specifier is register.
The canst and volatile modifiers can be used with formal
argument declarators.

Function calls
and argument
conversions

A function is called with actual arguments placed in the same sequence as their matching formal arguments. The actual arguments are converted as if by initialization to the declared types of
the formal arguments.
Here is a summary of the rules governing how Borland C++ deals
with language modifiers and formal parameters in function calls,
both with and without prototypes:

64

Borland C++ Programmer's Guide

1. The language modifiers for a function definition must match
the modifiers used in the declaration of the function at all calls
to the function.
2. A function may modify the values of its formal parameters,
but this has no effect on the actual arguments in the calling
routine, except for reference arguments in C++.
When a function prototype has not been previously declared,
Borland C++ converts integral arguments to a function call
according to the integral widening (expansion) rules described in
the section "Standard conversions," starting on page 41. When a
function prototype is in scope, Borland C++ converts the given
argument to the type of the declared parameter as if by
assignment.
When a function prototype includes an ellipsis ( ... ), Borland C++
converts all given function arguments as in any other prototype
(up to the ellipsis). The compiler widens any arguments given
beyond the fixed parameters, according to the normal rules for
function arguments without prototypes.
If a prototype is present, the number of arguments must match
(unless an ellipsis is present in the prototype). The types need
only be compatible to the extent that an assignment can legally
convert them. You can always use an explicit cast to convert an
argument to a type that is acceptable to a function prototype.
Important!

If your function prototype does not match the actual function definition, Borland C++ will detect this if and only if tha t definition is
in the same compilation unit as the prototype. If you create a
library of routines with a corresponding header file of prototypes,
consider including that header file when you compile the library,
so that any discrepancies between the prototypes and the actual
definitions will be caught. C++ provides type-safe linkage, so
differences between expected and actual parameters will be
caught by the linker.

Structures
Structure initialization is
discussed on page 42.

A structure is a derived type usually representing a user-defined
collection of named members (or components). The members can
be of any type, either fundamental or derived (with some restrictions to be noted later), in any sequence. In addition, a structure

Chapter 2, Language structure

65

member can be a bit field type not allowed elsewhere. The
Borland C++ structure type lets you handle complex data
structures almost as easily as single variables.
~

.

In C++, a structure type is treated as a class type (with certain differences: Default access is public, and the default for the base class
is also public). This allows more sophisticated control over access
to structure members by using the C++ access specifiers: public
(the default), private, and protected. Apart from these optional
access mechanisms, and from exceptions as noted, the following
discussion on structure syntax and usage applies equally to C and
C++ structures.
Structures are declared using the keyword struct. For example,
struct mystruct { .. , }i II mystruct is the structure tag
struct mystruct s, *ps, arrs[lOli
1* s is type struct mystructi ps is type pointer to struct mystruct;
arrs is array of struct mystruct. *1

Untagged
structures and If you omit the structure tag, you can get an untagged structure.
typedefs You can use untagged structures to declare the identifiers in the

comma-delimited struct-id-list to be of the given structure type (or
derived from it), but you cannot declare additional objects of this
type elsewhere:

Untagged structure and
union members are ignored
during initialization.

struct { ... } s, *ps, arrs[lOli II untagged structure

It is possible to create a typedef while declaring a structure, with
or without a tag:
typedef struct mystruct { ... } MYSTRUCTi
MYSTRUCT s, *ps, arrs[lOli
II same as struct mystruct s, etc.
typedef struct { ... } YRSTRUCTi II no tag
YRSTRUCT y, *yp, arry[201i

You don't usually need both a tag and a typedef: Either can be
used in structure declarations.

Structure member
declarations The member-decl-list within the braces declares the types and
names of the structure members using the declarator syntax
shown in Table 2.2 on page 36.

66

Borland C++ Programmer's Guide

A structure member can be of any type, with two exceptions:
1. The member type cannot be the same as the struct type being
currently declared:
You can omit the struct
keyword in C++.

struct mystruct { mystruct s } sl, s2; II illegal

A member can be a pointer to the structure being declared, as
in the following example:
struct mystruct { mystruct *ps } sl, s2; II OK

Also, a structure can contain previously defined structure
types when declaring an instance of a declared structure.
2. Except in C++, a member cannot have the type "function
returning ... ," but the type "pointer to function returning ... " is
allowed. In C++, a struct can have member functions.

Structures and
functions

A function can return a structure type or a pointer to a structure
type:
mystruct func1(void); II func1() returns a structure
mystruct *func2(void); II func2() returns pointer to structure

A structure can be passed as an argument to a function in the
following ways:
void func1(mystruct s);
void func2(mystruct *sptr);
void func3(mystruct &sref);

Structure member
access

II directly
II via a pointer
II as a reference (ett only)

Structure and union members are accessed using the selection
operators. and ->. Suppose that the object s is of struct type 5,
and sptr is a pointer to S. Then if m is a member identifier of type
M declared in 5, the expressions s.m and sptr->m are of type M,
and both represent the member object min s. The expression
sptr->m is a convenient synonym for (* sptr) . m.
The operator. is called the direct member selector; the operator ->
is called the indirect (or pointer) member selector; for example,
struct mystruct
{

int i;
char str[21];
double d;

Chapter 2, Language structure

67

} s, *sptr=&s;
s.i = 3;
sptr->d = 1.23;

II assign to the i member of mystruct s
II assign to the d member of mystruct s

The expression s.m is an lvalue, provided that s is not an lvalue
and m is not an array type. The expression sptr->m is an lvalue
unless m is an array type.
If structure B contains a field whose type is structure A, the
members of A can be accessed by two applications of the member
selectors:
struct A
int j;
double x;
};

struct B {
int i;
struct A ai
double d;
s, *sptr;
s.i = 3;
s.a.j = 2;
sptr->d = 1.23;
(sptr->a).x = 3.14

II
II
II
II

assign
assign
assign
assign

to
to
to
to

the i member of B
the j member of A
the d member of B
x member of A

Each structure declaration introduces a unique structure type, so
that in
struct A
int i,j;
double d;
a, al;
struct B {
int i,j;
double d;
b;

the objects a and al are both of type struct A, but the objects a and
b are of different structure types. Structures can be assigned only
if the source and destination have the same type:
a = al;
II OK: same type, so member by member assignment
a = b;
II ILLEGAL: different types
a.i = b.i; a.j = b.j; a.d = b.d 1* but you can assign
member-by-member *1

68

Borland C++ Programmer's Guide

Structure word
alignment

Memory is allocated to'a structure member-by-member from left
to right, from low to high memory address. In this example,
struct mystruct {
int i;
char str[21];
double d;
s;

the object s occupies sufficient memory to hold a 2-byte integer, a
21-byte string, and an 8-byte double. The format of this object in
memory is determined by the Borland C++ word alignment
option. With this option off (the default), s will be allocated 31
contiguous bytes.

If you turn on word alignment with the Options I Compiler I Code
Generation dialog box or with the -a compiler option, Borland
C++ pads the structure with bytes to ensure the structure is
aligned as follows:
1. The structure will start on a word boundary (even address).
2. Any non-char member will have an even byte offset from the
start of the structure.
3. A final byte is added (if necessary) at the end to ensure that
the whole structure contains an even number of bytes.
With word alignment on, the structure would therefore have a
byte added before the double, making a 32-byte object.

Structure name
spaces

Structure tag names share the same name space with union tags
and enumeration tags (but enums within a structure are in a
different name space in C++). This means that such tags must be
uniquely named within the same scope. However, tag names need
not differ from identifiers in the other three name spaces: the label
name space, the member name space(s), and the single name
space (which consists of variables, functions, typedef names, and
enumerators).

Chapter 2, Language structure

69

Member names within a given structure or union must be unique,
but they can share the names of members in other structures or
unions. For example,
goto s;
s:
struct s
int s;
float s;
s;

union s
int s;
float f;

f;
struct t
int s;
s;

Incomplete
declarations

II
II
II
II
II

OK: tag and label name spaces different
OK: label, tag and member name spaces different
ILLEGAL: member name duplicated
OK: var name space different. In Ctt, this can only
be done if s does not have a constructor.

II ILLEGAL: tag space duplicate
II OK: new member space
II OK: var name space
II OK: different member spaceII ILLEGAL: var name duplicate

A pointer to a structure type A can legally appear in the declaration of another structure B before A has been declared:
struct A;
II incomplete
struct B { struct A *pa }i
struct A { struct B *pb };

The first appearance of A is called incomplete because there is no
definition for it at that point. An incomplete declaration is
allowed here, since the definition of B doesn't need the size of A.

Bit fields
A structure can contain any
mixture of bit field and nonbit field types.

You can declare signed or unsigned integer members as bit fields
from 1 to 16 bits wide. You specify the bit field width and
optional identifier as follows:

type-specifier  : width;
where type-specifier is char, unsigned char, int, or unsigned into Bit
fields are allocated from low-order to high-order bits within a
word. The expression width must be present and must evaluate to .
a constant integer in the range 1 to 16.

70

Borland C++ Programmer's Guide

If the bit field identifier is omitted, the number of bits specified in
width is allocated, but the field is not accessible. This lets you
match bit patterns in, say, hardware registers where some bits are
unused. For example,
struct mystruct {
int
2;
unsigned
5;
int
4;
int
k
1i
unsigned m 4;
a, b, Ci

produces the following layout:
15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

x

x

x

x

x

x

x

x

x

x

x

x

x

x

x

x

.

~~

m

k

"

(unused)

,...

... ...
J

.I

....

~

j

i

Integer fields are stored in 2's-complement form, with the leftmost
bit being the MSB (most significant bit). With int (for example,
Signed) bit fields, the MSB is interpreted as a sign bit. A bit field
of width 2 holding binary II, therefore, would be interpreted as 3
if unsigned, but as -1 if int. In the previous example, the legal
assignment a. i = 6 would leave binary 10 =-2 in a.i with no
warning. The signed int field k of width 1 can hold only the values
-1 and 0, since the bit pattern 1 is interpreted as -l.
'-

Bit fields can be declared only in structures, unions, and classes.
They are accessed with the same member selectors ( . and -»
used for non-bit field members. Also, bit fields pose several problems when writing portable code, since the organization of bitswithin-bytes and bytes-within-words is machine dependent.
The expression &mystruct.x is illegal if x is a bit field identifier,
since there is no guarantee that mystruct.x lies at a byte address.

Unions
Unions correspond to the
variant record types of
Pascal and Modula-2.

Union types are derived types sharing many of the syntactical
and functional features of structure types. The key difference is
that a union allows only one of its members to be "active" at any
one time. The size of a union is the size of its largest member. The

Chapter 2, Language structure

71

value of only one of its members can be stored at any time. In the
following simple case,
union myunion
int i;
double d;
char Chi
} mu, *muptr=μ

1* union tag = myunion *1

the identifier mu, of type union myunion, can be used to hold a 2byte int, an 8-byte double, or a single-byte char, but only one of
these at the same time.
sizeof(union myunion) and sizeof(mu) both return 8, but 6 bytes
are unused (padded) when mu holds an int object, and 7 bytes are
unused when mu holds a char. You access union members with

the structure member selectors (. and -», but care is needed:
mu.d = 4.016;
printf(" mu .d = %f\n",mu.d};
printf("mu .i = %d\n",mu.i};
mu.ch = I A';
printf(" mu . ch = %c\n",mu.ch};
printf("mu.d = %f\n",mu.d};
muptr->i = 3;
printf(" mu .i = %d\n",mu.i);

II OK: displays mu.d = 4.016
II peculiar result
II OK: displays mu.ch
/1 peculiar result

=A

II OK: displays mu.i = 3

The second printf is legal, since mu.i is an integer type. However,
the bit pattern in mu.i corresponds to parts of the double previously assigned, and will not usually provide a useful integer
.
interpretation.
When properly converted, a pointer to a union points to each of
its members, and vice versa.

Anonymous
unions (C++ only)

~

A union that doesn't have a tag and is not used to declare a
named object (or other type) is called an anonymous union. It has
the following form:
union { member-list };

Its members can be accessed directly in the scope where this
union is declared, without using the x.y or p->y syntax.

72

Borland c++ Programmer's Guide

Anonymous unions can't have member functions and at file level
must be declared static. In other words, an anonymous union may
not have external linkage.

Union
declarations

©$>

The general declaration syntax for unions is pretty much the same
as that for structures. Differences are
1. Unions can contain bit fields, but only one can be active. They
all start at the beginning of the union (and remember that,
because bit fields are machine dependent, they pose several
problems when writing portable code).
2. Unlike c++ structures, c++ union types cannot use the class
access specifiers: public, private, and protected. All fields of a
union are public.
3. Unions can be initialized only through their first declared
member:
union loca187
int i;
double d;
a = { 20 };

©$>

4. A union can't participate in a class hierarchy. It can't be
derived from any class, nor can it be a base class. A union can
have a constructor.

Enumerations
An enumeration data type is used to provide mnemonic
identifiers for a set of integer values. For example, the following
declara tion,
enum days { sun, mon, tues, wed, thur, fri, sat} anyday;

establishes a unique integral type, enum days, a variable anyday of
this type, and a set of enumerators (sun, man, ... ) with constant
integer values.
Borland C++ is free to store enumerators in a single byte when
Treat enums as ints is unchecked (0 I C I Code Generation) or the
-b flag. The default is on (meaning enums are always ints) if the
range of values permits, but the value is always promoted to an
int when used in expressions. The identifiers used in an

Chapter 2, Language structure

73

enumerator list are implicitly of type signed char, unsigned char,
or int, depending on the values of the enumerators. If all values
can be represented in a signed or unsigned char, that is the type
of each enumerator.
~

In C, a variable of an enumerated type can be assigned any value
of type int-no type checking beyond that is enforced. In C++, a
variable of an enumerated type can be assigned only one of its
enumerators. That is,
anyday
anyday

= moni
= 1i

II OK
II illegal, even though mon

==

1

The identifier days is the optional enumeration tag that can be
used in subsequent declarations of enumeration variables of type
en urn days:
enum days payday, holidaYi II declare two variables
~

In C++, you can omit the enurn keyword if days is not the name of
anything else in the same scope.
As with struct and union declarations, you can omit the tag if no
further variables of this enurn type are required:
enum { sun, mon, tues, wed, thur, fri, sat} anydaYi
1* anonymous enum type *1

See page 17 for more on
enumeration constants.

The enumerators listed inside the braces are also known as enumeration constants. Each is assigned a fixed integral value. In the
absence of explicit initializers, the first enumerator (sun) is set to
zero, and each succeeding enumerator is set to one more than its
predecessor (mon = 1, tues = 2, and so on).
With explicit integral initializers, you can set one or more enumerators to specific values. Any subsequent names without initializers will then increase by one. For example, in the following
declaration,
1* initializer expression can include previously declared
enumerators *1

enum coins { penny = 1, tuppence, nickel = penny +4, dime
quarter = nickel * nickel } smallchangei

=

10,

tuppence would acquire the value 2, nickel the value 5, and quarter
the value 25.
The initializer can be any expression yielding a positive or
negative integer value (after possible integer promotions). These
values are usually unique, but duplicates are legal.

74

Borland C++ Programmer's Guide

enum types can appear wherever int types are permitted.
enum days { sun, man, tues, wed, thur, fri, sat} anydaYi
enum days payday;
typedef enum days DAYS;
DAYS *daysptr;
int i = tues;
any day = man;
/I OK
*daysptr = anyday;
/I OK
II ILLEGAL: man is a constant
man = tues;

Enumeration tags share the same name space as structure and
union tags. Enumerators share the same name space as ordinary
variable identifiers:
int man = 11;
enum days { sun, man, tues, wed, thur, fri, sat} anyday;
1* enumerator man hides outer declaration of int man *1
struct days { int i, j;};
II ILLEGAL: days duplicate tag
double sat;
II ILLEGAL: redefinition of sat
man
~

= 12;

II back in int mon scope

In C++, enumerators declared within a class are in the scope of
that class.

Expressions
Table 2.11 shows how
identifiers and operators are
combined to form
grammatically legal
"phrases. "

The standard conversions are
detailed in Table 2.6 on
page 42.

An expression is a sequence of operators, operands, and ~
punctuators that specifies a computation. The formal syntax,
listed in Table 2.11, indicates that expressions are defined
recursively: Subexpressions can be nested without formal limit.
(However, the compiler will report an out-of-memory error if it
can't compile an expression that is too complex.)
Expressions are evaluated according to certain conversion,
grouping, associativity, and precedence rules which depend on
the operators used, the presence of parentheses, and the data
types of the operands. The way operands and sub expressions are
grouped does not necessarily specify the actual order in which
they are evaluated by Borland C++ (see "Evaluation order" on
page 78).

Chapter 2, Language structure

75

Expressions can produce an lvalue, an rvalue, or no value. Expressions may cause side effects whether they produce a value or
not.
We've summarized the precedence and associativity of the
operators in Table 2.10. The grammar in Table 2.11 on page 77
completely defines the precedence and associativity of the
operators.
There are sixteen precedence categories, some of which contain
only one operator. Operators in the same category have equal
precedence with each other. Where there are duplicates of
operators in the table, the first occurrence is unary, the second
binary. Each category has an associativity rule: left to right, or
right to left. In the absence of parentheses, these rules resolve the
grouping of expressions with operators of equal precedence.
Table 2.10
Associativity and
precedence of Borland C++
operators
Precedence of each
category is indicated by
order in this table. The first
category (the first line) has
the highest precedence.

Associativity

Operators

() [] -> ::
! - + - ++ - -

&*

(typecast) sizeof new delete

.* ->*
* 1 %
+ -

« »
< <= > >=
-- !=
&

"
1

&&
II
?: (conditional expression)
= *= 1= 0/0= += -= &= "= 1= «= »=

76

Left to righ t
Right to left
Left to right
Left to right
Left to right
Left to right
Left to right
Left to right
Left to right
Left to right
Left to right
Left to right
Left to right
Right to left
Right to left
Left to right

Borland C++ Programmer's Guide

Table 2.11: Borland C++ expressions
primary-expression:
literal
this (C++ specific)
:: identifier (C++ specific)
:: operator-fllnction-name (C++ specific)
::qllalified-name (C++ specific)

(expression)
name
literal:
integer-constant
c/zaracter-collstall t
floating-constant
string-literal
name:
identifier
operator-function-name (C++ specific)
conversion-function-name (C++ specific)
- class-name
qualified-name (C++ specific)
qllalified-name: (C++ specific)
qualified-class-name:: name

( type-name) cast-expression
pm-expression:
cast-expressioll
pill-expression .* cast-expressioll (C++ specific)
pm-expression ->* cast-expression (C++ specific)
mllitiplicative-expression:
pm-expression
mllltiplicative-expression * plll-expressioll
multiplicative-expression 1 pm-expression
mllitiplicative-expression % pill-expression
additive-expression:
mllitiplicative-expression
additive-expression + mllltiplicative-expressioll
additive-expression - multiplicative-expressioll
shift-expression:
additive-expression
slzift-expression « additive-expressioll
shift-expression » additive-expression
reiational-expression:
shift-expression
reiational-expression
reiational-expression
reiational-expression
relational-expression

postfix-expression:
primary-expression
postfix-expressioll [expression 1
postfix-expressioll «expressioll-list»
simple-type-name «expression-list» (C++ specific)
postfix-expression . name
postfix-expression -> Ilame
postfix-expression ++
postfix-expression --

eq lIality-expression:
reiational-expression
eqllalityexpression == relational-expression
eqllality expression != relational-expression

expressioll-list:
assignment-expression
expression-list , assignment-expressioll

AND-expressioll:
eqllality-expression
AND-expression & eqllality-expression

lInary-expression:
postfix-expression
++ lInary-expression
- - lInary-expression
lInary-operator cast-expression
sizeof unary-expression
sizeof ( type-name)
al/ocation-expression (C++ specific)
deal/ocation-expression (C++ specific)

exclusive-OR-expression:
AND-expression
exclusive-OR-expression

lIllary-operator: one of
&

•

+

al/ocation-expression: (C++ specific)
<::> new  new-type-name 
<::> new  (type-name) 
placement: (C++ specific)
( expression-list)
new-type-name: (C++ specific)
type-specifiers 
new-declarator: (C++ specific)
ptr-operator 
new-declarator [  1
deal/ocation-expression: (C++ specific)
<::> delete cast-expression
<::> delete [ 1cast-expression
cast-expression:
unary-expression

Chapter 2, Language structure

< shift-expressioll
> slzift-expression

<= shift-expression
>= shift-expression

1\

AND-expression

inclusive-OR-expressioll:
exclusive-OR -expression
illclusive-OR-expressioll I exclllsive-OR-expression
logical-AND-expression:
inclusive-OR-expression
logical-AND-expression && inclllsive-OR-expression
logical-OR-expression:
logica I-AN D-expression
logical-OR-expressioll II logical-AND-expression
conditional-expression:
logical-OR-expression
logical-OR-expression ? expressioll : conditiollal-expression
assignment -expression:
collditiollal-expression
unary-expression assignment-operator assignment-expression
assigmnent-operator: one of
1=

+=

&=
1=
«=
»=
expression:
assignmellt-expression
expression, assigllment-expression

constallt-expression:
conditional-expression

77

Expressions and
C++

c++ allows the overloading of certain standard C operators, as
explained starting 'on page 136. An overloaded operator is defined
to behave in a special way when applied to expressions of class
type. For instance, the relational operator == might be defined in
the class complex to test the equality of two complex numbers
without changing its normal usage with non-class data types. An
overloaded operator is implemented as a function; this function
determines the operand type,lvalue, and evaluation order to be
applied when the overloaded operator is used. However,
overloading cannot change the precedence ~t an operator.
Similarly, C++ allows user-defined conversions between class
objects and fundamental types. Keep in mind, then, that some of
the rules for operators and conversions discussed in this section
may not apply to expressions in C++.

Evaluation order
The order in which Borland C++ evaluates the operands of an
expression is not specified, except where an operator specifically
states otherwise. The compiler will try to rearrange the expression
in order to improve the quality of the generated code. Care is
therefore needed with expressions in which a value is modified
more than once. In general, avoid writing expressions that both
modify and use the value of the same object. Consider the
expression
i

= v[i++j;

II i is undefined

The value of i depends on whether i is incremented before or after
the assignment. Similarly,
int total = 0;
sum = (total = 3) + (++total)i II sum = 4 or sum = 7 ??

is ambiguous for sum and total. The solution is to revamp the
expression, using a temporary variable:
int temp, total = Oi
temp = tttotal;
sum = (total = 3) + tempi

Where the syntax does enforce an evaluation sequence, it is safe to
have multiple evaluations:
sum

78

= (i

=

3, itt, itt); I I OK: sum

= 4,

i

=5

Borland C++ Programmer's Guide

Each subexpression of the comma expression is evaluated from
left to right, and the whole expression evaluates to the rightmost
value.
Borland C++ regroups expressions, rearranging associative and
commutative operators regardless of parentheses, in order to
create an efficiently compiled expression; in no case will the rearrangement affect the value of the expression.
You can use parentheses to force the order of evaluation in expressions. For example, if you have the variables a, b, c, and I, then
the expression I = a + (b + c) forces (b + c) to be evaluated before
adding the result to a.

Errors and
overflows
See math err and signal in the
Library Reference.

We've summarized the precedence and associativity of the
operators in Table 2.10. During the evaluation of an expression,
Borland C++ can encounter many problematic situations, such as
division by zero or out-of-range floating-point values. Integer
overflow is ignored (C uses modulo 211 arithmetic on n-bit
registers), but errors detected by math library functions can be
handled by standard or user-defined routines.

Operator semantics
The Borland C++ operators
described here are the
standard ANSI C operators.

Unless the operators are overloaded, the following information is
true in both C and C++. In C++ you can overload all of these operators with the exception of . (member operator) and ?: (conditional operator) (and you also can't overload the C++ operators ::
and .*).
.
If an operator is overloaded, the discussion may not be true for it
anymore. Table 2.11 on page 77 gives the syntax for all operators
and operator expressions.

Operator descriptions
Operators are tokens that trigger some computation when applied
to variables and other objects in an expression. Borland C++ is
especially rich in operators, offering not only the common
arithmetical and logical operators, but also many for bit-level

Chapter 2, Language structure

79

manipulations, structure and union component access, and
pointer operations (referencing and dereferencing).
~

Overlo?ding is covered
startIng on page 735.

C++ extensions offer additional operators for accessing class
members and their objects, together with a mechanism for overloading operators. Overloading lets you redefine the action of any
standard operators when applied to the objects of a given class. In
this section, we confine our discussion to the standard operators
of Borland c++.
After defining the standard operators, we discuss data types and
declarations, and explain how these affect the actions of each
operator. From therewe can proceed with the syntax for building
expressions from operators, punctuators, and objects.
The operators in Borland c++ are defined as follows:

operator: one of

The operators # and ## are
used only by the preprocessor (see page 757).

->

++

%

«

>=

--

»
!=
=
«=

<
A
*=
»=

#

##

[]

()

&

*

+

sizeof

1

>
1

<=
&&
%=
A=

1=

&=

"

+=
1=

?:
-=

And the following operators specific to C++:
*

->*

Except for [], (), and ?:, which bracket expressions, the multi character operators are considered as single tokens. The same operator
token can have more than one interpretation, depending on the
context. For example,
A* B
*ptr

Multiplication
Dereference (indirection)

A &B

Bitwise AND
Address operation
Reference modifier (C++)

&A

int &

Statement label
Conditional statement

label:
a

? x : y

void func(int
a

=

(b+c) *di

a, b,

80

Ci

n)i

Function declaration
Parenthesized expression
Comma expression

Borland c++ Programmer's Guide

fune(a, b,

Function call

e)i

a = -bi
-fune() {delete

ai}

Bitwise negation (one's complement)
Destructor (C++)

Unary operators
&
*
+

++

Address opera tor
Indirection operator
Unary plus
Unary minus
Bitwise complement (1's complement)
Logical negation
Prefix: preincrement; Postfix: postincrement
Prefix: predecrement; Postfix: postdecrement

Binary operators
Additive operators

+

Binary plus (addition)
Binary minus (subtraction)

Multiplicative operators

*
1
%

Multiply
Divide
Remainder

Shift operators

«

Shift left
Shift right

»

Bitwise operators

&
A

Logical operators

&&
1\

Assignment operators

=
*=
1=
0/0=

+=

-=
Chapter 2, Language structure

Bitwise AND
Bitwise XOR (exclusive OR)
Bitwise inclusive OR
Logical AND
Logical OR
Assignment
Assign product
Assign quotient
Assign remainder (modulus)
Assign sum
Assign difference

81

«=
»=
1=

Assign left shift
Assign right shift
Assign bitwise AND
Assign bitwise XOR
Assign bitwise OR

<
>
<=
>=

Less than
Grea ter than
Less than or equal to
Greater than or equal to

!=

Equal to
Not equal to

->

Direct component selector
Indirect component selector

&=

Relational operators

Equality operators

Component selection
operators
Class-member
operators

Conditional operator

->*

Scope access/ resolution
Dereference pointer to class member
Dereference pointer to class member

a? x: y

"if a then x; else y"

*

Comma operator

Evaluate; e.g., a, b, c; from left to right
The operator functions, as well as their syntax, precedences, and
associativities, are covered starting on page 75.

Postfix and prefix
operators

Array subscript
operator []

The six postfix operators [] () . -> ++ and - - are used to build
postfix expressions as shown in the expressions syntax table
(Table 2.11). The increment and decrement operators (++ and --)
are also prefix and unary operators; they are discussed starting oil
page 84.
In the expression

postfix-expression [expression]
either postfix-expression or expression must be a pOinter and the
other an integral type.

82

Borland C++ Programmer's Guide

In C, but not necessarily in C++, the expression exp1[exp2J is
defined as
*

((expl) + (exp2))

where either expl is a pointer and exp2 is an integer, or expl is an
integer and exp2 is a pointer. (The punctuators [ ], *, and + can be
individually overloaded in C++.)

Function call
operators ( )

The expression

postfix-expression (

In the expression

postfix-expression. identifier
the postfix expression must be of type structure or union; the
identifier must be the name of a member of that structure or
union type. The expression designates a member of a structure or
union object. The value of the expression is the value of the
selected member; it will be an lvalue if and only if the postfix
expression is an lvalue. Detailed examples of the use of . and ->
for structures are given on page 67.
In the expression

postfix-expression -> identifier
the postfix expression must be of type pointer to structure or
pointer to union; the identifier must be the name of a member of
that structure or union type. The expression designates a member
of a structure or union object. The value of the expression is the
value of the selected member; it will be an lvalue if and only if the
postfix expression is an lvalue.

Chapter 2, Language structure

83

Postfix increment
operator ++

In the expression

postfix-expression ++
the postfix expression is the operand; it must be of scalar type
(arithmetic or pointer types) and must be a modifiable lvalue (see
page 26 for more on modifiable lvalues). The postfix ++ is also
known as the postincrement operator. The value of the whole
expression is the value of the postfix expression before the
increment is applied. After the postfix expression is evaluated, the
operand is incremented by 1. The increment value is appropriate
to the type of the operand. Pointer types are subject to the rules
for pointer arithmetic.

Postfix decrement
operator --

Increment and
decrement
operators

Prefix increment
operator

The postfix decrement, also known as the postdecrement, operator
follows the same rules as the postfix increment, except that 1 is
subtracted from the operand after the evaluation.

The first two unary operators are ++ and - -. These are also
postfix and prefix operators, so they are discussed here. The
remaining six unary operators are covered following this
discussion.
In the expression
++ unary-expression

the unary expression is the operand; it must be of scalar type and
must be a modifiable lvalue. The prefix increment operator is also
known as the preincrement operator. The operand is incremented
by 1 before the expression is evaluated; the value of the whole
expression is the incremented value of the operand. The 1 used to
increment is the appropriate value for the type of the operand.
Pointer types follow the rules of pointer arithmetic.

Prefix decrement
operator

The prefix decrement, also known as the predecrement, operator
has the following syntax:

- - unary-expression

84

Borland C++ Programmer's Guide

It follows the same rules as the prefix increment operator, except
that the operand is decremented by 1 before the whole expression
is evaluated.

Unary operators
The six unary operators (aside from ++ and - -) are & * + - and !. The syntax is

unary-operator cast-expression
cast-expression:
unary-expression
(type-name) cast-expression
Address operator &
The symbol & is also used in
c++ to specify reference
types: see page 705.

The & operator and * operator (the * operator is described in the
next section) work together as the referencing and dereferencing
operators. In the expression

& cast-expression
the cast-expression operand must be either a function designator or
an lvalue designating an object that is not a bit field and is not
declared with the register storage class specifier. If the operand is
of type type, the result is of type pointer to type.
Some non-lvalue identifiers, such as function names and array
names, are automatically converted into "pointer to X" types
when appearing in certain contexts. The & operator can be used
with such objects, but its use is redundant and therefore discouraged.
Consider the following extract:
type t1 = 1, t2 = 2;
type *ptr = &t1;
II initialized pointer
II same effect as tl = t2
*ptr = t2i

type *ptr = &t1 is treated as
T *ptr;
ptr = &tii

so it is ptr, not *ptr, that gets assigned. Once ptr has been
initialized with the address &t1, it can be safely dereferenced to
give the lvalue *ptr.

Chapter 2, Language structure

85

Indirection operator *

In the expression

* cast-expression
the cast-expression operand must have type "pointer to type,"
where type is any type. The result of the indirection is of type
type. If the operand is of type "pointer to function," the result is a
function designator; if the operand is a pointer to an object, the
result is an lvalue designating that object. In the following
situations, the result of indirection is undefined:
1. The cast-expression is a null pointer.
2. The cast-expression is the address of an automatic variable and
execution of its block has terminated.

Unary plus operator +

In the expression

+ cast-expression
the cast-expression operand must be of arithmetic type. The result
is the value of the operand after any required integral promotions.

Unary minus operator -

In the expression

- cast-expression
the cast-expression operand must be of arithmetic type. The result
is the negative of the value of the operand after any required
integral promotions.

Bitwise complement
operator -

In the expression

- cast-expression
the cast-expression operand must be of integral type. The result is
the bitwise complement of the operand after any required integral
promotions. Each 0 bit in the operand is set to 1, and each 1 bit in
the operand is set to O.

Logical negation
operator!

In the expression
! cast-expression

the cast-expression operand must be of scalar type. The result is of
type int and is the logical negation of the operand: 0 if the op-

86

Borland C++ Programmer's Guide

erand is nonzero; 1 if the operand is zero. The expression fE is
equivalent to (0 == E).

The sizeof
operator

There are two distinct uses of the sizeof operator:
sizeof unary-expression
sizeof (type-name)

How much space is set aside
for each type depends on
the machine.

The result in both cases is an integer constant that gives the size in
bytes of how much memory space is used by the operand
(determined by its type, with some exceptions). In the first use,
the type of the operand expression is determined without
evaluating the expression (and therefore without side effects).
When the operand is of type char (signed or unsigned), sizeof
gives the result 1. When the operand is a non-parameter of array
type, the result is the total number of bytes in the array (in other
words, an array name is not converted to a pointer type). The
number of elements in an array equals sizeof array I sizeof

array[O].
If the operand is a parameter declared as array type or function
type, sizeof gives the size of the pointer. When applied to
structures and unions, sizeof gives the total number of bytes,
including any padding.
sizeof cannot be used with expressions of function type,
incomplete types, parenthesized names of such types, or with an
lvalue that designates a bit field object.

The integer type of the result of sizeof is size_t, defined as
unsigned int in stddef.h.
You can use sizeof in preprocessor directives; this is specific to
Borland C++.
~

Multiplicative
operators

In C++, sizeof(classtype), where class type is derived from some
base class, returns the size of the .object (remember, this includes
the size of the base class size).

There are three multiplicative operators:

*

I and %. The syntax is

multiplica tive-expression:
cast-expression
multiplicative-expression * cast-expression

Chapter 2, Language structure

87

multiplicative-expression! cast-expression
multiplicative-expression % cast-expression
The operands for * (multiplication) and! (division) must be of
arithmetical type. The operands for % (modulus, or remainder)
must be of integral type. The usual arithmetic conversions are
made on the operands (see page 41).
The result of (opl * op2) is the product of the two operands. The
results of (opl! op2) and (opl % op2) are the quotient and remainder, respectively, when opl is divided by op2, provided that op2 is
nonzero. Use of ! or % with a zero second operand results in an
error.
When opl and op2 are integers and the quotient is not an integer,
the results are as follows:
Rounding is a/ways toward
zero.

Additive
operators

1. If opl and op2 have the same sign, opl ! op2 is the largest
integer less than the true quotient, and opl % op2 has the sign
of opl.
2. If opl and op2 have opposite signs, opl ! op2 is the smallest
integer greater than the true quotient, and opl % op2 has the
sign of opl.

There are two additive operators: + and -. The syntax is

additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression - multiplicative-expression
The addition
operator +

The legal operand types for opl + op2 are
1. Both opl and op2 are of arithmetic type.
2. opl is of integral type, and op2 is of pointer to object type.
3. op2 is of integral type, and opl is of pointer to object type.
In case 1, the operands are subjected to the standard arithmetical
conversions, and the result is the arithmetical sum of the
operands. In cases 2 and 3, the rules of pointer arithmetic apply.
(Pointer arithmetic is covered on page 58.)

88

Borland C++ Programmer's Guide

The subtraction
operator -

The legal operand types for opl - op2 are
1. Both opl and op2 are of arithmetic type.

_

2. Both opl and op2 are pointers to compatible object types. The
unqualified type type is considered to be compatible with the
qualified types canst type, volatile type, and canst volatile
type.

3. opl is of pointer to object type, and op2 is integral type.
In case 1, the operands are subjected to the standard arithmetic
conversions, and the result is the arithmetic difference of the
operands. In cases 2 and 3, the rules of pointer arithmetic apply.

Bitwise shift
operators

There are two bitwise shift operators: «and ». The syntax is

shift-expression:
addi tive-expression
shift-expression « additive-expression
shift-expression » additive-expression
Bitwise shift operators
and »)

«<

The constants ULONG_MAX
and UINCMAX are defined in
limits.h.

In the expressions El «E2 and El » E2, the operands E1 and E2
must be of integral type. The normal integral promotions are
performed on El and E2, and the type of the result is the type of
the promoted El. If E2 is negative or is greater than or equal to
the width in bits of El, the operation is undefined.
The result of El « E2 is the value of El left-shifted by E2 bit positions, zero-filled from the right if necessary. Left shifts of an unsigned long El are equivalent to multiplying El by 2E2 , reduced
modulo ULONG_MAX + 1; left shifts of unsigned ints are
equivalent to multiplying by 2 E2 reduced modulo UINT_MAX + 1.
If El is a signed integer, the result must be interpreted with care,
since the sign bit may change.
The result of El » E2 is the value of El right-shifted by E2 bit positions. If El is of unsigned type, zero-fill occurs from the left if
necessary. If El is of signed type, the fill from the left uses the
sign bit (0 for positive, 1 for negative El). This sign-bit extension
ensures that the sign of El » E2 is the same as the sign of El.
Except for signed types, the value of El » E2 is the integral part
of the quotient El/2E2.

Chapter 2, Language structure

89

Relational
operators There are four relational operators: <

> <= and >=. The syntax

for these operators is:

relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression> shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression
The less-than
operator <

Qualified names are defined
on page 117.

In the expression El < E2, the operands must conform to one of
the following sets of conditions:
1. Both El and E2 are of arithmetic type.
2. Both El and E2 are pointers to qualified or unqualified
versions of compatible object types.
3. Both El and E2 are pointers to qualified or unqualified
versions of compatible incomplete types.
In case I, the usual arithmetic conversions are performed. The
result of El < E2 is of type int. If the value of El is less than the
value of E2, the result is 1 (true); otherwise, the result is zero
(false).
In cases 2 and 3, where El and E2 are pointers to compatible
types, the result of El < E2 depends on the relative locations
(addresses) of the two objects being pointed at. When comparing
structure members within the same structure, the "higher"
pointer indicates a later declaration. Within arrays, the "higher"
pointer indicates a larger subscript value. All pointers to members
of the same union object compare as equal.
Normally, the comparison of pointers to different structure, array,
or union objects, or the comparison of pointers outside the range
of an array object give undefined results; however, an exception is
made for the "pointer beyond the last element" situation as
discussed under "Pointer arithmetic" on page 58. If P points to an
element of an array object, and Q points to the last element, the
expression P < Q + 1 is allowed, evaluating to 1 (true), even
though Q + 1 does not point to an element of the array object.

90

Borland C++ Programmer's Guide

The greater-than
operator>

The expression E1 > E2 gives 1 (true) if the value of E1 is greater
than the value of E2; otherwise, the result is 0 (false), using the
same interpretations for arithmetic and pointer comparisons, as
defined for the less-than operator. The same operand rules and
restrictions also apply.

The less-than or equalto operator <=

Similarly, the expression E1 <= E2 gives 1 (true) if the value of E1
is less than or equal to the value of E2. Otherwise, the result is 0
(false), using the same interpretations for arithmetic and pointer
comparisons, as defined for the less-than operator. The same
operand rules and restrictions also apply.

The greater-than or
equal-to operator >=

Finally, the expression E1 >= E2 gives 1 (true) if the value of E1 is
greater than or equal to the value of E2. Otherwise, the result is 0
(false), using the same interpretations for arithmetic and pointer
comparisons, as defined for the less-than operator. The same
operand rules and restrictions also apply.

Equality operators

..

There are two equality operators: == and !=. They test for equality
and inequality between arithmetic or pointer values, following
rules very similar to those for the relational operators.
However, == and != have a lower precedence than the relational
operators < >, <=, and >=. Also, == and != can compare certain
pointer types for equality and inequality where the relational
operators would not be allowed.
The syntax is

equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression
The equal-to
operator ==

In the expression E1 == £2, the operands must conform to one of
the following sets of conditions:
1. Both E1 and £2 are of arithmetic type.
2. Both E1 and £2 are pointers to qualified or unqualified
versions of compatible types.

Chapter 2, Language structure

91

3. One of El and E2 is a pointer to an object or incomplete type,
and the other is a pointer to a qualified or unqualified version
of void.
4. One of El or E2 is a pointer and the other is a null pointer
constant.
If El and E2 have types that are valid operand types for a

relational operator, the same comparison rules just detailed for El
< E2, El <= E2, and so on, apply.
In case I, for example, the usual arithmetic conversions are performed, and the result of El == E2 is of type int. If the value of El
is equal to the value of E2, the result is 1 (true); otherwise, the
result is zero (false).
In case 2, El == E2 gives 1 (true) if El and E2 point to the same
object, or both point "one past the last element" of the same array
object, or both are null pointers.
If El and E2 are pointers to function types, El == E2 gives 1 (true)
if they are both null or if they both point to the same function.
Conversely, if El == E2 gives 1 (true), then either El and E2 point

to the same function, or they are both null.
In case 4, the pointer to an object or incomplete type is converted
to the type of the other operand (pointer to a qualified or
unqualified version of void).
The inequality operator
!=

Bitwise AND
operator &

The expression El != E2 follows the same rules as those for El ==
E2, except that the result is 1 (true) if the operands are unequal,
and 0 (false) if the operands are equal.

The syntax is

AND-expression:
equal ity-expression
AND-expression & equality-expression
In the expression El & E2, both operands must be of integral type.
The usual arithmetical conversions are performed on El and E2,
and the result is the bitwise AND of El and E2. Each bit in the
result is determined as shown in Table 2.12.

92

Borland C++ Programmer's Guide

Table 2.12
Bitwise operators truth table

Bit value
in E1
0

1
0

1

Bitwise exclusive
OR operator 1\

Bit value
in E2

E1 & E2

E1 " E2

0
0
0

0

0

1
1

1

0

1
1
1

0
0

1
1

E11 E2

The syntax is

exc lusive-OR-expression:
AND-expression
exclusive-OR-expression

1\

AND-expression

In the expression El 1\ E2, both operands must be of integral type.
The usual arithmetic conversions are performed on El and E2,
and the result is the bitwise exclusive OR of El and E2. Each bit in
the result is determined as shown in Table 2.12.

Bitwise inclusive
OR operator I

The syntax is

inclusive-OR-expression:
exclusive-OR-expression
inclusive-OR-expression I exclusive-OR-expression
In the expression El I E2, both operands must be of integral type.
The usual arithmetic conversions are performed on El and E2,
and the result is the bitwise inclusive OR of El and E2. Each bit in
the result is determined as shown in Table 2.12.

Logical AND
operator &&

The syntax is

logical-AND-expression:
inclusive-OR-expression
logical-AND-expression && inclusive-OR-expression
In the expression El && E2, both operands must be of scalar type.
The result is of type int, the result is 1 (true) if the values of El and
E2 are both nonzero; otherwise, the result is 0 (false).

Chapter 2, Language structure

93

Unlike the bitwise & operator, && guarantees left-to-right
evaluation. E1 is evaluated first; if E1 is zero, E1 && E2 gives 0
(false), and E2 is not evaluated.

Logical OR
operator I I

The syntax is

logical-DR-expression:
logical-AND-expression
logical-DR-expression Illogical-AND-expression
In the expression E1 II E2, both operands must be of scalar type.
The result is of type int, and the result is 1 (true) if either of the
values of E1 and E2 are nonzero. Otherwise, the result is 0 (false).
Unlike the bitwise I operator, II guarantees left-to-right evaluation.
E1 is evaluated first; if E1 is nonzero, E1 II E2 gives 1 (true), and
E2 is not evaluated.

Conditional
operator ?:

The syntax is

conditional-expression
logical-DR-expression
logical-DR-expression ? expression: conditional-expression
In C++, the result is an Ivalue.

In the expression E1 ? E2 : E3, the operand E1 must be of scalar
type. The operands E2 and E3 must obey one of the following sets
of rules:
1.
2.
3.
4.

Both of arithmetic type
Both of compatible structure or union types
Both of void type
Both of type pointer to qualified or unqualified versions of
compatible types
5. One operand of pointer type, the other a null pointer constant
6. One operand of type pointer to an object or incomplete type,
the other of type pointer to a qualified or unqualified version
of void

First, E1 is evaluated; if its value is nonzero (true), then E2 is evaluated and E3 is ignored. If E1 evaluates to zero (false), then E3 is

94

Borland C++ Programmer's Guide

evaluated and E2 is ignored. The result of El ? E2 : E3 will be the
value of whichever of E2 and E3 is evaluated.
In case 1, both E2 and E3 are subject to the usual arithmetic conversions, and the type of the result is the common type resulting
from these conversions.
In case 2, the type of the result is the structure or union type of E2
and E3.
In case 3, the result is of type void.
In cases 4 and 5, the type of the result is pointer to a type qualified
with all the type qualifiers of the types pointed to by both
operands.
In case 6, the type of the result is that of the nonpointer-to-void
operand.

Assignment
operators

=

There are eleven assignment operators. The operator is the
simple assignment operator; the other ten are known as
compound assignment operators.
The syntax is

ass ignmen t-expression:
condi tional-expression
unary-expression assignmen t-operator assignment-expression
assignment-operator: one of
*=
/=
«= »= &=

%=

+=

"=

1=

-=

The simple assignment In the expression El = E2, El must be a modifiable lvalue. The
operator = value of E2, after conversion to the type of El, is stored in the
object designated by El (replacing El's previous value). The value
of the assignment expression is the value of El after the
assignment. The assignment expression is not itself an lvalue.
In C++, the result is an Iva/ue.

The operands El and E2 must obey one of the following sets of
rules:
1. El is of qualified or unqualified arithmetic type and E2 is of
arithmetic type.

Chapter 2, Language structure

95

2. El has a qualified or unqualified version of a structure or
union type compatible with the type of E2.
3. El and E2 are pointers to qualified or unqualified versions of
compatible types, and the type pointed to by the left has all the
qualifiers of the type pointed to by the right.
4. One of El or E2 is a pointer to an object or incomplete type
and the other is a pointer to a qualified or unqualified version
of void. The type pointed to by the left has all the qualifiers of
the type pointed to by the right.
5. El is a pointer and E2 is a null pointer constant.
The compound
assignment operators

The compound assignments op=, where op can be anyone of the
ten operator symbols * I % + - « » & A I, are interpreted as
follows:

El op= E2
has the same effect as

El

=El op E2

except that the lvalue El is evaluated only once. For example, El
+= E2 is the same as El = El + E2.
The rules for compound assignment are therefore covered in the
previous section (on the simple assignment operator =).

Comma operator
The syntax is

expression:
assignment-expression
expression, assignment-expression
In c++, the result is an Ivalue.

In the comma expression

El,E2
the left operand El is evaluated as a void expression, then E2 is
evaluated to give the result and type of the comma expression. By
recursion, the expression

El, E2, ... , En
results in the left-to-right evaluation of each Ei, with the value
and type of En giving the result of the whole expression. To avoid

96

Bor/and C++ Programmer's Guide

ambiguity with the commas used in function argument and
initializer lists, parentheses must be used. For example,
func (1, (j = 1, j + 4), k);

calls func with three arguments, not four. The arguments are i, 5,
andk.

c++ operators
See page 108 for information
on the scope access
operator (::).

The operators specific to c++ are:: .* ->*. The syntax for the .*
and ->* operators is as follows:

pm-expression
cast-expression
pm expression .* cast-expression
pm expression ->* cast-expression
The .* operator dereferences pointers to class members. It binds
the cast-expression, which must be of type "pointer to member of
class type", to the pm-expression, which must be of class type or of a
class publicly derived from class type. The result is an object or
function of the type specified by the cast-expression.
The ->* operator dereferences pointers to pointers to class
members (no, that isn't a typo; it does indeed dereference pointers
to pointers). It binds the cast-expression, which must be of type
"pointer to member of type," to the pm-expression, which must be
of type pointer to type or of type "pointer to class publicly derived
from type." The result is an object or function of the type specified
by the cast-expression.
If the result of either of these operators is a function, you can only
use that result as the operand for the function call operator ( ). For
example,
(ptr2object->*ptr2memberfunc) (10);

calls the member function denoted by ptr2memberfunc for the
object pointed to be ptr2object.

Statements
Statements specify the flow of control as a program executes. In
the absence of specific jump and selection statements, statements
are executed sequentially in the order of appearance in the source
code. Table 2.13 on page 98 lays out the syntax for statements:

Chapter 2, Language structure

97

Blocks
A compound statement, or block, is a list (possibly empty) of statements enclosed in matching braces ({ }). Syntactically, a block can
be considered to be a single statement, but it also plays a role in
the scoping of identifiers. An identifier declared within a block
has a scope starting at the point of declaration and ending at the
closing brace. Blocks can be nested to any depth.
Table 2.13: Borland C++ statements

declaration-list declaration

statement:
labeled-statement
compound-statement
expression-statement
selection-statement
iteration-statement
jump-statement
asm-statement
declaration (C++ specific)

statement-list:
statement
statement-list statement
expression-statement:
 ;

asm-statement:
asm tokens newline
asm tokens;
asm {tokens; =

I
labeled-statement:
identifier : statement
case constant-expression : statement
default : statement
compound-statement:
{   }
declaration-list:
declaration

Labeled
statements

...

selection-statement:
if (expression) statement
if (expression) statement else statement
switch (expression) statement
iteration-statement:
while ( expression) statement
do statement while (expression);
for (for-init-statement  ; ;

A statement can be labeled in the following ways:
1. label-identifier: statement
The label identifier serves as a target for tJ.:1e unconditional
goto statement. Label identifiers have their own name space
and enjoy function scope. In C++ you can label both
declaration and non-declaration statements.
2. case constant-expression: statement
default: statement

Case and default labeled statements are used only in
conjunction with switch statements.

98

Borland C++ Programmer's Guide

Expression
statements

Any expression followed by a semicolon forms an expression

statement:
 ;
Borland C++ executes an expression statement by evaluating the
expression. All side effects from this evaluation are completed
before the next statement is executed. Most expression statements
are assignment statements or function calls.
A special case is the null statement, consisting of a single semicolon
(;). The null statement does nothing. It is nevertheless useful in
situations where the Borland C++ syntax expects a statement but
your program does not need one.

Selection
statements

if statements
The parentheses around
cond-expression are
essential.

Selection or flow-control statements select from alternative
courses of action by testing certain values. There are two types of
selection statements: the it ... else and the switch.
The basic it statement has the following pattern:
it (eond-expression) t-st 

The eond-expression must be of scalar type. The expression is evaluated. 1£ the value is zero (or null for pointer types), we say that
the eond-expression is false; otherwise, it is true.

1£ there is no e,lse clause and eond-expression is true, t-st is
executed; otherwise, t-st is ignored.
1£ the optional else f-st is present and eond-expression is true, t-st is
executed; otherwise, t-st is ignored and f-st is executed.
_

Unlike, say, Pascal, Borland C++ does not have a specific Boolean
data type. Any expression of integer or pointer type can serve a
Boolean role in conditional tests. The relational expression (a > b)
(if legal) evaluates to int 1 (true) if (a > b), and to int 0 (false) if
(a <= b). Pointer conversions are such that a pointer can always be
correctly compared to a constant expression evaluating to O. That
is, the test for null pointers can be written if (lptr) ... or
if (ptr

Chapter 2, Language structure

==

0) ....

99

The f-st and t-st statements can themselves be if statements, allowing for a series of conditional tests nested to any depth. Care is
needed with nested if ... else constructs to ensure that the correct
statements are selected. There is no endif statement: Any "else"
ambiguity is resolved by matching an else with the last
encountered if-without-an-else at the same block level. For
example,
if (x == 1)

if (y == 1) puts("x=l and y=l");
else puts("x != 1");

draws the wrong conclusion! The else matches with the second if,
despite the indentation. The correct conclusion is that x = 1 and y
!= 1. Note the effect of braces:
if

(x

== 1)

{

if (y == 1) puts("x = 1 and y = 1");
else puts("x != 1"); II correct conclusion

switch statements

The switch statement uses the following basic format:
switch (sw-expression) case-st

A switch statement lets you transfer control to one of several
case-labeled statements, depending on the value of sw-expression.
The latter must be of integral type (in C++, it can be of class type,
provided that there is an unambiguous conversion to integral
type available). Any statement in case-st (including empty
statements) can be labeled with one or more case labels:
It is illegal to have duplicate
case constants in the same
switch statement.

case const-exp-i : case-st-i

where each case constant, const-exp-i, is a constant expression with
a unique integer value (converted to the type of the controlling
expression) within its enclosing switch statement.
There can also be at most one default label:
default: default-st

After evaluating sw-expression, a match is sought with one of the
const-exp-i. If a match is found, control passes to the statement
case-st-i with the matching case label. .

If no match is found and there is a default label, control passes to
default-st. If no match is found and there is no default label, none

100

Borland C++ Programmer's Guide

of the statements in case-st is executed. Program execution is not
affected when case and default labels are encountered. Control
simply passes through the labels to the following statement or
switch. To stop execution at the end of a group of statements for a
particular case, use break.

Iteration
statements
while statements

Iteration statements let you loop a set of statements. There are
three forms of iteration in Borland C++: while, do, and for loops.
The general format for this statement is
while (cond-exp) t-st

The parentheses are
essential.

The loop statement, t-st, will be executed repeatedly until the
conditional expression, cond-exp, compares equal to zero (false).
The cond-exp is evaluated and tested first (as described on page
99). If this value is nonzero (true), t-st is executed; if no jump
statements that exit from the loop are encountered, cond-exp is
evaluated again. This cycle repeats until cond-exp is zero.
As with if statements, pointer type expressions can be compared
with the null pointer, so that while (ptr) ... is equivalent to
while (ptr != NULL) ...

The while loop offers a concise method for scanning strings and
other null-terminated data structures:
char str[lO]="Borland";
char *ptr=&str[O];
int count=O;
//

...

while (*ptr++)
count++;

II loop until end of string

,In the absence of jump statements, t-st must affect the value of
cond-exp in some way, or cond-exp itself must change during
evaluation in order to prevent unwanted endless loops.
do while statements

The general format is
do do-st while (cond-exp);
The do-st statement is executed repeatedly until cond-exp
compares equal to zero (false). The key difference from the while
statement is that cond-exp is tested after, rather than before, each

Chapter 2, Language structure

101

execution of the loop statement. At least one execution of do-st is
assured. The same restrictions apply to the type of cond-exp
(scalar).
for statements
For C++,  can be
an expression or a
declaration.

The for statement format in C is
for

«init-exp>; ;