Terry’s C++ Mini-Manual

Terry’s C++ Mini-Manual
Terry Rogers
3 June 2011
Ver 1.2 18 June 2011
Ver 1.3 28 Oct 2012 – minor updates
Ver 1.4 15 May 2014 – significant updates to C Section
Ver 1.5 27 May 2014 – Updates to C++ Section
This is a handbook for C++ and a summary description only. It does not include Visual C++ per
se. See the WROX C++ help file or Microsoft’s MSDN. The first part is just ANSI C which is
compatible with C++.
<optional> means this part may be excluded. [insert] means replace as shown in text.
Underlined sections below are links to parts of this document.
ANSI C
ANSI C is a union of K&R C and C++ but without OOPS components and having only an update of
the older standard library.
Table of Contents
ANSI C .............................................................................................................................................. 1
Compiler Directives ..................................................................................................................... 4
Statements .................................................................................................................................. 5
Assignment and Operators ......................................................................................................... 5
assignment statement ............................................................................................................ 5
unary operators ...................................................................................................................... 5
binary operators ..................................................................................................................... 6
relational operators ................................................................................................................ 6
Data Objects ................................................................................................................................ 7
declaration: ............................................................................................................................. 7
storage class ............................................................................................................................ 7
Page 1 of 33
type specifiers: primitive type specifiers are: ......................................................................... 8
type modifiers (may be applied to most primitive types) ..................................................... 8
Initialization............................................................................................................................. 8
Arrays ...................................................................................................................................... 9
struct (ures) ............................................................................................................................. 9
Union struct (ures) ................................................................................................................ 10
Enumerated .......................................................................................................................... 12
Procedure and Function Calls ................................................................................................... 13
Function Prototypes.............................................................................................................. 13
Branching and Conditional ........................................................................................................ 14
if/then/else ........................................................................................................................... 14
while --- do while .................................................................................................................. 14
for loop .................................................................................................................................. 14
case statement ...................................................................................................................... 15
Exit from function ................................................................................................................. 15
goto statement ..................................................................................................................... 15
Pointers ..................................................................................................................................... 16
In Line Assembler ...................................................................................................................... 17
Input/Output in Standard Library ............................................................................................. 18
printf/fprintf/sprintf --- formatted output ........................................................................... 18
scanf/fscanf/sscanf --- formatted input................................................................................ 20
fflush(fp);............................................................................................................................... 20
setnbf(fp); ............................................................................................................................. 20
gets/fgets --- get a string....................................................................................................... 21
fopen/freopen --- open a buffered file ................................................................................. 21
fclose --- close a buffered file ret=fclose(fp);........................................................................ 21
clrerr/clearerr --- clear error flag for file............................................................................... 21
Scope of Variables ..................................................................................................................... 22
Subtle Errors Enabled by C Language ....................................................................................... 23
Page 2 of 33
Buffer Over-run ..................................................................................................................... 23
Pointer Addressing ................................................................................................................ 23
Harvard Architecture Problems ............................................................................................ 24
C++ ................................................................................................................................................ 27
Class (Structures) .......................................................................................................................... 27
Simple Parent Class ................................................................................................................... 28
Derived Class ............................................................................................................................. 29
Constructors & Destructors ...................................................................................................... 32
Scope (Public/Restricted/Private)............................................................................................. 32
Embedded SW Problems .......................................................................................................... 32
Operator Overloading ................................................................................................................... 32
Polymorphism ............................................................................................................................... 32
Subtle Errors Enabled by C++ ........................................................................................................ 33
Constructor Chaos..................................................................................................................... 33
Virtual Functions in ROM(!) ...................................................................................................... 33
Page 3 of 33
Compiler Directives
syntax:
#[directive]
where [directive] may be:
examples
define identifier token-string
#define CARRIAGE_RET 0x0D
include “filename” or <filename>
#include <exec/types>
“ “ means local & < > means look in /I library path first. Directives make up a scripting language
that includes conditionals and branching. For example, a header file:
#ifndef
MESSAGES_H
#define
MESSAGES_H 1
… body of file
#endif
note ‘#pragma once’ does the same
for MSoft compilers
Note directives do not end in a semi-colon.
Compiler directives constitute their own language in that one can use #if, #else etc. to control
code generation for such things as model variances. HOWEVER, it becomes extremely difficult
to read through extensive use of such code control forcing you to resort to reading listings. It is
recommended that, instead one use directives or better, a versioning system to bring in and
delete whole files or at least whole functions and rely on design documentation to link
variations.
Special debug statements can be put in under control of a conditional (above) and the variable
DEBUG or MYDEBUG if the compiler already uses ‘debug.’ The IDE or build file will permit
definition of whatever configuration variables, such as DEBUG, that are required. In the current
MSoft IDE, one uses different projects in the same Solution to swap out build files and thus
predefined configuration variables as compiler directives. Ultimately, compiler directives can
be contorted into a macro language which I don’t find desireable since there are no standard
validation tools for this approach. Use features of ANSI C++, the IDE and Configuration and
Control System instead. There are many more directives.
Page 4 of 33
Statements
syntax:
statement; /* comment */
{statement; statement;...;} /* compound statement */
Statement; // comment to end of line
Individual but not compound statements end with a semi-colon. Function code is ultimately
contained in one large compound statement which may contain other compound and likely
simple statements. Statements define or declare objects, control flow, evaluate expressions,
assign values or call functions (subroutines). main() is also a function which is called by the
linker supplied start up code.
Note that classes and structures also end with a semi-colon as do enumerations.
Assignment and Operators
assignment statement
lvalue op expression /* lvalue means something that can go on the left of = .  )
lvalue = expression /* most common */
where: lvalue is an expression referring to a data object e.g. a variable or *p where p is a
pointer op is an assignment operator e.g.
+=
lvalue=lvalue + expression
-=
analogous to above
*=
/=
%=
(modulo, operands must not be float)
>>=
(shift lvalue right by expression bits)
<<=
(shift lvalue left by expression bits)
&=
(bitwise AND)
^=
(bitwise XOR)
|=
(bitwise OR)
unary operators
*expression indirection, meaning the object pointed to and not the pointer itself
&lvalue
pointer operator, result is pointer to object
-expression unary minus
!expression logical NOT
~expression one’s compliment of its operand (what size?!?!)
++lvalue
numeric ++x is same as x+=1 (but conversion may be required & pointers are
different). Increment occurs before use of the resulting value
Page 5 of 33
--lvalue
lvalue++
same as above but decrementing
increment lvalue (type always lvalue). The increment occurs after use of the
resulting value
lvalue-decrement lvalue (type always lvalue). Same as above but decrementing.
(type name) expression cast operator. example:
wp = (struct Window *)make_window(args); where wp is type struct *Window (pointer)
 not required for lvalue conversion
sizeof expression size of expression in bytes
sizeof(type name) size of [type name] in bytes. This can get you into trouble
because you might be talking about “int myarray[20]” and think the sizeof is 20
but in reality, it is 20xsizeof integer in bytes.
binary operators
expression op expression where op is one of the following:
*
multiplication
/
division, result varies if neg integers
%
modulo div, must not be float
+
addition
subtraction
<<
shift left exp left by right exp bits. Example: byte (1<<2) is 00000100
>>
shift left exp right by right exp bits. Example: byte (0xf0>>2) is 00111100
note: pointers may be added or subtracted. See Ritchie for conversion rules.
relational operators
expression op expression
where op is one of the following: (value is int 1 if TRUE)
<
less than
>
more than
<=
less than or equal to
>=
more than or equal to
==
equality
&&
logical AND as in A and B are both true
||
logical OR as in either A or B are true
!=
NOT equal
Be cautious of domain problems created with the above. For example, testing a pointer range
using if(pointer<limit) when we mean <= and not just <. C and C++ will allow the pointer to
move beyond the range of the buffer or array it is indexing and write on variables which just
happen to lie nearby causing debugging crosstalk between variables and maybe even functions.
Page 6 of 33
Data Objects
see procedure & function calls for scope rules
declaration:
All variables must be declared but declaration does not necessarily reserve storage depending
upon scope rules and storage class specification. Mostly definition sets type and declaration
creates storage.
syntax: <storage class> [modifier ] [type specifier] [variable list]
storage class
static
auto
register
extern
volatile
not allocated on the stack, fixed, persistent storage. This is the
default for variables declared outside a function whose scope is
then also global.
allocated on the stack. Does not persist between calls. This is the
default for variables declared inside a function including main()
try to keep the variable in a CPU register. May not work
link to external compiled module, can be return value on stack or
static
actually information to the compiler that the variable may change
between accesses because it can be changed by another process.
Ergo, the compiler stores and recovers the variable to register
often. No firm definition for exactly when this happens.
Register variables do not remain in place between procedures.
Notes on storage class and types
A storage class list permits the compiler to find a variable so in:
MOVE.L Parm1,D1
MOVE.L 4(SP),D1
static storage
auto storage
the compiler will know the address of Parm1 or even if it should use the first or second
instruction. A variable type identifies which routines a compiler should use in:
a = b * c;
The variables may be short, long, float or double and thus require different multiplication
routines and over flow checks.
Page 7 of 33
The exact consequence of the volatile storage class varies between compilers and does not
protect against access by interrupts. However, declaring a variable to be volatile will prevent
some types of optimization and sequence relocation if one also uses a function like barrier( )
which is used to control compiler optimization by re-sequencing operations.
type specifiers: primitive type specifiers are:
char
ASCII, 8 bit (signed, may be used in integer operations). The only
type guaranteed to be 8 bits.
int
integer (32 bit on all 68000 Lattice C, may be 16 on IBM and small
processors or even eight bit!)
float
floating point, (32 bit, signed, +-10E-37 to +-10E38)
double
floating point, (64 bit, signed, +-10E-307 to +-10E308)
Note that integers vary between compilers and machines. For this reason, a file often called
compiler.h will typically define things like int8_t, int16_t, uint16_t and int32_t for signed 8 bit,
signed 16, unsigned 16 and signed 32 bit respectively (and more types). Use these if you want
the code to be portable or just be sure what size the variable might be. In small machines, this
level of control is necessary to preserve internal SRAM, NVM and throughput.
type modifiers (may be applied to most primitive types)
short short integer or just short, 16 bit signed
long
long integer or just long, 32 bit signed
unsigned
changes range to 0 to 255 or 65535 or 4294967295
Initialization
long count = 0L;
sets auto variable to (long) zero
int count(0); also sets auto variable to zero using functional notation
Note that an uninitialized auto variable may have garbage values unless the
compiler or library in use takes care of it. A BSS segment is also unitialized data
unless the above is used or the compiler sets it to zero (as is often done if no
initializer).
examples:
int c,i,j,counter,*cn /* pointers must have data types of target */
long c,i,j,counter /* 32 bit integer */
long float c,i,j /* same as double */
unsigned long int c,i,j /* 32 bit unsigned */
Page 8 of 33
Arrays
There may be an array of up to X dimensions of any of the types above as in:
int MyOneDimArray[30];
// One dimension is also called a vector
float MyThreeDimArray[10][11][12];
long MyInitArray[ ] = {1,2,4,5,7}; long MyNullInitArray[2][4] = {0}; // inits all to zero
Remember that indices begin from 0 so the elements of the first example are
MyOneDimArray[0] .. MyOneDimArray[29]. One can have four dimensional arrays. Microsoft
may support up to 32 dimensions. One can also declare an array of structures or pointers.
Note that strings are intrinsically a vector of type char and that the string name is actually a
pointer to the first array address as is an array of structs. The last char in a string must be \0
(null) meaning a 10 character string needs at least 11 elements. Derived Objects
Derived data types may be generated with:
typedef [type] [newname];
Examples:
typedef char
BYTE;
typedef unsigned char
typedef unsigned char
typedef unsigned char
/* signed 8 bit quantity */
UBYTE; /* unsigned 8 bit quantity */
BYTEBITS; /* byte value with bit fields */
*STRPTR; /* C string pointer */
struct (ures)
An array of dissimilar simple data types or of simple types and other structures or pointers.
Example:
typedef struct bool_control {
CONTROL
b_control; /* standard control struct */
UWORD
bit_mask;
/* mask to find affected bit(s) */
BITSWITCHPTR
resource;
/* resource containing the bit */
PFI
target_method;
/* target_method(*this)
if target of virtual */
int
link_obj;
/* link object number if virtual */
} BOOL_CONTROL, *BOOL_CONTROLPTR;
Upper case labels are themselves #defined or typedef as for example, UWORD means unsigned
word and is replaced with same by the preprocessor. PFI is interesting being a pointer to
Page 9 of 33
function returning integer. In ANSI C, functions could be defined as part of the structure more
directly.
typedef int
(*PFI) ();
The struct itself is typed so that one could have:
BOOL_CONTROL
gain_control;
which would allocate memory for a Boolean control structure called gain_control so that one
could write:
gain_control.target_method(gain_control);
However, if the pointer were defined as:
BOOL_CONTROLPTR p_gain_control;
then the call would be:
p_gain_control->target_method(gain_control);
The characters -> indicate dereference or that the name is a pointer. In C and C++, one tells the
compiler that the name is a pointer if it is not otherwise defined by the language (as in the case
of string names). However, the IDE may supply -> if you type ‘.’ If in fact the name is a pointer
to a structure. Other examples of declarations:
static struct IntuiMessage *msg,*GetMsg();
struct NewWindow MyWindow;
Union struct (ures)
An unusual structure still used in embedded software having small memory is the union.
Operating systems may use it as part of IPC1 but it’s not recommended in general since saving
memory is not typically a goal. An example is shown below.
1
Inter Process Communications
Page 10 of 33
typedef struct exec_message { //THERE ARE ONLY FOUR FIELDS HERE
exec_PID_t from,
// field 1
exec_msg_t type
// field 2: needed to describe contents of the next fields
union {
CMD_t
command;
ERROR_CODE error;
exec_info_t info_msg;
} msg_info;
// field 3
//! data field is zero unless 'type' above is data_msg.
union {
uint16_t
uint16_data;
uint16_t
*uint16_ptr;
int16_t
int16_data;
int16_t
*int16_ptr;
float
float_data;
char
*char_ptr;
} msg_data;
// field 4
} EXEC_MSG, *EXEC_MSG_PTR;
The above union actually has four fields:
 from
 type
 msg_info
 msg_data
The latter two may be of various types and sizes depending on the type of message. This is a
named union but there are also anonymous unions where the union is not itself identified (e.g.
msg_info). One sets a union element as in:
EXEC_MSG
cmd_msg;
cmd_msg.from = PROCESS1;
// non-union elements in the usual way
cmd_msg.type = COMMAND;
cmd_msg.msg_info.command = TURN_OFF; // setting a union member
The named union is handled as if it is a substructure with the particular type being a sub-subelement. One can initialize a union thus:
Page 11 of 33
EXEC_MSG cmd_msg = {
PROCESS1, // from us
COMMAND, // it's a command type
{TURN_ON}, // handle unions like sub-structs...
{0}
// no numerical data
}; // cmd_msg
Enumerated
A variable whose permissible values are constrained by it’s type definition. The keyword
typedef is not required.
typedef
enum {
FLOAT_NO_LABEL=0x00,
/* floating point but no label */
FLOAT_PERCENT=0x10,
/* a floating point percentage */
FLOAT_HZ=0x20,
/* a floating point frequency in Hz */
FLOAT_LINES=0x30, /* floating point number of
lines in format */
INTEGER_NO_LABEL=0x40, /* integer type but no label */
BOOL_LABEL=0x50, /* ON or OFF */
STRING_MSG=0x60, /* the message is a string */
STRING_ARRAY_MSG=0x70
/* the message is an array of ptrs
to strings like argv */
} DATA_LABEL,*DATA_LABELPTR;
If the ‘=’ is not supplied, then constants of DATA_LABEL type are assigned in sequence the
values 0,1,2,3… For any name, you can assign a value and the subsequent values will be one
larger. The above example is assigning the meanings of a bit mask in an unusual way.
Example 2:
typedef enum {
CONTRAST=1,
BRIGHTNESS,
RED_DRIVE,
GREEN_DRIVE,
BLUE_DRIVE,
RF_FREQUENCY,
RF_BANDSWITCH,
COLOR,
HUE_DECODER,
H_PHASE,
/* begin prop_control objects */
/* note that cont,brite,drives & bkgnds */
/* are all virtual controls */
/* tuning */
/* DAC that controls the bandswitch */
/* controls the decoder */
/* controls position on pulse bd */
In the above example, the first value = 1 instead of 0 with the rest incrementing 1. This is
convenient for case statements which do not handle value 0.
Page 12 of 33
Procedure and Function Calls
Calling Method: There is no difference between procedures and functions in C. Arguments are
passed by value (copied and pushed on the stack). An argument may be passed by reference by
passing a pointer to the argument. However, arrays, structs and strings are always passed by
reference2. See section on pointers.
Examples:
ret = [procedure_name](arg1, arg2, ..., argn);
where:
ret and [procedure_name] are the same type or
ret = (ret_type *)[procedure_name](...) is cast same.
ret is optional. In this case, make function VOID. argx is optional. Use (), empty parenthesis if
none. Functions have a declared type.
Examples:
int factorial( );
float determinant( );
VOID closewindow( );
//type VOID returns no value
Function Prototypes
It’s possible to define a function multiple times as long as all of them are the same. Only one
may contain the code. The rest are function prototypes, typically in a header file, used to
eliminate a common C language coding error…that the arguments in the call do not match the
arguments in function code. The result is often an application crash.
Example:
int prop_save(PROP_CONTROLPTR prop_this, OBJECT_DATAPTR data);
Types in capital were previously #define ed as structure pointers.
2
Although some compilers may require that you specifically include the ‘&’ to indicate a pointer or otherwise
declare it
Page 13 of 33
Branching and Conditional
Remember that (Boolean) TRUE=1 and FALSE=0. Expressions are actually compared to FALSE
(=0) and if not, then assumed TRUE. If expression is variable = 0, then FALSE condition will be
satisfied.
if/then/else
if (expression) statement else statement
A common error not caught by the compiler is to do this:
If ( sum = 10) counter++;
Actually, the expression will always be true and counter will always increment. That’s because
one intended to write:
If ( sum == 10) counter++;
One intended to write, “If sum is equal to 10, then increment counter” but that is not what the
first version says. It says to assign 10 to sum and then test if sum is equal to zero (of course it’s
not!) so then it’s TRUE and counter is incremented. The operators ‘=’ and ‘==’ are not the
same.
while --- do while
while (expression) statement
/* expression tested before executing statement */
do --- do until
do statement while (expression);
/* expression tested after executing statement */
for loop
for (<expression1>;<expression2>;<expression3>) statement
Expression1 is executed before the loop begins, expression2 is tested before statement is
executed and expression3 is executed after statement. If expression2 missing, then it is
equivalent to having while(1) below.
Page 14 of 33
Example:
for (n=0,n<20,n++) sum = sum + n;
equivalent to:
n=0;
while (n<20) {
sum=sum+n;
n++;
}
case statement
switch (expression) statement
expression must be type int. statement is typically compound with labels of the form:
{
case constant-expression1: case const2: statement1;break;
case constant-expression3: statement2;break;
default: default_statement;break;
}
The case expressions must be of type integer and no two the same value. The break statement
is required or the next statement will be executed (crude, isn’t it?). statementx may be
compound itself. Multiple labels are permitted.
break;
causes termination of the smallest enclosing branch statement.
continue;
causes control to pass to the loop-continuation portion of the smallest enclosing branch
statement. Remember that TRUE=1 and FALSE=0. Expressions are compared to FALSE (=0) and
if not, then assumed TRUE. If expression is variable = 0, then FALSE condition will be satisfied.
Exit from function
(before end of possibly compound statement) or exit from and return value of expression.
goto statement
goto identifier;
See! This ain’t PASCAL.
Page 15 of 33
Pointers
C has direct control of some machine operations to include pointers or indirection. A typical
machine operation would be:
MOVE.(A2),D1
MOVE.(A3)+,D2
move indirect address reg 2 to data register 1
move indirect address reg 2 post increment to
data register 2
In C code for this machine, one could write:
result = *data1 + data2;
// data pointed to by data1 add to data2
and perhaps add
data1++;
// increment the pointer address by the size of its type
The first C code line above might result in just three lines of assembler whilst the second would
only change one of the assembler lines to include address register post increment. This is
rather tight correspondence between C and machine code but at least it would be machine
portable. Also, one could set pointer variable to a machine address as in “data1 = 0xfffc1234;”
(more likely a #define hardware register name) and thereby be able to use C code to directly
control machine hardware. C and C++ can be used to write an operating system through the
use of pointers.
It is possible to use double indirection or have an array of pointers to other pointers. The
classic example is the command line argument list, argv from “main(argc,argv)”:
char
**argv;
// argv is an array of pointers to character (strings)
Page 16 of 33
Examples:
int
*p_timer;
// p_timer is a pointer to an integer data type
int* p_timer;
// this is the same thing as above in alternate form
p_timer = &timer_two;
// &timer_two means ‘get the address of timer_two’
*p_timer = *p_timer + increment; /* add increment to data pointed to by p_timer
and put that back in the address pointed to by p_timer */
p_timer += increment;
/* increment the pointer by
“increment X sizeof(*p_timer)” */
sum += *p_array_data++;
/* sum the contents of what p_array_data is pointing at
and then increment the data by one */
sum += *(p_array_data++); /* sum the contents of what p_array_data is pointing at
and then increment the pointer by one */
Note that the indirection operator ‘*’ has a higher priority than arithmetic operators which
causes the above idiosyncrasy. The dot operator is higher priority than * dereference. One
can use -> to be clear. See the C++ section Simple Parent Class. Note that an array, structure
or string name is automagically a pointer.
In Line Assembler
Most C compilers support in line assembly programs or the writing of assembler code directly
into a C program. This is useful in the case of an often repeated routine that must operate very
efficiently such as digital signaling operations. Alternately, it may be used for critical timing and
sequencing in the use of certain hardware. A compiler will probably optimize out any busy wait
looping instructions that might be used to make hardware perform correctly but you can be
sure of the timing of assembler programs. In line assembler has easy access to static C routine
variables in domain.
In line assembler is compiler specific and obviously processor specific. Consult the manual.
This is just one example for an Atmel AVR processor and the Atmel adapted gcc compiler. The
general format in this specific case is:
asm(code : output operand list : input operand list [: clobber list]);
In the code section, operands are referenced by a percent sign followed by a single digit. 0
refers to the first 1 to the second operand and so forth. For example:
asm("in %0, %1" : "=r" (value) : "I" (_SFR_IO_ADDR(PORTD)) );
Page 17 of 33
From the above example:
0 refers to "=r" (value) and
1 refers to "I" (_SFR_IO_ADDR(PORTD)).
The last part of the asm instruction, the clobber list, is mainly used to tell the compiler about
modifications done by the assembler code. See the compiler manual as this is all highly
compiler and machine specific.
Input/Output in Standard Library
printf/fprintf/sprintf --- formatted output
#include “stdio.h”
printf(control_string, arg1, arg2, ...); /* stdout, see below for control_string */
FILE *fopen( //should be fp = fopen( .. ). Return NULL is error
const char *filename,
// filename is a string or include explicitly
const char *mode // mode should be w .. writing to use fprintf( )
);
fprintf(fp, control_string, arg1, arg2, ...); /* file, open first */
n=sprintf(ds, control_string, arg1, arg2, ...); /* string */
int n; /* number of chars transferred */
FILE *fp; /* file pointer. args are variables to print */
char *control_string, *ds; /* ds is destination string */
example:
printf(“pi= %d”,pi);
The control string may be included explicitly. It may also include conversion specifications the
syntax of which is:
%<-><m><.><n><l>[convert] where:






left justify (else right justify)
m minimum field width
. separator required to include numerical precision
n precision, max char from string or digits to right of float l the data item is long and not
type int convert (character) is:
d use decimal in output
o use unsigned octal
Page 18 of 33






x use unsigned hex
u use unsigned decimal
c argument is single character
s argument is string
e use exponential <->m.nnnnnnE<+->xx (default n 6) f use fixed point <->mmm.nnnnn
(default n 6)
g use e or f whichever is shorter
Page 19 of 33
scanf/fscanf/sscanf --- formatted input
#include “stdio.h”
n=scanf(control_string, pointer1,pointer2,...); /* stdin */
n=fscanf(fp,control_string,pointer1,pointer2,...);/* file, open 1st */
n=sscanf(ss,control_string,pointer1,pointer2,...); /* string */
int n;
FILE *fp; /* file pointer. pointers are to input variables */
char *ss,*control_string;
example: n = scanf(“%2d %f %*d %2s”, &i, &x, name);
where %.. input conversion is as in printf,
and n is no. items matched or EOF and * means skip
so that with input:
56789 0123 45a72
is read as: 56->i, 789.0->x, skip 0123,45->name
note: a72 is left in input channel for next scanf()! name is already type pointer.
control_string may be included explicitly.
fflush(fp);
FILE *fp; /* fp file pointer, forced output buffer clear */
setnbf(fp);
FILE *fp; /* deletes I/O buffer for immediate I/O, e.g. console */
Page 20 of 33
gets/fgets --- get a string
p=get(s);
/ * stdin */
p=fgets(s,n,fp); /* file */
char *p, *s; /* p returned string pointer, s buffer for input str */
int n;
/* n number of bytes in buffer */
FILE *fp; /* fp file pointer */
Get an input string from a file, stdin for gets.
File is read until newline char or n-l char has been read
(fgets only). gets replaces newline char with NULL byte
and fgets appends NULL byte. Useful for free form input.
Caution: gets can crash the system if *p too small.
fopen/freopen --- open a buffered file
fp = fopen(name, mode); fp = freopen(name, mode, fpx);
FILE *fp,*fpx; /* fp file pointer */
/* note: Ritchie recommends FILE *fopen(),*fp,*fpx; */ char *name,*mode; /* name is file
name, mode access mode */ where:
name includes the path
mode is one of the following:
r reading, w writing, a appending note: fp==0 if error.
fclose --- close a buffered file ret=fclose(fp);
int ret; /* return code */
FILE *fp; /* pointer to file to be closed */ where: ret==-1 if error and 0 successful
clrerr/clearerr --- clear error flag for file
clrerr(fp);clearerr(fp);
FILE *fp;
/* file pointer */
note: Once error flag is set, it will remain set untill cleared.
Page 21 of 33
Scope of Variables
Example:
/* first file to be compiled */
#include <stdio.h>
/* preprocessor directives */
struct Window *MyWindow;
/* global or external variable */
struct NewWindow *MyNewWindow; /* because outside of any procedure*/
main(argc,argv)
{
int counter,limit,*parameter; /* local to main because inside of main() statement */
extern float pie; /* global declaration (not definition) of float variable pie
with key word extern
from this point on */
...;
}
#include <mydirectory/next_procedure>
/* procedure next_procedure will have globals *MyWindow
and *MyNewWindow because compiled at same time */
/* end of the first file */ -----------------------------------------------------/* second file to be compiled */
#include <stdio.h>
/* preprocessor directives */
extern struct Window *MyWindow; /* must be extern or duplicate
definition */
int foo(arg1) /* scope of arg1 is function foo() */ {
int counter; /* local variable. This is not the same variable
as in file 1 */
int sum = 0; /* initialized local variable */
for (counter=0;counter<20;counter++) {
float sum = 0; /* very local variable, good for length of block (compund statement). Not
the same as int sum in rest of procedure */
...;
}
/* end of block and float sum. Now int sum is effective */
...
}
/* end of file two */
Page 22 of 33
Subtle Errors Enabled by C Language
C Language was developed as a higher level language (actually medium level) to be used in
writing OS, Operating Systems. As such, it is able to read and write directly to hardware via it’s
ability to create any microprocessor instruction whatever.
Buffer Over-run
Circular buffers are common each of which needs two indices, input and output. Perhaps the
buffer is BUF_LEN long so then one might use array indices as in my_buffer[out_index] and
always increment the indices up. Then, to check if we reached the end of the buffer:
char my_buffer[BUF_LEN]; // array of characters used as a buffer
int in_index = 0;
// indices into the character array
int out_index = 0;
…
// operations on buffer in and output here
If (out_index++ < BUF_LEN) out_index = 0;
The above code has a sequence error in that the index is post incremented after the compare
rather than before (++out_index) which causes the index to move past the end of the character
array and out of the buffer depositing a character on whatever variable happened to be there.
Most of the embedded compilers will let this happen.
Consider that BUF_LEN is 10 in which case the last character is my_buffer[9] (the index runs
0..9 for 10 elements). When out_index is 9, we compare to BUF_LEN which is 10 and find it less
so we post increment to out_index = 10 rather than resetting to the beginning. Then the buffer
operations will use my_buffer[10] which is probably another variable in the memory causing a
mysterious failure perhaps in some completely unrelated routine! There are even more ways
to make this happen.
Pointer Addressing
The above example uses array indexing but often we do:
char* my_pointer; // now a pointer to a character or byte
char my_buffer[BUF_LEN]; // an array of characters
my_pointer = &my_buffer; // pointing to the first character of the array/buffer
Page 23 of 33
Then one can use operations like:
my_pointer++;
*my_pointer = ‘A’;
However, it is easily possible in the same way as before to increment right off the end of an
array…even easier.
Harvard Architecture Problems
ANSI C implicitly assumes that the target machine has one linear address space and that
pointers can access it all. This might not be true.
One might declare a pointer to a function thus:
typedef (*PFV)
(void); // PFV is intended to mean pointer to void function
So that we can define a structure:
typedef exec_task {
PFV cold_constructor;
PFV warm_constructor;
PFV object_run;
} EXEC_TASK;
// initialize the object
// reinitialize the object
// do work
And then some data:
int
speed = 55;
int* data_pointer[10];
data_pointer[3] = &speed;
Then you can define an instance of the function structures as an array of them:
EXEC_TASK object_table[ ] = {
{constructor1, warm_constructor1, obj_run1},
{constructor2, warm_constructor2, obj_run2}
};
Page 24 of 33
The above is an array of pointers to (void) functions and above that we have data pointers, e.g.
data_pointer[3]. BUT…there are at least two kinds of address space in a Harvard Architecture
CPU, code space and data space. Is the compiler smart enough to keep these two pointer
domains separate? You may look up pointers in a small Harvard computer and find that they
are always 16 bits but then the code address space could be something up to 500k+ bytes. 16
bits cannot point to all of 500k or even 128k of address space. It can only point to addresses
0..65535 or 64k. So must the PFVs be in the lower 64k and if so, how do you know they are in
that address range?
C and C++ Compiler designers for small Harvard Architecture Computers3 must pay particular
attention to address space and often supply, transparently4, a mechanism called trampolines so
that pointers to functions, an intrinsic part of C++, will work. Indeed, if the data space becomes
too large, they must also supply extended addressing for data as well. Trampolines are jmp or
jump instruction tables at a known part of the FLASH code space, often somewhere near the
beginning. If the program counter cannot address all of the code space, the jump table may
occupy extra space to set address bits. When the compiler implements a pointer to a function,
it loads an index register with the address of this table and then jumps to the instruction at the
register + (integer) pointer value (or 2X or 3X pointer precomputed) which is a jump or jsr (jump
subroutine) instruction to the real function address. The jump table is constructed so that we
also return to the address after the call when a rtn (return) instruction is executed. You don’t
need to know about this action for it to work.
In contrast, you must take deliberate action to save your perhaps limited (S)RAM from
exploitation by constants. What would constants be doing in SRAM? Of course, we frequently
use pointers to constants. In fact, every reference to a string is a pointer to the first character
of a character array5. If we have the situation with 16 bit pointers and the FLASH is say 256k,
then how does the following work?
printf(“Hello World!\n”);
3
These are often RISC type as well
Maybe not to you since if you knew about the 16 bit function pointers, you would be confused
5
Terminated by a NULL, zero
4
Page 25 of 33
Hello World is a string that must certainly be stored in (256k) FLASH when the program begins.
How does a 16 bit pointer access it in FLASH? It doesn’t. The compiler also makes a second
location for the string in (S)RAM and just before main( ) executes, it runs its own initialization
program which copies ALL the strings into RAM. If you have a lot of strings…the RAM is gone
before you do any work! A quick peek at memory space just before main( ) starts in debug
mode should convince you of how this works6. Here is an example from an XMega.
The regular library printf function takes pointers to strings and pointers ONLY work in RAM so
the character strings are all copied to RAM for you. BUT that means there must be a way to get
constant data from the FLASH. Atmel supplies a second printf function, printf_P( ) meaning
constant strings print from program memory.
printf_P(PROGMEM_STRING("Change freq = %ld\n"),hz);
You need also to declare the constant string in FLASH as well. There are other functions and
requirements for using data in FLASH on the Atmel AVR Harvard RISC machines and their gcc
compiler. See their manuals7. Other Harvard machines have similar problems and fixes when
using C and C++.
6
7
E.g. on Atmel AVRs like Mega and XMega
Which are hidden in the program directory on a PC and not in user data space
Page 26 of 33
C++
See above for ANSI C. Below find only the additional C++ features which include OOPS (classes
and related), operator overloading and polymorphism. This is just simple C++ and not any of
the tricks or tool kits like MSoft MFC. Small processors usually do not have expansive libraries
with perhaps the exception of Windows and OS7 mobile versions. Those typically require a
processor having Von Neumann, linear address space such as in the quad ARM series of
processors used by the Windows Surface2 series8. Both OSes have free IDE development
system downloads for mobile applications with support for mobile being part of the
professional development system. See also the WROX C++ tutorial on line.
My opinion is that the major C++ improvement over C Language is OOPS9 support with assured
initialization especially in embedded software where the OS may be too simple to provide that
service. Disadvantages include slower execution10, sometimes obscure constructor operation11
and difficulty in controlling target memory selection so that sometimes code ends up assigned
to RAM in a mobile system…not possible. This last problem has forced me in times past to use
C code and implement the OOPS paradigm manually. The same and more happened to
Microsoft with one of their earlier OS versions which partially explains the mix of methods and
data types in their API.
Class (Structures)
A class is a named structure which explicitly permits inclusion of procedures (functions) without
C language pointer patching12. The intent is to create a software object having all necessary
data and methods (functions) to operate on the included data. Classes may be instantiated at
compile time as in C (early binding) or created on the fly using the new operator13 (late binding).
Classes may exhibit inheritance. That is, they main contain or inherit other classes even if using
late binding. It may be intended that methods be replaced at run time (place keeper or virtual
functions/methods)14.
8
Harvard Architecture exists at the cache memory level where the processors sort data and code from the
common memory space into separate cache segments. However, this method is C language compatible.
9
Object Oriented Programming System which is a paradigm and not a language. It is possible to use the OOPS
method in C and even assembly, sometimes with better success than using C++
10
Offset by very high clock speeds
11
This may be most apparent using late binding or the new operator to create new instances of objects/classes
12
This example back in the C Language section
13
This is a significant problem for embedded processors having small RAM. It is not necessary to cast newly
allocated memory into a struct as in C.
14
Another problem for embedded processors
Page 27 of 33
Simple Parent Class
A named structure type with at least one of public or private and restricted data. Example:
class dog {
private:
dog* body;
char* name;
dog *tail;
public:
dog(char* dog_name);
char* dog_name( );
void create_tail(char* name_of_tail);
char* get_name_of_tail( );
};
// note semicolon
The above could contain a section called protected:
Note that a class can have itself as a member as for example to implement a linked list. A class
as a member is actually a pointer to the class. Functions may be implemented in the
declaration as in:
char* dog_name( ) {return name};
The above code will be implemented in-line if it’s shown in the declaration which is usually in
an *.h file even without an in-line declaration. If the function is implemented separately, it is a
called procedure or subroutine as in:
char* dog::dog_name( ) {
return name;
}
// no semicolon
‘::’ is the scope operator meaning that dog_name is in the scope of the class dog. Something
like ‘::dog_name’ (without dog) is ‘world scope’ or global and is perfectly lega. If we have at
compile time or early binding object:
dog
silky_terrier(“Lacy”);
Page 28 of 33
then use:
char* name_string;
name_string = silky_terrier.dog_name( );
If there is a run time created or late binding object:
dog* silky_terrier = new dog(“Lacy”);
then use:
char* name_string;
name_string = silky_terrier->dog_name( );
As explained in the sections above on structs and pointers, the arrow -> dereferences the
pointer silky_terrier. Interestingly, this also works but not used very much:
name_string = (*silky_terrier).dog_name( );
It’s clumsy because the dot operator would have preference over * dereference so the
parenthesis is required.
Derived Class
A derived class inherits its own copy of the parent class data and methods but can only access
those in the public section without re-declaring them or including the public keyword in the
definition meaning everything in the parent is public to the derived class and thus accessible
without redefining. It is very common to see the public keyword in the definition.
Page 29 of 33
Example:
class parent_class {
private:
int private1, private2;
public:
parent_class (int p1, int p2) { //constructor
private1 = p1;
private2 = p2;
}
void assign(int p1, int p2) {
private1 = p1;
private2 = p2;
}
int inc1( ) {return ++private1;}
…
};
Page 30 of 33
class derived_class1 : parent_class {
private:
int private3;
parent_class private4; // it has included one of the parent class
public:
derived_class1(int p1,int p2,int p3,int p4,int p5) : (p1, p2), //constructor
private4(p3,p4)
// inits also internal parent class
{
private3 = p5;
}
void assign(int p1, int p2) {
parent_class::assign(p1, p2); // :: is the scope operator
{
int inc1( ) { return parent_class::inc1( );}
…
};
In the above derived class constructor, after the colon, arguments p1 and p2 go to initialize
(construct) the intrinsically included parent class object, p3 and p4 go to initialize the explicitly
included parent object private4 and the remaining parameter p5 goes to the derived class
constructor as shown in the statement “private3 = p5;” . Intrinsically included parent class
constructors are always called first.
A program using the above might be:
main( ) {
derived_class1 d1(17,18,1,2,-5);
d1.inc1( );
…
}
Page 31 of 33
Constructors & Destructors
See also section Simple Parent Class for new and delete.
A member function (method) of a class by the same name is a constructor. See comments in
examples of sections Simple Parent Class and Derived Class. There may be multiple
constructors distinguished automatically by the number or type of arguments (DANGER! This is
not true if object creation is by a pre-compiled and unlinked library like DLLs or the OS.
Typically, the first parameter then describes which constructor or number of arguments are
used). The intrinsically included parent class object is initialized first (if any constructor, that is
called) and then the derived class constructor. This can all be tiered further.
Scope (Public/Restricted/Private)
See also section Scope of Variables in the section on ANSI C above. See also the scope operator
:: in sections Simple Parent Class and Derived Class above.
Embedded SW Problems
Part of the code for initializing a class and especially a contained parent in a derived class is in
the (invisible) compiler/linker libraries. If the development suite was not intended for firmware
or they forgot to set the constructor (or other necessary) code segment as being in ROM (as in
segment BCC), then it could land in RAM. This might not be apparent during development as
the RAM is powered up continuously and only appear once the application or other code is
flashed.
Operator Overloading
A method in a class which is invoked by one of the standard C/C++ operators such as +, -, *, =,++
or even ( ). More operators can be overloaded. Consider a class of integer counter register
which runs the range of 0 to 2^16 – 1 without rolling over. Then it would be convenient to use
‘++’ for increment instead of calling an increment method as in ‘c1.increment( );’ where c1 was
of the class ‘counter.’
void counter::operator ++ ( )
{
if(value << 65535) value++;
}
Polymorphism
This is a method marked by the keyword virtual.
TBD
Page 32 of 33
Subtle Errors Enabled by C++
Constructor Chaos
Virtual Functions in ROM(!)
Page 33 of 33