Keywords

You might also like

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 44

Keyword Description

and alternative to && operator

and_eq alternative to &= operator

asm insert an assembly instruction

declare a local variable,


auto
or we can let the compiler to deduce the type of the variable from the initialization.

bitand alternative to bitwise & operator

bitor alternative to | operator

bool declare a boolean variable

break break out of a loop

case a block of code in a switch statement

catch handles exceptions from throw

char declare a character variable

class declare a class

compl alternative to ~ operator

const declare immutable data or functions that do not change data

const_cast cast from const variables

continue bypass iterations of a loop

default default handler in a case statement

#define All header files should have #define guards to prevent multiple inclusion.

delete make dynamic memory available

do looping construct

double declare a double precision floating-point variable

dynamic_cast perform runtime casts

else alternate case for an if statement

enum create enumeration types

exit() ending a process


explicit only use constructors when they exactly match

export allows template definitions to be separated from their declarations

extern declares a variable or function and specifies that it has external linkage

extern "C" enables C function call from C++ by forcing C-linkage

false a constant representing the boolean false value

float declare a floating-point variable

for looping construct

friend grant non-member function access to private data

goto jump to a different part of the program

if execute code based on the result of a test

inline optimize calls to short functions

int declare an integer variable

long declare a long integer variable

mutable override a const variable

namespace partition the global namespace by defining a scope

new allocate dynamic memory for a new variable

not alternative to ! operator

not_eq alternative to != operator

operator create overloaded operator functions

or alternative to || operator

or_eq alternative to |= operator

private declare private members of a class

protected declare protected members of a class

public declare public members of a class

register request that a variable be optimized for speed

reinterpret_cast change the type of a variable

short declare a short integer variable


signed modify variable type declarations

sizeof return the size of a variable or type

static create permanent storage for a variable

static_cast perform a nonpolymorphic cast

struct define a new structure

switch execute code based on different possible values for a variable

template create generic functions

this a pointer to the current object

throw throws an exception

true a constant representing the boolean true value

try execute code that can throw an exception

typedef create a new type name from an existing type

typeid describes an object

typename declare a class or undefined type

union a structure that assigns multiple variables to the same memory location

unsigned declare an unsigned integer variable

using import complete or partial namespaces into the current scope

virtual create a function that can be overridden by a derived class

void declare functions or data with no associated data type

volatile warn the compiler about variables that can be modified unexpectedly

void declare functions or data with no associated data type

wchar_t declare a wide-character variable

while looping construct

xor alternative to ^ operator

xor_eq alternative to ^= operator


KEYWORDS
http://www.bogotobogo.com/cplusplus/cplusplus_k
eywords.php
main()
Q: What're the right forms of main()?

1. void main()
2. static int main()
3. int main(char** argv, int argc)
4. int main(int argc, char** argv)
5. int main()
6. inline int main(int argc, char* argv[])
7. int main(char* argv[], int argc)
8. int main(int argc, char* argv[])
9. int main(int argc, char* argv[], char* options[])

Ans: 5, 8, 9

main() must return an int, and must have either no parameters or (int


argc , char* argv[])as its first set of parameters. A program that declares
main() to be inline or static is ill-formed.

auto
With C++ 11, we can use auto to declare types that can be deduced from
context so that we can iterate over complex data structures conveniently.
It is no longer a storage specifier which is used to put the auto variable to
a stack. Actually, this usage as a storage specifier has never been widely
used since its inception.
So, the following code

for(vector<int>::iterator it = v.begin(); ...)


cout << *it << '\t';
cout << endl;

can be simplified by using auto:

for(auto it = v.begin(); ...)

Here is another sample for the usage of auto:

#include <iostream>
#include <list>
using namespace std;

ostream& operator<< (ostream& os, const list<int>&lst;)


{
for(auto elm: lst) {
os << " " << elm;
}
return os;
}

int main ()
{
list<int> listA;
list<int> listB;

for(int i = 1; i < 6; i++) listA.push_back(i);


for(int i = 1; i < 6; i++) listB.push_back(i*100);

cout << "listA: " << listA << endl;


cout << "listB: " << listB << endl;

auto iter = listA.begin();


advance(iter, 3);

/*
Moves all elements from listB into *this.
The elements are inserted before the element pointed by iter.
The container listB becomes empty after the operation.
*/
listA.splice(iter, listB);

cout << "listA: " << listA << endl;


cout << "listB: " << listB << endl;

/*
Moves the elements in the range [iter, listA.end()) from
listA into *this.
The elements are inserted before the element pointed to by
listB.begin().
*/
listB.splice(listB.begin(), listA, iter, listA.end());
cout << "listA: " << listA << endl;
cout << "listB: " << listB << endl;

return 0;
}

Output:

listA: 1 2 3 4 5
listB: 100 200 300 400 500
listA: 1 2 3 100 200 300 400 500 4 5
listB:
listA: 1 2 3 100 200 300 400 500
listB: 4 5
const
const qualifier allows us to ask compiler to enforce a semantic constraint:
a particular object should not be modified. It also allows us to tell other
programmers that a value should remain invariant. The general form for
creating a constant is:

const type name = value;

Note that we initialize a const in the declaration. So, the following line is
an error:

const int cint;


cint = 10; // too late

We'll get an error message something like this:

error: 'cint' : const object must be initialized if not extern


error: 'cint' : you cannot assign to a variable that is const

If we don't provide a value when we declare the constant, it ends up with


an unspecified value that we cannot modify.

const correctness
What is const?
If we answer that question with "const is constant."
We may get 0 point for that answer.
If we answer with "const is read only."
Then, we may get some points.
Const correctness refers to use of the C++ const keyword to declare a
variable or method as immutable. It is a compile-time construct that can
be used to maintain the correctness of code that shouldn't modify certain
variables. We can define variables as const, to indicate that they should
not be modified, and we can also define methods as const, to mean that
they should not modify any member variables of the class. Using const
correctness is simply good programming practice. It can also provide
documentation on the intent of our methods, and hence make them
easier to use.

pointers
For pointers, we can specify whether the pointer itself is const, the data it
points to is const, both, or neither:

char str[] = "constantness";


char *p = str; //non-const pointer to non-const data
const char *pc = str; //non-const pointer to const data
char * const cp = str; //const pointer to non-const data
const char * const cpc = str; //const pointer to const data

When const appears to the left of the *, what's pointed to is constant, and


if const appears to the right of the *, the pointer itself is constant.
If const appears on both sizes, both are constants.

Using const with pointers has subtle aspects. Let's declare a pointer to a


constant:

int year = 2012;


const int *ptr = &year;
*ptr = 2020; // not ok because ptr points to a const int
How about the following code:

const int year = 2012;


int *p = &year; // not ok

C++ doesn't allow the last line for simple reason: if we can assign the
address of year to p, then we can cheat and use p to modify the value
of year. That doesn't make sense because year is declared as const. C++
prohibits us from assigning the address of a const to a non-const pointer.

STL iterators
Since STL iterators are modeled on pointers, an iterator behaves mush
like a T* pointer. So, declaring an iterator as const is like declaring a
pointer const. If we want an iterator that points to something that can't be
altered (const T*), we want to use a const_iterator:

vector<int> v;
vector<int>::const_iterator itc = v.begin();
*itc = 2012; // error: *itc is cost
++itc; // ok, itc is not const

How about T*const iterator:

vector<int> v;
const vector<int>::iterator cit = v.begin;
*cit = 2012; // ok
++cit; // error: cit is const
arrays with const
Making a function to display the array is simple. We pass the name of the
array and the number of elements to the function. However, there are
some implications. We need to guarantee that the display doesn't change
the original array. In other words, we need to guard it from altering the
values of array. That kind of protection comes automatically with ordinary
parameters of the function because C++ passes them by value, and the
function plays with a copy. But functions that use an array play with the
original. To keep a function from accidentally modifying the contents of an
array, we can use the keyword const:

void display_array(const int arr[], int sz);

This declaration says that the pointer arr points to constant data, which


means that we can't use ar to alter the data.

const Member Functions


A class designer indicates which member functions do not modify the
class object by declaring them as const member functions. For example:

class Testing
{
public:
void foo() const {}
};

In that way, we can protect members of an object from being modified.


So, in the following example, we'll get an error. For VS, we get "Error:
expression (val) must be a modifiable lvalue."

#include <iostream>
using namespace std;

class Testing
{
public:
Testing(int n):val(n){}
int getValue() const { return val; }
void setValue(int n) const { val = n; }
private:
int val;
};

int main()
{
Testing test1(10);
return 0;
}

Because, in the member function setValue() is trying to modify a member


variable val though the function is declared as const. So, we should
remove the const from the setValue()function.

There is another case which a member function appears against the const
declaration:
#include <iostream>
using namespace std;

class Testing
{
public:
Testing(int n):val(n){}
void foo1() const { foo2(); }
void foo2() {}

private:
int val;
};

int main()
{
Testing test1(10);
return 0;
}

In this case, the member function foo2() is not doing anything. However,


compiler thinks the foo2() is not safe because it does not
have const declaration. In other words, compiler thinks that by calling
non-constant function from const function, the code may try to change
the value of the class object.

So, as a constant member function can't modify a data member of its


class, a constant member function cannot make a call to a non-
constant function.

However, there are exceptions to the rule:

1. A constant member function can alter a static data member.


2. If we qualify a data member with the mutable keyword, then even a
constant member function can modify it.

The example below shows that a const member function can be


overloaded with a non-constmember function that has the same
parameter list. In this case, the constness of the class object determines
which of the two functions is invoked:

#include <iostream>
using namespace std;

class Testing
{
public:
Testing(int n):val(n){}
int getVal() const {
cout << "getVal() const" << endl;
return val;
}
int getVal() {
cout << "getVal() non-const" << endl;
return val;
}

private:
int val;
};

int main()
{
const Testing ctest(10);
Testing test(20);
ctest.getVal();
test.getVal();
return 0;
}

Output is:

getVal() const
getVal() non-const

Other exmaples using const related to returning object, see Object


Returning.

Non-const member functions can not


be called on const objects
#include <iostream>

struct Foo
{
Foo() {}

void go()
{
std::cout << "Foo" << std::endl;
}
};

struct Bar : public Foo


{
Bar() {}

void go()
{
std::cout << "Bar" << std::endl;
}
};
int main(int argc, char** argv)
{
Bar b;

const Foo f = b;

f.go(); // 'Foo::go' : cannot convert 'this' pointer from 'const


Foo' to 'Foo &'

return 0;
}

A Note On const.

Many users bring up the idea of using C's keyword const as a means of


declaring data to be in Program Space. Doing this would be an abuse of
the intended meaning of the const keyword.
const is used to tell the compiler that the data is to be read-only. It is
used to help make it easier for the compiler to make certain
transformations, or to help the compiler check for incorrect usage of
those variables.
For example, the const keyword is commonly used in many functions as a
modifier on the parameter type. This tells the compiler that the function
will only use the parameter as read-only and will not modify the contents
of the parameter variable.
const was intended for uses such as this, not as a means to identify where
the data should be stored. If it were used as a means to define data
storage, then it loses its correct meaning (changes its semantics) in other
situations such as in the function parameter example.

#define
All header files should have #define guards to prevent multiple inclusion.
The format of the symbol name should
be <PROJECT>_<PATH>_<FILE>_H_.

To guarantee uniqueness, they should be based on the full path in a


project's source tree. For example, the file foo/src/bar/baz.h in
project foo should have the following guard:

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_

...

#endif // FOO_BAR_BAZ_H_

enum (Enumerations)
An enum is a very simple user-defined type, specifying its set of values as
symbolic constants.

#include <iostream>

enum Month {
Jan = 1, Feb, Mar, Apr, May, June,
Jul, Aug, Sep, Oct, Nov, Dec
};

int main()
{
using namespace std;

Month f = Feb;
Month j = Jul;

cout << "f = " << f << endl;


// f = 2; // error: cannot convert from 'int'
to 'Month'
int jj = j; // allowed: can get the numeric value
of a 'Month'
Month jjj = Month(7); // Convering int to 'Month'

cout << "jj = " << jj << ", jjj = " << jjj << endl;
return 0;
}

Output is:

f = 2
jj = 7, jjj = 7

Let's look at another usage example of enum:

#define NumArrays 10
class ArrayObj
{
private:
int array[NumArrays];
};
int main()
{ ArrayObj a;
return 0;
}

Here, #define does its job. However, we can


use const instead. const qualifier lets us specify the type explicitly as well
as we can use scoping rules to limit the definition to particular functions
or files. In other words, there's no way to create a class-specific constant
using a #define, because #define doesn't respect scope.

class ArrayObj
{
private:
static const int NumArrays = 5;
int array[NumArrays];
};

We can also use enum for the array size:

class ArrayObj
{
private:
enum {NumArrays = 5};
int array[NumArrays];
};

enum with operator


overloading( '<<' , '+')
The following example defines days as enum, increment the days by one
day, and then display them as strings by overloading + and <<.
#include <iostream>
using namespace std;

typedef enum days {SUN, MON, TUE, WED, THURS, FRI, SAT} days;

days operator+(days d) {
return static_cast<days> ((static_cast<int>(d) + 1) % 7);
}

ostream& operator<<(ostream& os, days d) {


switch (d)
{
case SUN:
os << "SUN";
break;
case MON:
os << "MON";
break;
case TUE:
os << "TUE";
break;
case WED:
os << "WED";
break;
case THURS:
os << "THURS";
break;
case FRI:
os << "FRI";
break;
case SAT:
os << "SAT";
break;
}

return os;
}

int main()
{
days d, a;
d = WED ;
a = +d;
cout << d << " " << a << endl;

d = SAT ;
a = +d;
cout << d << " " << a << endl;

return 0;
}

Output:

WED THURS
SAT SUN

explicit
Adding explicit is a good practice for any constructor that accepts a single
argument. It is used to prevent a specific constructor from being called
implicitly when constructing an object. For example, without
the explicit keyword, the following is valid C++ code:
Array a = 10;

This will call the Array single-argument constructor with the integer


argument of 10:

Array::Array(int size) {}

This type of implicit behavior, however, can be confusing, and in most


cases, unintended. As a further example of this kind of undesired implicit
conversion, let's consider the following function:

void checkArraySize(const Array &array;, int size);

Without declaring the single-argument constructor of Array as explicit, we


could call this function as

checkArray(10,10);

As another example, let's look at the following example which has a


constructor that takes a single argument. It actually, defines a conversion
from its argument type to its class:

class Complex
{
public:
Complex(double) // This defines double-to-complex
conversion
Complex(double, double)
};
...
Complex cmplx = 3.14 // OK: convert 3.14 to (3.14,0)
Complex cmplx = Complex(1.0, 2.5);

But this kind of conversion may cause unexpected and undesirable effects
as we see in the example below:

class Vector
{
int sz;
double *elem;
public:
Vector(int s): sz(s)
elem (new double[s]) {
for(int i=0; i< s; ++i) elem[i] = 0;
}
...
};

The Vector has a constructor that takes an int, which implies that it


defines a conversion from int to Vector:

class Vector
{
...
Vector(int);
...
};

Vector v = 10 // makes a vector of 10 double ?


v = 20; // assignes a new Vector of 20 double to v ?

This weakens the type safety of our code because now the compiler will
not enforce the type of the first argument to be an
explicit Array/Vector object in the above examples. As a result, there is
the potential for the user to forget the correct order of arguments and
pass them in the wrong order. This is why we chould always use
the explicit keyword for any single argument constructors unless we
know that we want to support implicit conversion.

So, for the 2nd example, we put explicit:

class Vector
{
...
explicit Vector(int);
...
};

Vector v = 10 // error: no int-to-Vector,double> conversion


v = 20; // error: no int-to-Vector,double> conversion
Vector v(10) // OK

inline
Though implementing a program as a set of functions is good from a
software engineering standpoint, function calls involve execution-time
overhead. So, C++ provides inline functions to help reduce function call
overhead, especially for small functions. Placing the qualifierinline before
a function's return type in the function definition tells the compiler to
generate a copy of the function's code in place to avoid a function call.

The trade-off is that multiple copies of the function code are inserted in
the program rather than there being a single copy of the function to which
control is passed each time the function is called. The compiler can ignore
the inline qualifier and typically does so for all but the smallest functions.

The complete definition of function should appear before it is used in the


program. This is required so that the compiler knows how to expand a
function call into its inlined code. For this reason, reusable inline functions
are typically placed in header files, so that their definitions can be
included in each source file that uses them.

In general, we provide declaration in our .h files and associated definition


in our .cpp files. However, it's also possible to provide a definition for a
method at the point where we declare it in a .h file:

class MyClass
{
public:
void MyMethod() { }
};

This implicitly requests the compiler to inline the MyMethod() member


function at all places where it is called. In terms of API design, this is a bad
practice since it exposes the code for how the method has been
implemented and directly inlines the code into our clients' programs.

There are exceptions to this rule to support templates and intentional use
of inline.

1. Pros
Inlining a function can generate more efficient object code, as long as the
inlined function is small. Feel free to inline accessors and mutators, and
other short, performance-critical functions.
2. Cons
Overuse of inlining can actually make programs slower. Depending on a
function's size, inlining it can cause the code size to increase or decrease.
Inlining a very small accessor function will usually decrease code size
while inlining a very large function can dramatically increase code size. On
modern processors smaller code usually runs faster due to better use of
the instruction cache.
3. Decision
A decent rule of thumb is to not inline a function if it is more than 10 lines
long. Beware of destructors, which are often longer than they appear
because of implicit member- and base-destructor calls!
Another useful rule of thumb: it's typically not cost effective to inline
functions with loops or switch statements (unless, in the common case,
the loop or switch statement is never executed).
It is important to know that functions are not always inlined even if they
are declared as such; for example, virtual and recursive functions are not
normally inlined. Usually recursive functions should not be inline. The
main reason for making a virtual function inline is to place its definition in
the class, either for convenience or to document its behavior, e.g., for
accessors and mutators.

mutable
To allow a class data member to be modified even though it is the data
member of a constobject, we can declare the data member as mutable. A
mutable member is a member that is never const, even when it is the
data member of a const object. A mutable member can always be
updated, even in a const member function.
struct account
{
char name[50];
mutable int id;
};

const account ac = {"Bush", 0, ....};


strcpy(ac.name, "Obama"} // not allowed
ac.id++; // allowed

The following example has an error because it tries to modify a variable


which in a constmember function:

#include <iostream>
#include <cstring>

class MyText
{
public:
std::size_t getLength() const;
private:
char * ptrText;
std::size_t txtLen;
};

std::size_t MyText::getLength() const


{
// error: l-value specifies const object
// cannot assign to txtLen because it is in a const member
function
txtLen = std::strlen(ptrText);
return txtLen;
}

We can solve the problem. mutable frees non-static data members from


the constconstraints:
#include <iostream>
#include <cstring>

class MyText
{
public:
std::size_t getLength() const;
private:
char * ptrText;
mutable std::size_t txtLen;
};

std::size_t MyText::getLength() const


{
txtLen = std::strlen(ptrText); // ok
return txtLen;
}

How can we make the code work by making changes only inside class C

#include <iostream>

class C
{
private:
int num;
public:
C(int a) : num(a) {}
int get_val() const;
};

//changes are not allowed in below code


int C::get_val() const
{
num++;
return num;
}

int main()
{
C obj(29);
std::cout << obj.get_val() << std::endl;
}

Ans: mutable int num;

namespaces
As programming projects grow large, the potential for name conflicts
increases. The language mechanism for organizing classes, functions,
data, and types into an identifiable and named part of a program without
defining a type is a namespace. The C++ standard provides namespace
which allows us to have greater control over the scope of names.

Note that a namespace scope does not end with a semicolon(;).


The following code uses namespace to create two namespaces, Stock,
and Market:

namespace Stock {
double penny;
void order();
int amount;
struct Option { ... };
}

namesapce Market {
double dollar;
void order();
int amount;
struct Purchase { ... };
}

The names in any one namespace don't conflict with names in another
namespace. So, the order in Stock is not confused with
the order in Market

Namespaces can be located at the global level or inside other


namespaces, but they cannot be in a block. Therefore, a name declared in
a namespace has external linkage by default.

Namespaces are open or they can be discontiguous. In other words, we


can add names to existing namespaces. For instance, we can add another
name to the existing list of names in Stock:

namespace Stock {
string getCompanyName();
}

The original Stock namespace provides a prototypes for order() function.


We can provide the code for the function later in the file or in another file
by using the Stock namespace again:
namespace Stock {
void order() {
...
}
}

How do we access names in a given namespace?


We use the scope-resolution operator (::), to qualify a name with its
namespace:

Stock::amount = 200;
Market::Purchase p;
Market::order();

Just variable name, such as penny is called unqualified name, which a


name with the namespace, as in Market::dollar is called qualified name.

As a quick summary:
If we want to reference "j" in "main()", how do we do that?

namespace
{
int j;
}

int main()
{
// ??
return 0;
}

Answer:

int i = ::j;

So, it looks like this when we have j both in global and local scope:

#include <iostream>
namespace
{
int j;
}

int main()
{
::j = 911;
int j = 800;

int n1 = ::j;
int n2 = j;

std::cout << "n1 = " << n1 << std::endl;


std::cout << "n2 = " << n2 << std::endl;

return 0;
}

Output:

n1 = 911
n2 = 800

The ::j appears to be defined at global scope and it can be handled as


such. In other words, it is declared outside any class, function, or
namespace. The global namespace is implicitly declared and exists in
every program. Each file that defines variables at global scope adds those
names to the global namespace. To refer to the member in the global
namespace, we use scope operator(::). However, the ::j is not actually
defined at global scope. More specifically, it is declared using unnamed
namespace. So, unlike the variable in the global namespace, it is local to a
particular file and never space multiple files.

register
The register keyword is a hint to the compiler that we want it to provide
fast access to the variable, perhaps by using a CPU register instead of the
stack to handle a particular variable. The CPU can access a value in one of
its registers more quickly than it can access memory in the stack. Some
compilers may ignore the hint and use register allocation algorithms to
figure out the best candidates to be placed within the available machine
registers. Because the compiler is aware of the machine architecture on
which the program is run, it is often able to make a more informed
decision when selecting the content of machine registers.

Usually, automatic objects used heavily within a function can be declared


with the keyword register. If possible, the compiler will load the object
into a machine register. If it cannot, the object remains in memory.

To declare a register variable, we preface the type with the


keyword register:

register int heavy_use;

Array indexes and pointers occurring within a loop are good candidates
for register objects.

for (register int i = 0; i < sz ; i++)


...
for (register int *ip = array; p < arraySize ; p++)
If a variable is stored in a register, it doesn't have a memory address. So,
we can't apply the address operator to a register variable. Therefore, in
the following code, it's okay to take the address of the variable xStack but
not of the register variable xRegister:

void f(int *);


int main()
{
int xStack;
int register xRegister;
f(&xStack;) // ok
f(&xRegister;) // not ok
...
}

struct
The keyword struct introduces a structure declaration, which is a list of
declarations enclosed in braces.

struct structure_name {
type1 member1;
type2 member2;
} object_name;

Specific example is like this:


struct data
{
int idata;
float fdata;
} myData;

myData.idata = 10;
myData.fdata = 3.14;

Once used like above, the data can represent for the declaration {...}, and
it can be used later in definition of instances of the structure. For example,
it can be used like this:

struct data yourData;

It defines a variable yourData which is a structure of type struct data.

We can also define array of structures with initializer:

#include <stdio.h>

struct data
{
int idata;
float fdata;
} myData[] = {
{10, 3.14},
{20, 0.314},
{30, 0.0314}
};

int main()
{
printf("%d,%f\n", myData[0].idata,myData[0].fdata);
printf("%d,%f\n", myData[1].idata,myData[1].fdata);
printf("%d,%f\n", myData[2].idata,myData[2].fdata);
printf("%d\n", sizeof data); // 8
printf("%d\n", sizeof myData); // 24
printf("%d\n", sizeof *myData); // 8

return 0;
}

The number of entries in the array myData[] will be computed if


initializers are there and the [] is left empty as in the above example.

We can also calculate the size of myData[] array using:

sizeof myData / sizeof (struct data)

or

sizeof myData / sizeof myData[0]

The 2nd one is better because it does not need to be changed if the type
changes. For more on the size of struct, see Size of struct
The struct is very useful when we build linked list:

struct list {
int data;
struct list *next;
};

The recursive declaration looks illegal because it's referring itself. But it's
not. It's not containing an instance of itself, but

struct list *next;

declares next to be a pointer to a list, not a list itself.

For more on the size of struct, visit the size of the struct.

Initialization of array struct


Suppose we have the struct as below:

typedef struct t
{
int i;
float f;
t* ptr;
} T;
How can we initialize the array of that struct?
Here is the answer:

T myStruct[10] = {{0}};

switch
The example below is a sort of quiz for switch related
to break and continue. We should be able to guess the output from the
code:

#include <iostream>
using namespace std;

int main()
{
for (int i = 0; i < 5; ++i)
{
switch (i)
{
case 0 : cout << "0";
case 1 : cout << "1"; continue;
case 2 : cout << "2"; break;
case 3 : cout << "3"; break;
default : cout << "d"; break;
}
cout << "*";
}
return 0;
}

Output should look like this:

0112*3*d*

At i = 0, prints out 0, continue to case 1, print 1, then next index of the


loop 
at i = 1, skip case 0, go to case 1 and print 1, then next index of the loop
at i = 2, skip case 0 and 1, go to 2, print 2, break, print *
at i = 3, skip case 0-2, at case 3, print 3, break print *
at i = 4, skip call cases, at default, print d, print *.

typedef
We can define a new name for an existing type. We use typedef to create
an alias:
typedef typeName aliasName;

So, we can make byte_pointer an alias for char *:

typedef char* byte_pointer;

Or we can create shorter names for types with longer names:

typedef unsigned short int ushort;

It defines ushort as another name for the type unsigned short int.

As an another example:

struct node
{
int data;
node *next;
};
typedef node node_t;

or

typedef struct node


{
int data;
node *next;
} node_t;

But typedef declaration does not create a new type. It just adds a new


name for existing type. The primary reason of using typedef is to
parameterize a code against portability issues. So, by just
changing typedefs, we can minimize the change in our source code.

There is another issue regarding the difference between C and C++ in


using struct tagged namespace:

In C, we'll get an error if we do:

struct Foo { ... };


Foo x;

while we do not get any error in C++.


So, we should use the following instead:

struct Foo { ... };


struct Foo x;

Why? Here is an explanation.

So, I usually use typedef to avoid this subtle difference:

typedef struct Foo { ... } Foo;

using
C++ provides two mechanisms to qualify names:

1. using declaration lets us to make particular identifiers available.

2. using Stock::order; // a using declaration

3. using directives makes the entire namespace accessible.


4. using namespace Stock; // make all the names in Stock
available

But we should not use the using keyword in the global scope of our public
headers because it would against the purpose of using namespaces in the
first place. If we want to reference symbols in another namespace in our
header, we should use fully qualified name, i.e., std::string.

volatile
The volatile keyword indicates that the value in a memory location can be
altered in ways unknown to the compiler or have other unknown side
effects (e.g. modification via a signal interrupt, hardware register,
or memory mapped I/O) even though nothing in the program code
modifies the contents. In other words, volatile informs the compiler that
the value of the variable can change from the outside, without any update
done by the code.

As an example, we can think of a memory-mapped register representing a


DIP-switch input:
while register is read and saved into a general-purpose register, our
program may keep reading the same value, even if hardware has
changed.

The intent of volatile keyword is to improve the optimization of


compilers. 
For more on optimization with volatile keywords, visit Volatile
Optimization.
In that optimization, compilers, can cache a value in a register if it's used
several times with the same value, under the assumption the variable
doesn't change during those uses. If we don't declare a variable
as volatile, then the compiler may make the optimization. If we do
declare a variable as volatile, we're telling the compiler not to make the
optimization of the code referring to the object.
In other words, volatile just tells the compiler to reload variables from
memory before using them and store them back to memory after they
have been modified.

Declaring a variable as volatile is more applicable to systems-level


programming rather than normal applications-level programming.

More on volatile:

1. Can a parameter be both const and volatile?


Yes.
A read-only status register is an example.
Also, embedded systems can have many kinds of input peripherals such
as free-running timers and keypad interfaces. They must be declared
"const volatile", because they both
1. change value outside by means outside our C program, and
also
2. our C program should not write values to them (it makes no
sense to write a value to a 10-key keypad).

It is volatile because it can change at any time.


It is const because our C program must not try to modify it.

2. Can a pointer be volatile?


An Interrupt Service Routine (ISR) can modify a pointer.

3. Check the following square code:

4. int square(volatile int *p)


5. { return *p * *p;}

Will the code always be working?


I found this code from the Web but not sure about the answer. Answer
from the site: It may be not. 
That's because the compiler would produce the internal code like this:

int square(volatile int *p)


{
int a = *p;
int b = *p
return a*b;
}

In other words, we may be end up multiplying different numbers because


it's volatile and could be changed unexpectedly. So, the site suggested the
following code:

int square(volatile int *p)


{
int a = *p;
return a*a;
}

volatile - compiler optimization*


The essense of embedded programming is that it requires
communications with the outside world. So, both input and output
devices needs the volatile keyword.
There are at least 3 types of optimizations that volatile turns off:
1. Read optimizations 
Without volatile, C compilers assume that once the program reads a
variable into a register, it doesn't need to re-read that variable every time
the source code mentions it, but can use the cached value in the register.
This works great with normal values in ROM and RAM, but fails miserably
with input peripherals. The outside world, and internal timers and
counters, frequently change, making the cached value stale and irrelevant.
2. Write optimizations
Without volatile, C compilers assume that it doesn't matter what order
writes occur to different variables, and that only the last write to a
particular variable really matters. This works great with normal values in
RAM, but fails miserably with typical output peripherals. Sending "turn left
90, go forward 10, turn left 90, go forward 10" out the serial port is
completely different than "optimizing" it to send "0" out the serial port.
3. Instruction reordering
Without volatile, C compilers assume that they can reorder instructions.
The compiler may decide to change the order in which variables are
assigned to make better use of registers. This may fail miserably with IO
peripherals where you, for example, write to one location to acquire a
sample, then read that sample from a different location. Reordering these
instructions would mean the old/stale/undefined sample is 'read', then
the peripheral is told to acquire a new sample (which is ignored).

*This optimization section is from WikiBooks. 

References on volatile:

1. "Empirical data suggests that incorrect optimization of volatile


objects is one of the most common defects in C optimizers".
2. Placing C variables at specific addresses to access memory-mapped
peripherals.

You might also like