C/C++ is one of the most common languages used in the industry today. Part of this wide acceptance has to do with the language's power. C/C++ allows a programmer to use high level abstractions which provide information hiding & ease of use, and at the same time low level access to a machine.
This document was written in 1998 for University of Waterloo students taking ECE 450 who have a background in programming but have not used C/C++. This document is not a programming primer and assumes that the reader has some knowledge of the basics: addresses, pointers, Abstract Data Types, etc.
The fundamental variable types in C/C++ are:
int i; float w, x, y; float z;
Most compilers have switched over to using C++ style comments. These are denoted by two forward slashes: "//". Everything from two double slashes to the end of a line is considered to be commented out. The C language uses a pair of characters to denote comments, with everything between a "/*" and a "*/" being commented out, even if it is multiple lines. It is highly suggested that programmers use the C++ style comment, as mistakes in pairing of C style comments can cause many problems in your compile. Furthermore nesting of comments is ignored in C++ but can cause problems depending on your C compiler. A common technique for debugging uses C style comments to comment out a chunk of code temporarily. If the programmer had used C style comments in the code, commenting out the "chunk" temporarily will cause nesting problems. Here are some examples of comments in code:
int x; // this is a C++ style comment, the kind you should use float cost; /* this is a C style comment which can spread across multiple lines */
A collection of keywords can be used to modify the fundamental variable types. All four fundamental types can me modified by the following keywords: unsigned and signed. Most compilers default to using signed types, but use of the keyword forces this. The other two modifiers are the following: long and short. The keyword long is used to double the size of the storage used for the variable, thus increasing the size of precision of the variable. This keyword only works with the int and double fundamental types. The short keyword can only be used with the int fundamental type, and ensures that only half the storage be used to keep the variable. The following code shows some more variable declarations, the comments explain the relative sizes of the variables.
short int a; // usually one or two bytes long int b; // usually eight bytes unsigned float c; // only positive floating point values signed long double d; // 16 byte signed floating point variable short e; // same as "short int e;" long f; // same as "long int f;" long long g; // 16 byte integer -- not supported by all compilers
C/C++ supports three types of operators: binary, unary, and comparison. The binary and comparison operators require two arguments, while the unary only require the one. The following is a list of the basic operators.
Symbol | Name | Example | Result | Comment |
---|---|---|---|---|
+ | addition |
5 + 3 |
8 |
|
- | subtraction |
8 - 3 |
5 |
|
* | multiplication |
5 * 3 |
15 |
|
/ | division |
15 / 3 |
5 |
|
% | remainder |
10 % 5 |
0 |
|
= | assignment |
x = 5 |
x gets value 5 |
|
+= | additive assignment |
x += 5 |
x increments by 5 |
equivalent to x = x + 5 |
<< | left binary shift |
1 << 2 |
4 |
|
>> | right binary shift |
4 >> 2 |
1 |
|
== | comparison |
4 == 5 |
0 |
returns boolean truth value |
< | less than |
3 < 5 |
1 |
returns boolean truth value |
<= | less than equal |
5 <= 5 |
1 |
returns boolean truth value |
> | greater than |
7 > 5 |
1 |
returns boolean truth value |
>= | greater than equal |
5 >= 5 |
1 |
returns boolean truth value |
++ | increment |
x++ |
one more than x |
equivalent to x = x + 1 |
-- | decrement |
x-- |
one less than x |
equivalent to x = x - 1 |
All of the comparison operators return a boolean value according to the evaluation of the expression. Boolean values in C/C++ are zero for false and anything besides zero to indicate truth. For example, the number 5 would be considered true. The unary operators were shown above in post-fix order, they can also be used before the variable. The difference between post and pre-fix orders is when a unary operator is used in a larger expression. If a unary operator is pre-fix then the operation is carried out before the rest of the expression, if it is post fix then the operation is carried out after the expression is evaluated. The following code shows this.
x = 3; y = 2; if( x == y++ ) // compare x with y, then increment y a = 1; x++; // increment x (will now be 4) if( x == ++y ) // increment y and compare it with x b = 2;The if statement causes the line following it to be run if the value in the parenthesis evaluates to true. More on the if statement will be covered later. In the above code the first if would not be true, so the variable a would not be assigned 1. This is because the comparison would be done between x as 3 and y as 2, as the increment has not yet happened. The second if statement would evaluate to true and so b would be assigned 2. This second statement is true since y is incremented before it is compared.
An array in C/C++ is denoted by the use of square brackets. The number of elements in an array can be determined both at compile and run time, but the square bracket notation is normally used for compile time declaration. Arrays are indexed starting at zero. Any element in array can be referenced by using the square brackets and the specific number of the element in question. C/C++ does not do boundary checking on arrays, so be careful not to go out of an array's declared range.
int x[10]; // an array of ten integers x[0] = 5; // assign 5 to the first element in the array x[9] = 15; // assign 15 to the last element in the arrayThere is no specific string type in C, but an array of characters can be used like one. A few tricks are built into the compilers to make this array of character manipulation easier. First a special character called the NULL which is the zero value denotes the end of a string. Second compilers will do automatic allocation of static string arrays for you. Third use of the double quote allows the programmer to specify the whole string at a time rather than character by character.
char c; // declare a character variable c = 64; // put the value 64 in the character variable c = 'A'; // put the character A in the variable char string1[6]; // declare a character array string1[0] = 'h'; string1[1] = 'e'; string1[2] = 'l'; string1[3] = 'l'; string1[4] = 'o'; string1[5] = 0; // string1 now contains the word hello string2[] = "hello"; // have the compiler allocate enough space for youBe careful when using strings that you leave enough space for the NULL character, it takes up an element in the array. Manipulating character strings is normally done using pointers, as the next section describes.
Understanding pointers is key to comprehension of the C/C++ language. A pointer to a variable contains the address where that variable is stored. Using the address-of (&) and indirection (*) operators a programmer can both get a pointer from a variable, and a variable's contents from a pointer.
int *p, q; // declare a pointer to an integer int x, y; // declare some integers x = 3; p = &x; // make p point to x y = *p; // put the value pointed to by p, 3, into y q = p; // make q point to what p points to, which is xArrays and pointers in C/C++ are very closely linked. The name of an array without using the square brackets is in fact a pointer to the first location in the array. The use of the square brackets is really a short form for indirection with an offset. In the following example assume x is an array of integers, and y is an integer.
y = x[2];The following piece of code is equivalent to the previous line.
y = *( x + 2 );What is this line of code doing? The x represents an array, but there are no square brackets beside it, so it means the address of the first element in the array. A value of two is added to the original pointer to get the offset into the array. Effectively equivalent to the first piece of code.
Is there a problem here? The variable x is an integer, which means it will take up more than one address space in memory. Most compilers currently use four byte integers, so shouldn't we be adding 2 * 4 = 8? The compiler automatically handles pointer arithmetic so that adding two means adding two integers.
So if the code is equivalent, why go through all this hassle? You don't have to, the square brackets are there to make your life easier, but always try and keep in mind that an array and a pointer are closely related.
Pointers will keep popping up as you go through the examples in this tutorial. A simple example is when using strings. As was mentioned earlier, strings are arrays of characters, but are not considered a fundamental type in C/C++. There are several libraries which allow the programmer to manipulate strings, and each of these libraries is a collection of functions. The functions require pointers to strings to be passed in to perform operations on, so using the name of the array without the square brackets will give you the desired pointer. More on this will come later as functions and their parameters are discussed.
Earlier we touched on the if statement, and mentioned that it allows the programmer to conditionally execute code. The simplest use of the if puts a statement to be evaluated in parenthesis, and the line following the if is run only if the statement is true.
if( x == 3 ) // compare x with 3 x++; // this line only gets run if x==3 is trueC/C++ provides a way to have multiple lines be grouped together using the brace brackets. This grouping of lines is called a block. When a block follows an if statement, the entire block becomes conditional on the truth value being evaluated.
if( x == 3 ) { x++; // increment both x and y, if x is equal to 3 y++; }
The if statement can be paired with an else statement which is executed when the if is not executed. Like the if, the else can control a single line or block of code. The else can also be immediately followed by another if statement, in order to provide a sequence of conditions.
if( x == 3 ) x++; else if( x == 4 ) x--; else if( x == 5 ) x += 2; else x = 5;Note that the else if and else statements only get evaluated when the original if evaluates to false. For example if x was equal to 3 and thus was incremented, it would now be equal to 4, but the following else if would not be evaluated because the if was already successful.
The two most common loops in C/C++ are the while and for loops. Every type of loop can be created with the while, but the for provides some common short cuts. The while statement takes an expression in parenthesis and executes the following line (or block of lines) while the expression evaluates to true.
x = 0; y = 100; while( x < 3 ) { y += 10; x++; }The previous loop would execute three times for the x values of 0, 1, and 2. The expression is evaluated before the code is executed, so if the result were false then the loop would not be entered.
The previous fragment of code is quite common, and contains three parts, initialization, comparison, and incrementation. C/C++ recognizes this and provides the for statement. The for statement takes three parameters in parenthesis. The first parameter is code which is executed before the loop is run, the second is evaluated for each iteration of the loop, and the last is run at the end of each loop iteration. The following code does the same thing as the previous example with the while loop.
y = 100; for( x=0; x < 3; x++ ) y += 10;The x=0; parameter is run before the loop is executed, and is used for initialization of a counting variable. The second parameter is evaluated for each iteration of the loop, so like the while loop this code will evaluate for x values of 0, 1, and 2. The third parameter is run at the end of each iteration of the loop, and is used to increment the counting variable.
The keyword break allows a programmer to leave a loop regardless of the current value of the conditional expression. The use of break gives you a way of changing the order of when things are evaluated in the loop.
while( 1 ) { x++; if( x == 3 ) break; // leave the while loop y++; }
C/C++ allows for modularity by defining functions. C/C++ does not bother making a distinction between functions and procedures, but allows you to declare that a function does not return a value. A function is defined by the type it returns, the name, and a parenthesis enclosed list of parameters. The keyword return is used to return a value from a function to its caller. The keyword void can be used as a return type when you do not want a function to return anything. The following simple function takes no parameters and returns 3 to the caller.
x = three(); // call the function "three" int three() // declare the function "three" { return( 3 ); } // end threeThe first appearance of a function is used by the compiler to define what parameters can be passed to it. So that the code listing which defines a function does not have to appear physically before the first call to the function, pre-declaration is allowed. This pre-declaration is called prototyping, and looks like the first line in a function definition with a semicolon after the line.
int three(); // a prototype for the above functionFunction parameters are a comma delimited list of variables. The following function takes two integers x and y, and returns their sum.
int add( int x, int y ) { int z; z = x + y; return( z ); } // end addNotice that in the above function a variable z is declared. This is called a local variable and is only usable inside of the function. A local variable gives you temporary storage for intermediate values. Local variables get put on the stack for the life of the function.
Parameters passed to a function are actually local copies of the variable passed in. This means that any modification to the variable is lost.
void UselessFunction( int x ) { x++; }Incrementing x in the above function does not affect the variable which is passed in, only the local copy of x. Notice the return type of void in this example means that the function does not have to return a value.
So what happens if you want to modify a value that is passed in? This is where pointers become essential. Instead of passing in a variable, you can pass in a pointer to a variable. The pointer can be dereferenced giving the programmer access to the original variable.
void swap( int *x, int *y ) { int temp; temp = *x; *x = *y; *y = temp; }In this example a two pointers to integers are passed in. By using the indirection operator and dereferencing the pointers it is possible to switch the values in the two variables. Without pointers the actual original variables would not have been effected.
The C/C++ language has one special function called main which is what is called when the program is executed. The main function then can call other functions in order to build a larger program. The main function returns a value of int to the operating system to give it an exit code for your program. The following shows a fully working program which uses the swap function.
void swap( int *p1, int *p2 ); // this is the main function for the program int main() { int a, b; a = 3; b = 4; // pass the swap function pointers to our variables swap( &a, &b ); // when you get here, a = 4 and b = 3 ! return( 0 ); } // end main // the swap function takes to integers and swaps their contents void swap( int *p1, int *p2 ) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } // end swap
C/C++ is a library based language. The concepts presented so far cover most of the essentials of the C language. C is so extensive because anyone can write some functions and package them as a library for others to use. Many libraries come with each compiler giving the programmer a wide selection of tools. A header file is used, amongst other things, to prototype the functions in a library.
The keyword #include is used to read a file into the code being compiled. Header files typically end with the extension ".h", ".H", ".hpp", or ".hxx". To use one of the system libraries, simply include the protype functions from the library's corresponding header file. Specifying the name of a header file in angle brackets tells the compiler to use the system's header directory, whereas the name of a file in quotes tells the compiler to check the current working directory.
#include <stdio.h> // will be using the system standard i/o library #include "mylib.h" // include my own header fileThe header files for some useful libraries which you will come across are: