1. C/C++ Language Summary


1.1 C/C++ Language Characteristics

Through the 1960s, and into the early 1970s, operating systems were written in assembler language. The C programming language was originally developed as a language to replace assembler in systems programming. It was very successful, making system code portable and easier to write and read.

C began to grow in popularity not just for systems programming, but as a general purpose programming language. Today C is one of the most used programming languages. The C programmer must realize, however, that C was designed to replace assembler language, and that in several important ways, it retains a very low level view of the machine. This language summary will discuss, among other things. some of the low level characteristics of C.

The C++ programming language was designed as a higher level version of C, providing support for object-oriented programming. Some features of C++ are discussed in this summary, and some are discussed under classes, later. C++ retains almost all of C, however, and therefore is a mixture of low level and high level features. Often, the programmer has a choice of using low level or high level approaches.

Case

C and C++ are both case sensitive. That means that B and b are two different letters. If you write
        int x;
you have declared a variable x of type int. If you write
        Int x;
you probably get a compile error, since there is no type Int.

Form

C/C++ is free-form. That is, in most contexts the end of a line is just like a space. So there is no special meaning to a line. Typically, you put one statement per line, because it looks nicer than packing several to a line, or breaking a statement into several lines. Long statements are usually placed on several lines, however.

Comments

You can write comments in either C style or C++ style (in C++ programs). A C style comment begins with /* and ends with */. A C++ style comments begins with // and ends at the end of a line. (So C++ comments are one of the few violations of free-formness.) For example,
      /* Here is a C-style comment */

      // Here is a C++ style comment.

Names and Reserved Words

You can name things in C/C++ with names that begin with a letter or underscore, and contain letters, underscores and digits in any combination. For example,
      apostrophe
      the_list
      horse43
      x
are all names that can be given to things. Names cannot contain spaces. You should avoid names that begin with an underscore, since they are often used to name special things in the system.

Certain words are special in the language, and you cannot use them to name things. These reserved words are shown in bold face in programs in this summary.


1.2 The Memory

A computer's memory stores programs and data. The usual model of a memory is as a mapping from integer addresses to values. You give the memory an address, and it gives you the value stored at that address. Here is a view of a memory.

Address    Value (for example)
0 20
1 46
2 -5
3 0
... ...

Typically, each memory address holds one byte, which can hold a number from -128 to 127. Some operations use several (typically 4) consecutive bytes called a word. In a four-byte word, we can store an integer from -2,147,483,648 to 2,147,483,647.

The operating system breaks the memory for a program up into three parts: the static part, the stack and the heap.

Static Memory

The static part of memory holds your program, some constants that your program contains, and global variables. The program itself (a sequence of machine-language instructions) normally does not change during its execution. Constants are also unchanged. Global variables can be changed, but they occupy the same memory locations throughout. The static portion of memory is called static because it is allocated once, when the program starts, and remains of the same size while the program runs.

The Run-Time Stack

The run-time stack (often called simply the stack) holds information necessary to manage function calls. The local variables of functions are stored in the stack, along with extra information to allow a function to know, for example, where it should return control when it is done.

When a function is called, it is automatically allocated as much memory as it needs in the stack. When the function leaves, all of its memory in the stack is automatically deallocated, to be reused later for another function.

The Heap

The heap is an area of memory where a program can allocate and deallocate memory. The key difference between the stack and the heap is that memory is not deallocated from the heap until the program explicitly deallocates it. The heap is used to get memory that a program can continue to use even after the function that allocated the memory has returned.

1.3 Expressions and Statements

An expression computes a value. For example, 3+5 has value 8. A statement is executed for an effect. For example, statement n = 1; puts value 1 into variable n.

An expression can both compute a value and have an effect. When an expression has an effect, that effect is called a side-effect. For example, if variable n has value 5, then expression n++ has value 5, but also has the side-effect of setting n to 6.

Any expression can be a statement. So, for example, n++ is an expression, but you can write it as a statement:

      n++;
When an expression is used as a statement, the expression is computed, but its value is ignored.

You can create one large statement, called a compound statement, or a block, from several smaller ones by enclosing the statements in {...}. For example,

      {
        n++;
        m++;
      }
is a compound statement. The statements in the compound statement are executed in the order in which they are written.

Note: Each statement generally ends on a semicolon. An exception is a compound statement, which ends on }.


1.4 Simple Data Types

Integer Types

Because C and C++ are low level languages, they provide several integer data types. Using these types, the programmer has very close control over how memory is used. Here are the integer data types.
char
A char is an integer stored in one byte. It is typically used to store the code for a character, but can be used to store any kind of integer. Some compilers say that a char is signed, meaning that it is taken to be an integer from -128 to 127. Some compilers say that a char is unsigned, meaning that it is an integer from 0 to 255.

unsigned char
Same as char, but all compilers treat this as an integer from 0 to 255.

short int
A short int is typically an integer stored in two bytes. It can hold values from -32,768 to 32,767. You can abbreviate short int as just short.

long int
A long int is typically an integer stored in four bytes. You can abbreviate long int as just long.

int
The type int is either the same as short int or the same as long int, at the compiler's discretion.

short unsigned int
long unsigned int
unsigned int
These types are used to store integers without a sign. Because there is no sign bit, you get one extra bit for storing the integer itself, so you can store twice as many values.

Operations on integers include

+ addition,
- subtraction,
* multiplication,
% x % y is the remainder when you divide x by y,
/ x/y is the quotient when you divide x by y (an integer)

Caution: 2/3 is 0, since the result of dividing two integers is an integer.

Integer Constants

Integer constants can be written in decimal (23) octal (071) or hexadecimal (0x3A). Character constants such as 'a' are integers -- 'a' is 97, the ascii code for character a. Some useful special characters are '\n' (newline) and '\t' (tab). The ascii code for the backslash character is written '\\'.

Enumerated Types

An enumerated type is an integer type with named members. You create such a type using an enum declaration. For example,
      enum Color {
        red, orange, yellow, green, blue, indigo, violet
      };
creates a type called Color (in C++) or enum Color (in C or C++). The compiler defines red to be 0, orange to be 1, etc., through violet, which is defined to be 6.

Floating Point Types

float Real numbers stored in four bytes. These offer about 7 digits of precision.
double Real numbers stored in eight bytes. These offer about 15 digits of precision.
long double Real numbers stored in more than eight bytes. This is not available with all compilers.

Operations on floating point types are the same as those for integers, except that % is not available, and division produces a floating point number. 2.0/3.0 yields 0.666666666666667.

Pointers

A pointer is a memory address. It typically occupies four bytes. What you can do with a pointer is look at or modify the memory at the address given by the pointer. You can also compute pointers to memory cells around a given pointer, and compare pointers to one another.

Each pointer has a type. If T is a type, the type T* is the type of a pointer that points to something of type T. For example, type int* is the type of a pointer to an int.

Operations on pointers are as follows.

*   *p is the content of the memory at address p. The number of bytes that you get depends on the type of p. For example, if p has type char*, then *p is a one- byte quantity (of type char). If p has type long int*, then *p is a four-byte quantity (of type long int.)

If p has type T*, then *p always has type T, for any T.

+   Suppose that p is a pointer pointing to a type that occupies b bytes, and suppose that k is an integer. Then p+k is memory address p + bk. For example, suppose that p is a pointer with memory address 100. If p has type char*, then p+2 is a pointer, also of type char*, with memory address 102. If p has type long int*, then p+2 is a pointer, also of type long int*, with memory address 108. Notice that, if p has type long int*, then p+1 points to the next long int in memory, just after the one pointed to by p, without overlap.
-  You can subtract an integer from a pointer. The result is similar to addition. For example, if p has type int*, then p-1 points to the int that occurs just before the int pointed to by p.
[ ]  p[i] abbreviates *(p+i). So p[0] is the same as *p. p[1] is the thing in memory just after the thing pointed to by p.

Note: Memory address 0 is not available to application programs. It is used as a special pointer value, called the null pointer. Normally, NULL is another name for 0. You use it when you want the null pointer.


1.5 Variables and Assignment

Declaring Variables

To declare a variable x of type T, you write
T x;
You can declare several variables at once. For example, to declare char variables c and d, and long int variables x and y, you can write
     char c,d;
     long int x,y;
When you write variable declarations outside a function, the variables will be allocated in the static area of memory. When you write them inside a function, the variables are automatically allocated in the function's stack frame, in the run-time stack. So writing
     long int n;
inside a function causes four bytes to be allocate in the stack, when the function encounters this variable declaration.

In C++, a variable can be declared anywhere. In C, a variable can only be declared outside a function or at the start of a function or block.

You can declare variables inside blocks. A variable declared in a block is only accessible within the innermost block that contains the declaration. For example, if you write

	{int x;
	 ...
	}
then you can use x within this block, but not outside. This is a convenient way to declare variable that are only used for a short part of a function.

Initial Values

When you create a variable, you can give it an initial value if desired. For example,
     long int n = 0;
not only allocates memory for variable n, but it sets that memory to 0. Outside a function, the initial value of a variable must be a constant, such as 0. Inside a function, the initial value of a variable can be given by any expression. For example, you can write
      long int n = a + b;
as long as the values of a and b are known.

Sometimes you want a "variable" that you never intend to change, after giving it an initial value. you can use modifier const for such variables. For example,

	const long int size = 40;
declares size to be 40, and does not allow size to be changed.

You can declare pointer variables to be constant, but the const modifier means something different for a pointer from what it means for a nonpointer. If you declare

	const char* p;
then you are allowed to change p, but you are not allowed to use p to change the memory that p points to. So you can get p[1], but you cannot set p[1] = 'a'.

Caution: If you do not initialize a variable, it has a "junk" value. Do not use the value of a variable before you put something in the variable.

Assignment

To change the value of a variable, use an assignment statement, of the form x = e, where x is a variable and e is an expression. For example,
      n = a + 1;
computes a+1 and puts the result in variable n. Statement
      n = n + 1;
adds one to the value of variable n, and puts the result back into n.

An assignment is an expression. That is, it has a value. The value is the value just put into the variable. So, for example,

      n = 0
is an expression that has value 0, but also has a side-effect: it puts 0 in variable n.

Note: you only include the type of a variable when you are creating the variable. So

      long int n = 0;
creates variable n and sets it to 0. But
      n =  0;
sets a previously created variable n to 0.

When you assign a value to a pointer variable, you change the pointer variable itself. If you what to modify the memory pointed to by p, assign to *p. For example,

	*p = 20;
goes to the memory whose address is in variable p and puts 20 there.

Abbreviations for Assignments

There are abbreviations for frequently done kinds of assignments. Here are a few.

Abbreviation  Meaning
n += k n = n + k
n -= k n = n - k
++n n = n + 1 (where the value of expression ++n is the value of n after the assignment)
n++ n = n + 1 (but the value of expression n++ is the value of n before the assignment)
--n n = n - 1 (where the value of expression --n is the value of n after the assignment)
n-- n = n - 1 (but the value of expression n-- is the value of n before the assignment)

Declaring pointers

***Special Warning.. You must be careful when declaring pointer variables. Each variable is said to be a pointer or not to be a pointer independently of the others. If you write
      int *x, *y;
then you declare x and y each to be pointers to integers. If you write
      int *x, y;
then you declare x to have type int*, but y to have type int. There is no * on y, so y is not a pointer. Spacing in your program does not affect this. So if you write
      int* x, y;
it might look like x and y are each pointers, but in fact only x is a pointer; y is an int. To avoid this problem, you might want to declare only one pointer variable per line. Alternatively, use a typedef, discussed in Section 1.7.

Getting the Address of a Variable

If x is a variable, then &x is the address of x (a pointer). If x has type int, for example, then &x has type int*.

What if you have a pointer variable p, and you get its address? If p has type int*, then &p has type int**. That is, it is a pointer to a pointer. &p is the address of a variable that contains a pointer value.

Caution: Be careful how you use the address of a variable. If the variable is in the stack, then it will be deallocated as soon as the function that created that variable returns. The address will no longer be valid.


1.6 Structured Data Types

C/C++ provides ways to deal with areas of memory that contain several values.

Arrays

An array consists of several of the same type of thing stored in consecutive memory addresses. If A is an array, then A[i] is the i-th thing in array A. All subscripting is from 0 in C/C++. So if you have an array A of four things, then those things are A[0], A[1], A[2] and A[3].

To declare an array, you must give the size. Write

      int x[20];
to declare an array x of 20 integers. Some versions of C++ allow the size to be any expression, but others require the size to be a constant, not something that you compute. When the size is a constant, you almost always use a named constant to select the size. That way, it is easy to modify the size later. You write, for example,
	const int namesize = 40;
	char name[namesize];
If you are not declaring an array, but are merely refering to one that is created elsewhere (as you do, for example, in a parameter list of a function heading) you do not need to give the size. You can write int x[] to indicate that x is an array of integers.

Equivalence of Arrays and Pointers

In C/C++, an array is really just the address where the array begins. So it is a pointer to the first thing in the array. If you declare array x as above, then x itself has type int* -- a pointer to an integer. It points to the first integer in the array. Recall that, when p is a pointer, p[i] abbreviates *(p+i). It is no different for arrays. A[i] abbreviates *(A+i).

Structures

A structure is a collection of values, possibly of different types, occupying consecutive memory locations. To get a structure, you must declare a structure type. You can do that as in the following example.
      struct MyStruct {
        long int size;
        char* name;
      };
In C++, this declaration causes you to get a type called MyStruct. You would write
      MyStruct s;
to declare a variable s of type Mystruct. (Variable s occupies eight bytes of memory, four for the long int and four for the char*.)

In C++, you can write the word struct explicitly when you declare a variable, as in

      struct MyStruct s;
and this form is required in C.

The parts of a structure (size and name in the example) are called fields. To get the fields of a structure, you use "dot" notation. s.name is the name field, and s.size is the size field. For example,

      s.size = 6;
      s.name = "George";
puts values into the structure. Note that no values are in the structure until you put something there.

You can copy the value of one structure into another. For example, if you declare variables s and t as having type MyStruct, then

 
      s = t;
has the same effect as
      s.size = t.size;
      s.name = t.name;
That is, the entire contents of structure t is copied into the memory occupied by structure s. If you declare structure
      struct Complex {
        double re, im;
      };
and you declare
      Complex z,w;
then
      z = w;
has the same effect as
      z.re = w.re;
      z.im = w.im;
This is an important difference between arrays and structures. To put it succinctly, the size of a structure is part of its type, but the size of an array is not part of the array's type. So copying a structure copies all of the memory occupied by the structure. But if p and q are pointers to arrays, then
	p = q;
only makes p point to the same array as q. It is the pointer that is copied, not the arrays themselves.

Note: A structure type is not the same as a structure variable. It would make no sense to write MyStruct.name since MyStruct is a type, not a variable, and has no name field to select. To use a structure, declare a variable of the structure type.

Initialization of Structures

You can arrange for the fields in a structure to be initialized whenever a structure variable is created. This is part of C++, and is not available in C. Inside the structure, put a function definition, without a return type, and with no parameters, whose name is the same as the name of the structure. This function is called a constructor. For example, suppose that you want each complex number to initially 0. You can write the complex type as follows.
      struct Complex {
        double re,im;

        Complex() 
        {
          re = 0.0;
          im = 0.0;
        }
      };
Another option is to ask for the initial values when a variable is created. That can be done by creating a constructor that has parameters. For example, you might declare the complex number structure as follows.
      struct Complex {
        double re,im;

        Complex() 
        {
          re = 0.0;
          im = 0.0;
        }

        Complex(double r, double i)
        {
          re = r;
          im = i;
        }
      };
Now
        Complex z, x(1,2);
declares variable z to be initialized by the constructor without parameters (and hence to have both its real and imaginary parts initialized to 0) and x to be initialized the the constructor with parameters (and hence to have a real part initialized to 1, and an imaginary part initialized to 2).

Pointers to Structures

It is quite common to have pointers to structures. Suppose that pointer variable p has been declared by
        MyStruct* p;
Initially, p has a junk value, so we would not want to look to see where it points. Let us presume that p has been given a value somehow, and we want to get the size field of the structure to which p points. That structure is *p. We can write (*p).size. For example,
        n = (*p).size;
The parentheses are required. Writing *p.size will not get you what you want. There is a convenient abbreviation that you can use to avoid the parentheses. p -> size abbreviates (*p).size. For example, you can write
        n = p->size;
Note: The -> notation is used only when you have a pointer to a structure. If s is a structure, not a pointer, then use dot notation such as s.name, not s->name.

Null-Terminated Strings

Arrays of characters are used to represent strings. A standard way or representing strings is to put a 0 in the last byte, so that it is possible to tell where the string ends. For example, the string "abc" would be represented as an array as follows.

Address    Content
p    'a'
p+1    'b'
p+2    'c'
p+3    0

Note that the 0 is not the character '0', but is the integer 0. It is called the null character. When a null character is at the end of a string, it is called the null terminator of the string. The null character can be written '\0'.

When you write "abc" in a C/C++ program, the compiler allocates four bytes, and fills them as shown above, with 0 in the last byte. The type of "abc" is char* -- a pointer to the first byte of the string.

In general, strings are stored in two parts: the pointer, which serves as the handle of the string, and the memory that is pointed to, which holds the content of the string. When you deal with strings, you must be aware of these two parts. Even though you often think of a variable of type char* as holding a string, it does not actually point to any string until you set it to point to appropriate memory.

If you want to put a double quote character in a string constant, precede it with a backslash.


1.7 Typedefs

You can give names to types using typedefs. Declaration
       typedef oldT newT;
declares name newT to be a new name for a previously declared type oldT. For example, you might declare a pointer type.
       typedef char* charptr;
Now, if you write
       charptr x,y;
both x and y will have type char*. You can get around some difficulties of C/C++ using typedefs. For example, there is no type for true/false data. Instead, a truth value is just an integer, with true being 1 and false being 0. You can write
       typedef char bool;
to define type bool to be the same a char. Some implementations of C++ define this type bool for you. We will assume a file bool.h that defines this. Additionally, we will presume that bool.h defines TRUE to be the constant 1, and FALSE to be the constant 0.

1.8 Allocating Memory in the Heap

To get memory from the heap, you can use either C or C++ style. C++ style is the most pleasant by far.

Memory Allocation in C++

Expression
      new T
allocates enough memory in the heap for something of type T, and returns the address of that memory, of type T*. For example, to get a pointer to a new MyStruct, you might write
       MyStruct* ms = new MyStruct;
As you can see, ms is a variable of type MyStruct*. That is, it is a pointer to a MyStruct. It is initialized to point to newly allocated memory in the heap. As another example, consider
       Complex* c = new Complex;
which creates a variable c of type Complex*, and initializes it to the address of some newly allocated memory in the heap. It is very common to have pointers to structures, and to allocate those structures in the heap. If you like, however, you can allocate a variable of a simple type in the heap. For example,
       long int* p = new long int;
You can allocate an array in the heap as well. To allocate an array of 20 chars, you might write
       char* p = new char[20];
Notice that p has type char*. Here, p will be set to the address of the first byte of the array. To set the third byte of the array to 'a', you would write
      p[2] = 'a';
(Recall that the first byte is p[0].) One nice feature about allocation in the heap is that you can compute the amount of memory to allocate. If integer variable n has a value, then you can write
       char* s = new char[n];
to allocate n bytes in the heap.

Memory Allocation in C

It is recommended that you use the C++ style. However, you are likely to see the C style used occasionally. In C, memory is allocated by calling a system function called malloc. malloc(n) returns n bytes in the heap and returns a pointer to the beginning of those bytes. To allocate a string of 20 bytes, you would write
       char* p = (char*) malloc(20);
Note the "cast" (char*), which tells the compiler to consider the pointer to be a char* pointer. malloc returns a pointer of an unknown kind, and you must tell the compiler what kind it should be. Be very careful with malloc. It allocates a given number of bytes. If you want to allocate a single MyStruct value in a C program, you would write
      struct MyStruct* ms = (struct MyStruct*) 
                            malloc(sizeof(struct MyStruct));
where sizeof(T) is the number of bytes needed by something of type T. As you can see, the C++ new operator is much nicer.

What If I Run Out Of Memory?

If you try to allocate memory in the heap (either via new or malloc) and there is no more memory available, you will get a null pointer as the result. It is a good idea to check that your pointer is not null after doing an allocation.

1.9 Deallocating Memory

You can return memory that you have allocated using new or malloc to the system. Suppose that pointer p points to memory that you have allocated. If you allocated that memory using new, and you did not allocate an array, then write
      delete p;
to return that memory to the system. If you allocated using new and you allocated an array, as in
      char* p = new char[30];
then delete the memory using
      delete[] p;
If you allocate that memory using malloc, use
      free(p);
to return the memory to the system. It is very important that you deallocate memory in a way that complements the way in which you allocated it. If you used malloc to allocate, use free to deallocate. Never deallocate a pointer that points into the run-time stack.

Be very careful about deallocating memory. Keep in mind that you can have more than one variable that holds a pointer to a given piece of memory. If you deallocate the memory, all of those pointers become stale. They continue to hold memory addresses, but the memory to which they refer does not belong to you any more. You have no idea what that memory will be used for after it has been deallocated. A pointer that points to deallocated memory is called a dangling pointer. Problems that occur from the use of dangling pointers are among the most devastating, and most difficult to eliminate, of all the problems that you can have. By avoiding them with care, you will not be subjected to the pain of finding them by laborious debugging. (Note that there is nothing terribly wrong with just having a dangling pointer in your program, as long as you do not use it.)

If you fail to deallocate memory that you have allocated, and you let that memory become inaccessible to you, the system will not take that memory back automatically. The system will presume that your program still needs the memory. The result is a memory leak. Memory leaks are quite benign for programs that do not use much memory, but can be deadly in long runs, when your program will run out of memory.


1.10 Conditionals

If-statements

Conditionals are used for making decisions. The most frequently used conditional is the if- statement. It has the form
      if(condition) statement else statement
The first statement is done if the condition is true, and the second is done if the condition is false. The else part is optional. If it is omitted, then nothing is done when the condition is false. Typically, the statements are compound statements. Here is an example.
      if(x == 0) {
        y = 0;
      }
      else {
        y = 1/x;
      }
The parentheses around the condition are required.

Note: The condition being tested is any integer or pointer expression. If the condition has value 0, then it is considered to be false. If the condition has any nonzero value, then it is considered to be true. This can be confusing. For example, cin.fail is a function that will tell you whether the most recent operation on cin failed. In C/C++, a function is represented by the address where the machine-language code of the function is loaded. Since an address is a pointer, a function is a pointer. If you write

        if(cin.fail) {
          ...
        }
you are not running function cin.fail at all, but are asking whether the pointer cin.fail is not null. Of course, it is not. The correct way to ask whether the most recent operation on cin failed is
        if(cin.fail()) {
          ...
        }

Comparisons and Boolean Operators

Here are some operations for creating conditions.

== equality test
!= inequality test
> test for greater
< test for less
>= test for greater or equal
<= test for less or equal
&&'and'. Expression A && B is evaluated by first evaluating A. A has value 0, then A && B also has value 0, and B is not evaluated. Otherwise, B is evaluated; if B has value 0, then A && B has the same value 0, and otherwise has value 1.
|| 'or'. Expression A || B is evaluated by first evaluating A. If A has a nonzero value, then A || B has value 1, and B is not evaluated. Otherwise, A || B has value 1 if B is nonzero and value 0 if B is zero.
! 'not'. Expression !A is 0 if A is nonzero, and is 1 if A is 0.

Caution: You can use the comparison operations on pointers. They compare addresses. For example, if p and q each have type char*, then testing whether p == q will test whether p and q are the same pointer. It will not test whether the strings pointed to by p and q are the same.

Caution: A frequent error is to confuse = and ==. If you write

         if(x = y) ...
then you will not get a test to see if x and y have the same value. Instead, you will get an assignment, putting the value of y into x, and that value will be tested. This is a serious error. You should learn to check your program carefully for this. One way to avoid this problem is, when doing a comparison that involves a constant, to write the constant first. That way, if you accidentally write = instead of ==, you will get a compile error. For example, write
        if(0 == x) ...

Conditional Expressions

You can make a test in an expression in C/C++. Expression
        condition ? val1 : val2
is val1 if condition is nonzero, and is val2 if condition is 0. For example, expression
        x > y ? x : y
evaluates to the larger of x and y.

Switches

Sometimes you want to make several tests of a single value. C/C++ provides a switch statement for that purpose. Here is an example.
      switch (x) {
      case -1: 
        printf("Less than zero\n");
        break;

      case 0:
        printf("Equal to 0\n");
        break;

      case 1:
        printf("Greater than 0\n");
        break;

      default:
        printf("Not one of the things I know about\n");
      }

Note: You must include break; after each case. If you do not, then the program will keep going into the next case. For example, if you run the following with day = 5

      switch(day) {
      case 5: 
        printf("five gold rings\n");
      case 4:
        printf("four calling birds\n");
      case 3:
        printf("three french hens\n");
      case 2:
        printf("two turtledoves\n");
      case 1:
        printf("and a partridge in a pear tree\n");
      }
the program prints
five gold rings
four calling birds
three french hens
two turtledoves
and a partridge in a pear tree
Note: If there is no default case, then nothing is done if none of the cases match.

Note: You can have two or more cases do the same thing. Just put them in a row, as in

      case 4:
      case 5:
        text for these cases
Note: You can only use a switch when the type of the value being tested is an integer type.

1.11 Loops

C/C++ provides three kinds of loop.

While Loops

The while-loop is used to do a statement zero or more times. Statement
      while (condition) statement
does the statement (called the loop body) repeatedly until the condition is tested and found to be true (nonzero). For example, suppose that s is a pointer to a null-terminated string. The following sets n to the length of string s.
        char* p = s;
        n = 0;
        while(*p != 0) {
                   p++;
          n++;
        }
The condition is tested before the loop is entered for the first time. If the condition is false immediately, then the loop body is not performed at all.

Do Loops

A do-loop is used to do a statement one or more times. Statement
        do statement while (condition);
does the statement repeatedly until the condition is true (nonzero), but tests the condition for the first time only after it has already done the statement once.

For Loops

A for-loop is typically used to count through a fixed sequence of values. In general, statement
        for (A; B; C) statement
abbreviates
        A;
        while (B) {
          statement
          C;
        }
For example, here is a statement that prints the numbers from 0 to 9.
        for(i = 0; i < 9; i++) {
          cout << i << endl;
        }
This for-loop abbreviates
        i = 0;
        while(i < 9) {
          cout << i << endl;
          i++;
        }
Note: A for-loop has just three parts, separated by semicolons, within its parentheses. You can omit any part, but must not omit the semicolons. If you omit the test (the middle part), you get an infinite loop. Note: In C++, if you declare any variables within the parentheses after for, those variables are only available within the for-loop. In C, those variables can be used after the for loop as well. (Some implementations of C++ behave like C in this respect.)

1.12 Functions

A function takes zero or more parameters, computes, and then returns a value. A function definition looks like this.
        return-type function-name(parameters)
        {
          statements
        }
Within the statements, you can use a return statement to cause the function to stop computing and to return a value. Here is an example, a function that returns three times its parameter.
        int triple(int x)
        {
          return 3*x;
        }
To use function triple in an expression, just provide a value for the parameter. For example, statement
        n = triple(5);
sets n = 15. Note: In the definition of function triple, parameter x is called a formal parameter. It is just a place holder. In the expression triple(5), the number 5 is called the actual parameter. It is the value that is used in place of the formal parameter.

You can use functions in arbitrarily complicated expressions. For example, statement

        m = n + triple(k + 2);
sets m = n + 3(k+2). Another example is a function that computes the length of a null-terminated string. We use the loop from the loop section.
        int length(char* s)
        {
          char* p = s;
          int n = 0;
          while(*p != 0) {
            p++;
            n++;
          }
          return n;
        }
To use the length function, just give it a pointer to a null-terminated string. For example,
        r = length("abcd");
would set r = 4.

Here is a function that takes two parameters, and returns the larger one. Note that the parameters are separated by a comma, and each must have a type.

        int max(int x, int y)
        {
          if(x > y) return x;
          else return y;
        }
For example,
        r = max(5,8);
would set r = 8. We can use function max to create another function max3, which computes the largest of three values.
        int max3(int x, int y, int z)
        {
          return max(x, max(y, z));
        }

Prototypes

Sometimes, you need to tell the compiler what types of parameters a function takes, and what type it returns, without writing the function. You can use a prototype, which is just the function heading, followed by a semicolon. A prototype for function max3 is
       int max3(int x, int y, int z);
In a prototype, you may include names of parameters, as is done above, or you can omit them, as in
       int max3(int, int, int);

Functions That Do Not Return Values

In C/C++, each function must have a return type indicated. However, that return type can be the reserved word void, indicating that no value is returned. For example, the system function exit takes an integer parameter, and stops the program. Its prototype is
        void exit(int status);
You do not need to have a return statement in a function that returns void. If you like, you can include one, but it should just say
        return;

Calling Modes

Three ways to pass information to a function are call-by-value, call-by-pointer and call-by reference.

Call-by-value. Call-by-value is the default calling mode. When you pass a parameter, the value of that parameter is sent to the function. The formal parameter acts as a variable local to the function that is initialized with the value of the actual parameter. If a structure is passed by value, the contents of the structure is copied into the formal parameter.

Call-by-pointer. Call-by-pointer is a special case of call-by-value in which the value that is passed is a pointer. When you have a pointer, you can do whatever you like with the memory that the pointer points to. For example, consider function increment.

        void increment(int* p)
        {
          (*p)++;
        }
Function increment adds one to the variable that points to. For example, in
        {int x = 0;
         increment(&x);
         ...
        }
the value of x would be 1 just after the call to increment. Notice that we must pass a pointer to function increment, so we use &x to get a pointer to variable x. If you already had a pointer, you would just pass that pointer value. For example,
        {int* p = new int;
         *p = 0;
         increment(p);
         ...
        }
Here, after the increment call, variable *p holds 1. (Variable p holds a pointer to variable *p.) What would happen if we did this?
        {int* p = 0;
         increment(p);
         ...
        }
The line int* p = 0 sets pointer variable p to 0. So p contains a null pointer. When function increment does *p, it will cause the program to abort. (You will get a memory protection error, since memory address 0 is not accessible to you.)

Call-by-pointer is the default mode for arrays, since an array is the same as a pointer to its first member. Sometimes, you want to pass an array as a parameter, but do not want to allow the array to be changed. To do that, use a constant pointer. For example, the prototype for the function length written previously should really have been

        int length(const char* p);
since the length function does not change the array that is passed to it.

Call by Reference. When you pass a variable as a parameter, it can be awkward to deal explicitly with pointers. C++ (but not C) has a way to pass variables in a more convenient way. Use & instead of * in the type of the parameter. Then the parameter is a reference parameter, and it is thought of as a variable that is being passed. For example, function increment can be written using call-by-reference.

        void increment(int& x)
        {
          x++;
        }
Notice that variable x is now just an integer variable, not a pointer variable. To use this new increment function, you do something like this.
        {int n = 0;
         increment(n);
         ...
        }
You do not pass &n now. You just pass n as the actual parameter. The compiler passes the variable n to function increment. Within function increment, formal parameter x just names variable n. So after the call to increment, n has value 1.

When the compiler implements call-by-reference, it really just does call-by-pointer, but it inserts the & and * operations where needed automatically.

Return Modes

Return-by-value. Typically, a function returns a value. For example, the max function takes two integers and returns an integer (that is, a value).

Return-by-pointer. You can have a function return a pointer. Then, the caller gets access to the memory that the pointer points to. A pointer returned by a function should usually point into either static memory or into the heap. Returning a pointer into the stack is asking for trouble, except when carefully controlled. Here is a function that allocates an integer variable in the heap, sets it to 0, and returns a pointer to the new integer variable. It also checks that the system did not run out of memory.

        int* allocInt()
        {
          int* p = new int;
          if(NULL == p) exit(1);
          else {
           *p = 0;
           return p;
          }
        }

Return-by-reference. In some circumstances, you would like a function to return a variable, so that a function call expression can be used just like a variable. In such circumstances, you use return-by-reference. Often, the reference that is returned is the same as one of the reference parameters. Here is a function that increments a variable, and returns the variable.

        int& increment(int& x)
        {
          x++;
          return x;
        }
Now you can write increment(increment(x)) to add two to x, if you were so inclined.

1.13 Output

You can perform output to a terminal or a file in either the C or C++ styles. (C style can be used in either C or C++ programs. C++ style can only be used in C++ programs.)

C++ Style

To use C++-style output, include header file iostream.h. Binary operator << is used to write. Expression
       str << x
prints the value of x onto stream str, and returns str by reference. You can write many different kinds of things, such as integers, characters and strings. Because expression str << x returns str, you can perform several writes in one statement. For example,
    str << x << y;
is understood to mean
    (str << x) << y;
which first prints x on stream str, then prints y on the same stream.

Stream cout refers to the standard output (usually the terminal). So

        int n = 2;
        char c = 'a';
        cout << n << c;
writes 2 and a, with no space between them. You can include null-terminated strings as well, as in
        cout << "Thank you for running me" << endl;
The endl constant is just "\n". Some other operations using cout are as follows.
cout.write(buf,n)
Write n characters from character array buf.

cout.flush()
When you print on cout, what you print goes into a buffer until the buffer becomes full. Then the contents of the buffer is actually printed. This delayed printing can be a problem sometimes. Function flush prints what is in the buffer.

cout.close()
Write an end-of-file marker to the file being written by cout. This terminates the file. After doing this, you can no longer write to cout.
Variable cout is just one variable of type ofstream, used to print to a file. You can create other ofstream variables, and tie them to files. To do this, include header file fstream.h. Then
        ofstream myout("myfile:, ios::trunc | ios::out);
declares variable myout of type ofstream. It also uses a constructor that ties this variable to the system file called "myfile". The file will be emptied, if it already exists. Anything that you write will be written into that file. When you are done writing to the file, close it, using the close function.

C Style

To write in C style, you can use library functions printf and fprintf. Include header file stdio.h to use these. The first parameter to printf is a format, a string that tells what the output should look like. Within the format are sequences that start with %, and that indicate that the value of the next parameter should be put here. For example,

        int m = 3;
        int n = 5;
        printf("I have n = %d and m = %d\n", n, m);
would print
I have n = 5 and m = 3

Here are some of the formats that are available.

%c
Print a character.

%d
Print an integer. %3d prints the integer in three spaces, padded on the left with blanks. If the integer is too large for three spaces, more will be used. %-3d is similar, but pads on the right with blanks.

%ld
Print a long integer. %5ld print the long integer in five spaces.

%f
Print a floating point number. %6.2f prints it in six spaces, with two digits to the right of the decimal point.

%s
Print a string. The parameter must point to a null-terminated string. %12s prints the string in 12 characters, padded on the left with blanks.

%%
Print a % character.
To print on a file, you must open the file. Use the following.
        FILE* f = fopen("filename", "w");
to open a file for writing. Now use
        fprintf(f, format, ...);
to print on the file. Finally, to close the file, use
        fclose(f);
For example, here is a function that writes file "test.out", putting string
abcdefghijklmnopqrstuvwyz
in the file.
        void print_alphabet()
        {
          char c;
          FILE* outf = fopen("test.out", "w");

          for(c = 'a'; c <= 'z'; c++) {
            fprintf(outf, "%c", c);
          }
          fprintf(outf, "\n");
          fclose(outf);
         }        
See documentation for fopen for more on opening files.

1.14 Input

You can perform input from a terminal or a file in either the C or C++ styles. (C style can be used in either C or C++ programs. C++ style can only be used in C++ programs.) Use the same header files as for output.

C++ Style

Variable cin refers to the standard input (usually the terminal). Binary operator >> is used to read. You can read many different kinds of things, such as integers, characters and strings, and you can perform several reads in one statement. For example,
        int n;
        char c;
        cin >> n >> c;
reads an integer, and puts it into variable n, and then reads a character, and puts it into variable c. If you read a string (char*) using >>, characters will be read up to a space or newline, and put into the string. Be sure that there is enough room in the arrray! If you read a character, you will get one character, except that white-space characters (blank, tab, newline) are skipped.

Some other operations using cin are as follows.

cin.eof()
True if cin is at the end of file.

cin.fail()
True if the most recent operation done failed. This can also be used to test whether opening a file failed.

cin.get()
Returns the next character, or the special value EOF (-1) if there is no more data in the file. The type of cin.get() is int, not char. This function does not skip over white-space characters.

cin.peek()
Returns the next character, without removing it from the stream. Like cin.get(), it returns an int, and returns EOF on end-of-file.

cin.putback(c)
Puts character c back at the front of the stream.

cin.read(buf,n)
Reads n characters and puts them into buffer buf. buf must be a pointer to a buffer that you have already allocated.

cin.getline(buf,n)
Reads a line and puts it into buf. It replaces the end of line character by a null character. n is the size of buf. No more than that many characters will be read.

cin.close()
Closes stream cin. (You cannot read from cin after closing it.)
You can read from a file. To do so, include header file fstream.h. To read from a file, just create a stream attached to that file. Declaration
        ifstream f(name, ios::in);
creates variable f, a stream bound to file name. You can use the same operations on f as on cin. For example,
        ifstream ins("test.txt");
        int k;
        ins >> k;
        ins.close();
creates a stream called ins and reads an integer from it. When you are done with a stream, close it, as is done above. But don't close it until you are done reading from it. The example only wants to read one integer, and no more.

C Style

To read in C style, you can use library functions scanf and fscanf. Include header file stdio.h to use these. The first parameter to scanf is a format, telling what kind of data to read. Usually, the format just has indications of types that are similar to the indications in output formats. For example, to read two integers m and n, you would write
        scanf("%d%d", &m, &n);
Notice the need to use &. The parameters after the format are call-by-pointer. If you use format %s, then a string will be read up to a space or newline.

To read from a file, you must open the file for reading, and use fscanf. For example,

 
        int n;
        FILE* inf = fopen("infile.txt", "r");
        fscanf(inf, "%d", &n);
        fclose(inf);
reads a single integer from file infile.txt. A handy function is gets. gets(f,s) reads a line from file f, and puts it into array s. The newline character in s is replaced by a null character, so that s becomes a null-terminated string.

1.15 The Preprocessor

When a C or C++ program is compiled, it is first passed through a preprocessor, which makes some modifications to the program. One thing that the preprocessor does is to expand includes. When you write
#include 
the preprocessor inserts the contents of file stdio.h in place of the include line. Include lines can have the file name enclosed in <...> or in "...". If you use <...>, the preprocessor looks for the file in the directory that has standard header files. If you use "...", the preprocessor looks for the file in the current directory. The preprocessor provides a macro facility. If you write
#define NULL 0
for example, each occurrence of NULL will be replaced by 0 by the preprocessor. Common substitutions are
#define TRUE 1
#define FALSE 0
We assume that header file bool.h define TRUE, FALSE and NULL. Note that there is no semicolon. If you write
#define TRUE 1;
then each occurrence of "TRUE" gets replaced by "1;", which is probably not what you want.

Note: TRUE will only be recognized by the preprocessor when it is not part of a longer word. If you write TRUE_LIES, for example, the preprocessor will not make a change.

Macros can have parameters. You can write

#define max(x,y) ((x) > (y) ? (x) : (y))
to create a macro for computing the maximum of two values. max(n,0) would be replaced by (n) > (0) ? (n) : (0). You should always put parentheses around arguments in macros, as has been done here, or you can get very strange effects from the textual substitution done by the preprocessor.

Note: Preprocessor directives start with a # in column 1. Don't indent the #. #define lines are one line long. The preprocessor is not free-form.

The preprocessor can also be used to make decisions at compile time. A common kind of decision is to ask whether a symbol is defined. A definition of NULL might look like this.

#ifndef NULL
#define NULL 0
#endif
This says to compile what is between #ifndef NULL and #endif only if NULL is not a defined preprocessor macro. This kind of thing is commonly used in header files (described in the next section) to prevent them from being read more than once by the compiler. A file called myhead.h typically has the form
#ifndef MYHEAD_H
#define MYHEAD_H
...
#endif
The first time the file is read, and MYHEAD_H is defined. Subsequent times, the entire file is skipped.

1.16 Modules

When you build a program from several files, you typically break each file into two parts: a code file (with extension .cpp or .cc for a C++ program, or .c for a C program) and a header file (with extension .h). The code file contains the function and variable declarations. The header file just tells what is in the code file, so that other files can know about them. If there is a function in the code file that other files should be able to use, then there should be a prototype of that function in the header file. If there is a variable in the code file that other files should know about, then there is an extern declaration in the header file. Here is an example of a pair of files. They must be in two different files. These should not be taken as a recommendation on how to write functions: realistically, char_to_count should be a parameter to function count_chars.
// File: count.h

// count_chars(str) returns the number of occurrences of 
// character char_to_count in null-terminated string str.

extern char char_to_count;
int count_chars(char* str);

// File: count.cpp

#include "count.h"

char char_to_count;

// count_chars(str) returns the number of occurrences of 
// character char_to_count in null-terminated string str.

int count_chars(char* str)
{
  int count = 0;
  char* p;

  for(p = str; *p != 0; p++) {
    if(*p == char_to_count) count++;
  }
  return count;
}

When you compile your files, you produce object files, with extension .o or .obj. You then run a linker to link the object files together into a single executable file.

Note: If you declare an array in a module, as in

       int A[30];
then the extern declaration should have the same form, but need not include the size. For example, you might write
        extern int A[];
An array is a pointer constant. Do not put
        extern int* A;
since that tells the compiler that A is a pointer variable, and causes confusion.

1.17 The Main Program

When the linker builds an executable file, it looks for a function of a specific name, and creates the executable file so that it starts with that function. In most systems, the function to start with is called main. The main function has three parameters, although you often use only the first two. When you use two, your main program heading looks like this.
        int main(int argc, char* argv[])
where array argv holds the parts of the command line that was used to invoke this program, and argc is the number of members of argv. argv[0] is the name of the command. If you type command
        myprog -d test.txt
then argc is 3 and argv is as follows.
argv[0] = "myprog"
argv[1] = "-d"
argv[2] = "test.txt"
The return result of main is the exit status of the program. Normally, it is 0. A nonzero value indicates that an error occurred during the program's execution.

Note: If you do not use the parameters argc and argv, you can omit them, and write

        int main()

1.18 The String Library

There are quite a few libraries available -- more for C++ than for C. We cannot discuss all of the libraries here. Instead, we cover a few useful functions for manipulating null-terminated strings that are available if you include string.h.
strlen(s)
Returns the length (number of characters, not counting the terminating null) of string s.

strcat(s,t)
Concatenates string t onto the end of string s. The memory that is used is what occurs just after string s in memory, so you must be sure that buffer s has enough extra memory available at its end. The return value is pointer s.

strcpy(s,t)
Copy string t into the buffer pointed to by pointer s. The return value is pointer s.

strcmp(s,t)
Compare the contents of buffers s and t. Return 0 if the strings are the same, -1 if s is alphabetically before t, 1 if s is alphabetically after t.

strchr(s,c)
Find the first character in string s that is equal to c, and return a pointer to the byte that contains that character. If there is no such character in strings, then return NULL.

strstr(s,t)
Find the first occurrence of string t as a substring of string s, and return a pointer to the start of that occurrence in s. If string t does not occur in string s, return NULL.

strdup(s)
Allocate memory in the heap (using malloc), and copy string s into that memory. Return a pointer to the copy of s.