All (usefull) programming languages support the usage of variables. A variable is a name in the program that is assigned a value. A variable can have a numeric value, a symbolic value, or any other type of value your imagination can create. These types of values that a variable can contain are aptly named data types. Many data types are already defined for you, and these are the data types we are going to focus on in this chapter.
A practical use of a variable is to store a numeric value. It may be useful in our program to save the value of 256 into the variable 'x'. Since 256 is a integer value--that is, a non-decimal number--we must tell C++ that 'x' has the ability to store this integer value. The process of telling the compiler (the software that converts your program source into an up and running program) that a variable is of a data type is called a declaration, while the actual process of giving a value to that variable (of the data type it was declared in) is called a definition. So to use the variable 'x', we must first declare it as a data type, and then give it data.
The process of declaring and defining is accomplished quite easily in C++. In a declaration, the data type goes first, followed by the variable name. To define the variable, you can use the = assignment operator (to be discussed in greater detail later in this chapter) to give the variable its value. Consider the following code:
int x; x = 256;The
int
tells the compiler that the variable 'x' will be an integer value. In the second line, the variable 'x' is asigned the value 256, which is an integer value. If we assign a non-integer value to a variable declared as an integer, and the compiler can't make a conversion, then we get an error when we try to compile the program. Notice the ; semicolons after each line in the above code snippet. These tell the compiler that this is the end of the expression; we'll cover this more later in the chapter.
Variables can also be defined in the same line as they are declared.
int x = 256;The compiler first creates the integer variable 'x' and immediately assigns it the value 256. This code and the previous snippet produce identical results.
There are several built-in data types that you might want to memorize. Here's a table describing the most basic built-in data types.
Data type | Size | Range |
char | 8 bits | -128 to 127 |
int | 16 bits | -32,768 to 32,767 |
long | 32 bits | -2,147,483,648 to 2,147,483,647 |
float | 32 bits | 3.4 x 10-38 to 3.4 x 10+38 |
double | 64 bits | 1.7 x 10-308 to 1.7 x 10+308 |
A variable declared as type char contains a small integer value that represents a single ASCII character code. The ASCII character set is a standard listing of numbers representing the letters of the alphabet, numbers, symbols, and other basic keyboard operations. A single character can be represented by enclosing it in ' marks. Note the following two definitions
char letter = 'a'; int number = 'a';If we were to print both of these variables to the screen, 'letter' would print out as 'a', while 'number' would print out as 97. Why is this? The compiler keeps in mind the type of the variable when it is used, and a conversion from one type to another is performed if nessesary. The value 'a' is a char value, so things are fine when we assign 'a' to a variable of type char. But when we assign 'a' to a variable of type integer, an implicit typecast is performed. A typecast is a conversion from one type of variable to another compatable type. An implicit typecast is a conversion that is handled by the compiler, behind the scenes. When we try to assign 'a' to an integer value, the compiler converts 'a' to an integer value, which would be the ASCII character code of 'a' (97). It is generally not a good idea to take advantage of implicit typecasting because it can be unclear to the reader of your code what you are trying to do, and it can result in subtle bugs. If you declare a variable as an integer, its usually the best idea to just give it a straight integer value.
The float and double data types are used for non-integer values. You'll often hear these data types refered to as real or floating point types. Floating point types are handled by the 80x87 math coprocessor on intel processors that have this feature, which greatly increases the calculation speed. However, even with a math coprocessor, floating point calculations tend to be slow. Floating point numbers are usefull in applications where a precision less than 1 is required. The fractional value '1/2' is represented by the floating point '.5'. Typecasts from floating points to integer values and vice-versa are also possible, as in the following code:
float real = 342.82; int integer = real;If the variable 'integer' were to be printed after it is defined, its value would be 342. Notice that the integer is not rounded up to 343 in the implicit typecast--the fractional part of the variable 'real' is simply removed when the implicit typecast is called.
Variables are not a difficult concept to grasp, but understanding them are a fundamental step in C++ programming. If you keep in mind that each variable has a type, and the value of that variable is of that type, then the concept becomes more meaningful. Typecasting is not a complex matter if you think of what the new value would logically become when assigned a different type. However, it takes some degree of memorization to know what types are compatable with each other for typecasting and which are not.
Data is useless unless you can do something with it. Data is manipulated by symbols called operators in the C++ programming language. Since elementary school we have all been exposed to the basic mathematical operators:
+ addition - subtraction x multiplication / division = equationMuch like the common math set of operators, C++ has its own operators for the manipulation of data, and these operators are not only limited to numerical values. Operators are commonly used in mathematical calculations and in allocating or addressing a variable. They can also be used for logical comparison, or even in manipulating the bits of a variable. For instance, the + addition operator is used for adding two numerical values together, the & operator is used for taking the address of a variable, and the == operator is used for equality comparison between two statements (that will sound like english by the end of this section). Lets try some basics:
int x = 5; int y = 20; int a = x + 2; // addition int b = y / x; // division int c = y * b; // multiplication int d = -y; // negationThe '//' marks declare a comment; everything after the '//' marks are comments about the program, and in no way effect the outcome of the program. Its fairly clear what we're doing here. An operation is performed and assigned to a variable we are initializing. For instance, in the addition example we add x (which has a value of 5) and 2, and give it to the integer 'a', which is being initialized. So 'a' has a starting value of 7. Is pretty simple really.
Operators can get fairly complex because they must be memorized, and often programmers use them in ways that can be very confusing. The basic operators are not difficult to learn because we have used them all our lives, in mathematics and possibly other programming languages. The basic operators are self-explanatory, but confusion can easily arise when the more advanced operators are presented. So we'll just take it slowly through this section. Take care not to read too fast, and do not try to clutter your mind with memorizing all of the operators now and the properties of those operators. You're not expected to know everything from day one.
Operators can be classified into two groups: unary and binary. A unary operator works on one value, doing something to that value and changing it. An example of a unary operator is the negation operator, used in the above code snippet assigned to the integer 'd'. The negation operator is placed before a variable, and it returns the oposite sign of the variable. So if we prefixed a variable 'x' containing a value of -5 with the negation operator, the result would be 5. A binary operator requires two values to work with. There are more binary operators than unary operators. Examples of binary operators include + addition, - subtraction, * multiplication, / division, and % modulus.
When an operation is performed, where does the result go? An expression where an assignment is involved (using the = assignment operator) requires an LValue. An LValue ("left-value") is where the result of an expression is returned. For example:
int x; x = 4 + 5 - 1;The LValue in this case is 'x', and after the expression is executed in the program, 'x' contains a value of 8. An LValue must be a name of a variable that we can assign a value to (yes, there are variables that we cannot reassign a value to, and we'll cover those later). So it works like this: the right hand side of the = is evaluated first, and the result from that is assigned to whatever is on the left hand side of the =. Its simple logic, really.
Lets try some more operators. There are more ways to assign a value to an LValue than just using the = assignment operator. Say we want to increase the value of a variable by 5. We can do this the "old-fashioned" way using the = assignment and + addition operators like this:
int x = 10; x = x + 5;Or we can use C++'s addition assignment operator, +=.
int x = 10; x += 5;This makes the code somewhat more readable and concise, which is very desireable in programming. The following are the commonly used assignment combo operators we can use
+= addition assignment -= subtraction assignment *= multiplication assignment /= division assignment %= modulus assignment (remainder from division)These are all unary operators because they only require one operand.
Computers only work with 1s and 0s, as you may be aware. Every number can be represented as a binary number, which are the set of 1s and 0s that computers deal with. We have been using decimal notation, or base 10, since first grade math. The number 439 in decimal can be represented by the sum of each digit being multiplied by a factor of 10 increasing as the digits increase.
439 = 4*10*10 + 3*10 + 9 or 439 = 4*10^2 + 3*10^1 + 9*10^0Where '*' represents multiplication and '^' represents exponentiation. Since we're working in base 10, we use 10 as the factor to multiply each didgit by. Now imagine if we used a different number in 10's place, like 2 for our purposes. To represent the number 52 in base 2 we would have to do some more work:
52 = 32 + 16 + 4 or 52 = 0*64 + 1*32 + 1*16 + 0*8 + 1*4 + 0*2 + 0*1 or 52 = 0*2^6 + 1*2^5 + 1*2^4 + 0*2^3 + 1*2^2 + 0*2^1 + 0*2^0 so 52 base 10 = 0110100 base 2Notice how we can represent 52 with a string of 1s and 0s. The pattern isn't too difficult to spot if you look at the first number of each term and notice that it is either a 1 or a 0, and then compare it to the binary string in the last line. They're the same! So now you know what all those 1s and 0s mean and how computers translate them into something meaningful.
So how does this apply to this section on operators? Well, several of the operators work on the binary level, manipulating the individual bits (1s and 0s) of a number. Operators that have the purpose of controlling the individual bits of a variable are called bitwise operators. There are six bitwise operators:
Operator | Meaning | Translation |
& | and | if both are 1 then result is 1 |
| | or | if either is 1 then result is 1 |
^ | xor | if either and only either is 1 then result is 1 |
~ | not | result is oposite of operand |
>> | right shift | result is operand's bits shifted to the right |
<< | left shift | result is operand's bits shifted to the left |
Lets try a few examples of each of the bitwise operators, with a bit of discussion on each:
and: With bitwise operators we are comparing one variable's bits with another variable's bits, and depending on the state of those bits (1 or 0) we obtain a result. So say we have the following expression demonstrating the use of the bitwise and operator (&).
int x = 30; int y = 19; int z = x & y;The variable 'z' is obtained by anding togther the binary values of 'x' and 'y'. We usually set up a binary operation with one operand per line and the result on the bottom line.
x = 30 011110 y = 19 010011 ------ z = x & y 010010 z = 18We compare each bit of 'x' with each bit of 'y'. Start with the right mostdigit. For 'x', the rightmost digit is 0, and for 'y' it is 1. With a bitwise and, we are comparing the bits to see if they are both 1, and if they are, the result is 1. Since both of the rightmost digits are not 1, the result is 0. In the second digit, both bits are 1, so the result is 1.
or: The principle behind the 'or' operator (|) is that a result of 1 is returned if either of the operands' bits are 1. So if we had the following expression:
int x = 74; int y = 211; int z = x | y;And we set up a bitwise expression in the same manner as we did the 'and' operator.
x = 74 001001010 y = 211 011010011 --------- z = x | y 011011011 z = 219Starting from the first bit (rightmost), we test if either of the bits are set (1). Since y's first bit is set, the result is set. In the second bit, both bits are set, so the result is set. In the third bit, neither bits are set, so the result is clear (0). When we 'or' two values, the result will always be greater than or equal to all of the operands, and when we 'and' two values the result will be less than or equal to all of the operands. This fact is used a lot where flags are involved--usually in low level hardware programming or windows programming. You can test if a bit is set by anding a mask to the value you are testing with each bit cleared (set to 0) except the bit you are testing. If the resultant value is non-zero then we know the bit is set. We can use the 'or' operator when combining flags. We'll be using these techniques later when we get into hardware and windows programming with C++.
xor: the exclusive or operator is used to determine if and only if either of the operands' bits are set, and if so, the result is set. If both of the bits are set, then the result is cleared. It works like the 'or' operator except that if the operands' bits are the same, the result is cleared. Lets try an example:
int x = 538; int y = 12; int z = x ^ y;And like we've done before, set up a binary expression:
x = 538 01000011010 y = 122 00001111010 ----------- z = x ^ y 01001100000 z = 608There are many uses for this, but its pretty dependant upon what you're doing. There's not really any general practical use for the 'xor' operator like there is for the 'or' and 'and' operators (described above). One general but not-so-useful use of the 'xor' operator is the fact that if you xor two variables of the same value, the result will be 0.
not: the bitwise not operator is the only purely unary bitwise operator. Recall that a unary operator requires only one operand. All the not operator does is simply reverses the operand's bits and stores them in the result. So if we had the following code:
int x = 485; int z = ~x;We could set up the following binary expression:
x = 485 0111100101 ---------- z = ~x ...1000011010 z = -486Does this make any sense? Why is 'z' -486 and not 26? Notice that we could write x as '...000000000111100101' with infinite 0s prefixed to the value. Then if we take the bitwise not of that value, every prefixed 0 would become a 1, resulting in a huge number. The PC processor cannot handle numbers this big, so when it reaches a number that is too large, it becomes a negative value. This is called an overflow error, and most compilers will give you a compile-time error for this. This fact makes the bitwise not operator a difficult operator to control, and its not a terribly useful operator in the first place (meaning that you hardly ever use it). Some uses for it might be to get the inverse of a pixel on the screen when dealing with graphics, or in an encryption program.
right shift and left shift: we're going to cover both of these operators in one para-section because once you know one, the other is self-explanatory. What these operators do is shift the bits a certain number to either the left or the right, depending obviously on which operator you use. These operators are binary operators, with the value you are changing on the left side of the operator and the number of bits you want to shift it as the right operand. I think an example like we've done above would make this most clear:
int x = 37; int y = 82; int z1 = x >> 1; int z2 = y << 3;'z1' will get its value from shifting x's bits right 1 slot, and 'z2' likewise by shifting y's bits left 3 slots.
x = 37 0100101 ------- z1 = x >> 1 0010010 z1 = 18 y = 82 00001010010 ----------- z2 = y << 3 01010010000 z2 = 656Its pretty simple actually. 'x' is shifted to the right 1 digit for a result of 18 decimal. 'y' is shifted to the left three digits for a result of 656. When shifting to the left, the first digit becomes a zero. If you go out of the range of the variable, which is 32,767 for an integer, then you'll get the weird overflow condition where the value becomes negative.
One last note on bitwise operators. Just as you can combine the basic operators with the assignment operator to form a unary operator that performs the calculation and assigns it to the LValue, you can combine the bitwise operators as well.
&= and assignment |= or assignment ^= xor assignment >>= right shift assignment <<= left shift assignmentPretty self-explanatory. Notice that there is not 'not' assignment operator because 'not' is a unary operator to begin with, and it wouldn't make much sense when you think about it.
And that pretty much covers it for the bitwise operators. They might not seem all that useful at first, but they come into play where you'd least expect it. Like I said before, they're used a lot in low level hardware programming and in programming windows applications. The register flags on hardware devices must be checked and set using these bitwise operators, and likewise in Windows programming for setting and checking event flags. There are many features in C++ that you'll be wondering what you can apply them to, and you might be compelled to skim over the descriptions of these features because you don't think they apply to you, but these features can pop up suddenly in a piece of code you may have to read or even write. Being prepared for the unexpected is the best thing to do in programming: learn things that you might not see a use for simply because they are part of the programming language. Guaranteed they will appear as you continue programming.
C++ provides a simple way of incrementing and decrementing (increasing and decreasing the value by 1, respectively) variables and with greater versatility than the previously discussed addition/subtraction assignment combonation operators. These are the aptly named incrementation and decrementation operators. Using the incrementation operator, we can write expressions such as the following:
int x = 5; x++;If 'x' were to be printed after the preceding code were executed, the output would be '6'. Ever wonder where the name C++ came from? Now you know. The decrementation operator works the same.
int x = 5; x--;'x' has a value of 4 upon completion. But its not quite that simple--there had to be something else to this to make it pointful. There prefix and postfix versions of these operators, meaning that you can either put the operator before or after the variable you are manipulating. The difference is that the prefix operator calculates the new value before the expression is complete, and the postfix calculates after the expression's completion. When the operator comes after the variable (like in the above snippets) the variable is not incremented or decremented until the semicolon is reached. Maybe an example would help:
int a = 5; int b = 5; int x = ++a; int y = b++;'a' and 'b' are both initialized with the same values. We use the prefix incrementation operator when initializing 'x', so a's value will be increased immediately and assigned to 'x'. When 'y' is initialized, 'b' is assigned to 'y' and then 'b' is incremented since we're using the postfix version. So if we were to print all the values out after this program is complete, 'a' and 'b' would be 6 since they were both incremented; 'x' would be 6, and 'y' would be 5. However, to avoid confusion, it is better to use the incrementation and decrementation operators only on lines by themselves instead of in expressions as shown above.
One last group of operators we need to touch on: the logical operators. These will come into heavy use in the next section on C++ constructs. Logical operators are used for comparision, and are a lot like the bitwise operators. With logical operators we are comparing two expressions (since logical operators are binary) which will evaluate to either true or false. The following are examples of logical expressions:
Bob has a tie Sally has a bouquet Frank has a Harley DavidsonUsing logical operators we can combine these expressions into one logical expression:
Bob has a tie and Sally has a bouquet or Frank has a Harley DavidsonAlthough the meaning of that is fairly unclear, we can make this unambiguous in programming. Say we can represent these three logical expressions with the following three variables:
int BobTie; int SallyBouquet; int FrankHarley;If 'BobTie' is 0 then Bob doesn't have a tie, and if it is non-zero then he does, and likewise for the other two expressions. Therefore, we can represent our big logical expression with the following line of psuedo-code:
BobTie && SallyBouquet || FrankHarley;As you may have guessed, '&&' is the logical and operator and '||' is the logical or operator. The meaning of this is unclear, however, so we should use parenthases to signify what subexpression should be calculated first. Use one of the following:
(BobTie && SallyBouquet) || FrankHarley; // expression 1 BobTie && (SallyBouquet || FrankHarley); // expression 2For expression 1 to evaluate to true, both 'BobTie' and 'SallyBouquet' would have to be true, or just FrankHarley would have to be true. For expression 2 to evaluate to true, 'BobTie' would have to evaluate to true, and either 'SallyBouquet' or 'FrankHarley' would. All it takes is some common logical sense. We'll use logical operators more in the next section and they will become more clear to you, as if they aren't clear already.
There are other logical comparison operators used in testing expressions in addition to 'and' and 'or'. These test for a greator, lesser, equal, or not-equal value. You may remember these kinds of operators from algebra, although you probably used slightly different symbols. Here's a table of all the logical comparison operators (two of which we've already covered).
Operator | Meaning | Translation |
&& | Logical and | If both expression true, result is true |
|| | Logical or | If either expression true, result is true |
== | Equality | If expressions are equal, result is true |
> | Greater-than | If exp1 is greater than exp2, result is true |
< | Less-than | If exp2 is less than exp2, result is true |
>= | Greater-than or eqaul to | If exp2 is greater than or equal to exp2, result is true |
<= | Less-than or equal to | If exp2 is less than or equal to exp2 result, is true |
!= | Not equal to | If expressions are not equal, result is false |
These are pretty self explanatory--they all involve two expressions, and depending on what operator is used and what the conditions evaluate to, the result may be either true or false. For instance, if we have two equal expressions compared with the == equality operator, the result is true. If the expressions differ in any way, the result is false.
There is also a special case in which the not equal to operator can be used by itself as a unary operator. If we prefix a variable with the ! operator, this is the same as testing the variable for a zero (false) value. For instance, if the value of a variable called 'MyVal' is known to be false, the expression '!MyVal' will evaluate to true. It is the same as comparing 'MyVal' to 0 with the == equality operator or to a non-zero value with the != operator. We'll use these more in the next section.
Well we've covered all the major operators you need to get started with C++. Of course, I haven't introduced all of the operators in this section because it would be difficult to explain "pointer dereferencement" and "variable addressing" in a section purely on operators. Later--don't worry.
You know how to create variables and how to change the values of those variables a little, but this does not suffice when trying to programming a major application (trivial statement of the century). This section will introduce some basic elements of C++ which are found in all other programming languages.
The first construct we will cover is conditional statements. A conditional statement is an expression that has a result of either true or false, and code is executed depending on the result. We use conditional statements in English. Consider the following
1. If Joe has a truck then buy Joe some pliers. 2. If Rachel has a house then go to Rachel's house, if not, go to Rachel's parents' house instead. 3. If QP7 is going to Denver University and Beowulf is going to Denver University, Then buy QP7 and Beowulf a TV/VCR combo; if not, then figure something else out instead.Conditional statements always have a test-clause and a result-clause. In statement #1, the test-clause is "If Joe has a truck" and the result-clause is "buy Joe some pliers". There must be something to test, and then there must be a result. Conditional statements can also have an else-clause, which is performed if the test-clause is false. In statement #2, "If Rachel has a house" is the test-clause, "go to Rachel's house" is the result-clause (which is performed if the test-clause is true), and "go to Rache'ls parents' house" is the else-clause (which is performed if the test-clause is false). We can also use boolean expressions such as 'and' and 'or' in the test clause as in statement #3. The test-clause is not true unless all of the conditions have been meet using the 'and' expression, or any of the conditions have been meet using the 'or' expression.
In C++, we use the if statement for the test-clause and the else statement for the else-clause. The syntax looks like this:
if(test-clause) result-clause
else else-clause
We will usually use the logical comparison operators in the test-clause. Just remember that if the test-clause evaluates to true (or non-zero) then the result-clause is executed, and if the test-clause evealuates to false (or zero) then the else-clause is executed if one is present. Example:
int x = 10; int y = 11; int result; if(x == y) result=1; else result=0;Since 'x' does not equal 'y', 'result' will be 0 when we finish the program. We can also use the logical 'and' and 'or' operators in the test-clause.
char bob='b'; int lowercase_letter; if( (bob >= 'a') && (bob <= 'z') ) lowercase_letter=1; else lowercase_letter=0The variable 'bob' is initialized with the letter 'b', and the variable 'lowercase_letter' will be used to determine if 'bob' contains a lowercase letter value. The conditional expression does exactly this. To meet the criteria for being a lowercase letter, the value of 'bob' must be between 'a' and 'z'. We could rephrase this by saying that "'bob' must be greater than or equal to 'a' and 'bob' must be less than or equal to 'z'". If the test-clause evaluates to true, lowercase_letter is set to 1 (true), otherwise its set to 0 (false). Trivial enough? I think so--but it demonstrates the point.
If a variable may be one of several values, and we want to do something different for each of several values that that variable might be, then we can use a different kind of conditional statement called the switch expression. The switch expression calls for the name of the variable we are testing followed by several case statements. The syntax looks like this:
switch(variable-name) {
case value1: expression1 break;
case value2: expression2 break;
case value3: expression3 break;
...
default: else-clause break;
}
We're testing a variable against several values, or cases, as they are called here. We test a value against the variable by supplying one after the case statement. We follow the value with a colon, and give the code which is executed in result to that case being true. We don't use {} braces for the executed code, but instead use the break statement after the expression is executed. If we don't include the break statement, the next case is executed, which is usually undesirable and can have distasterous results. The default case is like the else statement in an if expression. The else-clause is executed if none of the cases are met. Lets try an example to see if this makes anything more clear.
int GPA = 3; char grade; switch(GPA) { case 4: grade = 'A'; break; case 3: grade = 'B'; break; case 2: grade = 'C'; break; case 1: grade = 'D'; break; case 0: grade = 'F'; break; default: grade = '?'; break; }Since we defined 'GPA' to be 3 when it was initialized, 'grade' will be 'B' after the pseudo-code is "executed". If 'GPA' was not either 4, 3, 2, 1, or 0, then 'grade' would have been '?'. switch expressions are very useful in C++, and much easier than writing out several if expressions with a test-clause and result-clause for each one.
A fundamental construct of all programming languages is the loop. A loop defines a section of code that is executed over and over again until certian expressions are met. There are two kinds of loops in C++: condtional and for loops. for loops are really a type of conditional loop, but since it uses a different syntax.
for loops have three sections in which it is declared:
1. The variable(s) which will be used in the loop 2. The condition for while that must be met for the loop to continue looping 3. The way the variable(s) change on each cycle through the loop.An example would best explain this process.
int i; int val = 0; for( i=0; i<=100; i++ ) { val += 5; }We start out by initializing two variables: 'i', which will be used for the index in the for loop, and 'val', which will contain the value we'll be changing in the for loop. The three sections of the for loop are seperated by ; semicolons within () parenthases. The initial value of 'i' is 0 in the for loop, and the loop cycles while 'i' is less than or equal to 100. Each cycle through the loop, 'i' is incremented--so the loop cycles 100 times. The loop code increases the value of the variable 'val' by 5, so we're doing something. When the program is complete, 'i' will have a value of 100, and 'val' will have a value of 500. If we wanted to count how many letters are in the alphabet, we could use the following code:
char c; int numletters = 0; for( c='A'; c<='Z'; c++ ) { numletters++; }I think we all know what the value of 'numletters' will be upon completion, assuming you have had sufficient exposure to our alphabet, that is.
The for loop is based on an index variable paired with a while statement usually based on that index variable. C++ also provides functionality for a loop that is based only on a conditional statement. There are two types of loops that fall under this category: the while loop and the do...while loop. A while loop tests a conditional statement, and if this conditional statement is true, then it runs the loop until it is false. A do...while loop is very similar, except that it runs the loop, and then tests the conditional statement. If the statement is true, then it runs the loop again. The difference between a while and a do...while loop is that a do...while loop always runs the loop at least once.
The syntax for the while loop is as follows:
while (conditional-statment) {
...
loop body
...
}
The syntax for the do...while loop is quite similar.
do {
...
loop body
...
} while (conditional-statement);
It would be hard to present a practical example for these types of loops without first presenting methods of interfacing with the user, but we will cover this in later sections.
Functions represent the flow of the C++ program. Imagine a function as a task that you have to do, whether it be cleaning the house, emailing a friend, or doing your homework. As you go about your life, you decide you want to perform a task, and you follow the procedure in your head to complete the task. Functions in C++ consist of code that defines a task, and you can use this code over and over by calling the function. For instance, say we have the above three tasks in the form of C++ functions: clean_house, email_friend, and do_homework. These functions would be "called" from the main program, thier code would be executed, and the flow would return to the main program. The main program, however, is a function itself, called the main procedure. The program always starts its execution with the main procedure and ends it's execution when the main procedure ends. Other functions are called from the main procedure, and eventually return back to where they were called. Functions are called from within other functions, and when they are done, they return to the function that called it. Let's demonstrate this process using some pseudo-code:
// clean_house function definiton clean_house { disponse_of_junk dust_wood_furniture vacuume } // email_friend function definition email_friend { open_email_program type_email send } // do_homework function definition do_homework { do_calculus do_physics do_engineering do_shakespeare } // main procedure function definiton main function { // ! start of program call clean_house call email_friend call do_homework // ! end of program }
Our program starts with the main function (the main procedure). From within the main procedure, the clean_house function is called, which picks up the junk, dusts the wooden furniture, and vacuumes. After the clean_house function is called, the program returns to the main procedure. The email_friend and do_homework functions are called from the main procedure likewise. This is basically how the flow of a normal program goes.
In a C++ program, functions must be declared before they are called. You can define them before the main procedure (where the program starts and ends), and then you can call them in the main procedure, but this is sometimes hard to read. This is the method I used in the above pseudo-code program. A clearer way to write a program is to have the function declarations, also called the function prototypes above the main procedure, and put the function definitions below the main procedure. It is very important that you now understand the difference between a function declaration and a function definition, because we use a slightly different syntax for the two. Let's start out with the syntax of a function declaration (or a function protype) and an example.
return-type function-name (parameter-type parameter-name, ...);
Say we want to make a function that adds two numbers together and returns the sum. There are a couple things you should know first. Functions, besides transferring program flow to another section of the program, can have parameters and a return value. When you call a function, you can call it with no, one, or multiple values, which are "passed" to the function. These values become variables in the function, which you can perform opperations on. Sometimes it is useful for a function to return a value to where it was called from. So say we want to write a function called add(), which accepts two integer parameters (values to be added to each other) and returns an integer (the sum of the two values). We would declare the function like this:int add(int opperand1, int opperand2);
A few things to note about this. The function returns an integer to where it was called from, and expects two parameters, which are both integers. When the program calls the add() function, the two numbers that are going to be added are passed to the function, and their sum is returned.
We have declared this function, but we have not yet defined it. This means that the program knows that there is an add() function, but it doesn't know what it does because we haven't told it. If we tried to call the add() function without definining it somewhere in the program, we would not be able to compile the program. So what we need to do next is define the function, which is not too hard to do.
int add(int opperand1, int opperand2) { int sum = opperand1 + opperand2; return sum; }
When we pass values to the function, the variables in the parameter list of the function become local variables in the function definition. The paradigm in this add() function is to create a local variable called sum, and assign to it the sum of the opperand1 and opperand2 (which are passed to the function). Then we return sum using the return statement. Let's look at the whole program to demonstrate where everything in placed and how the sum is returned to the calling function.
int add(int opperand1, int opperand2); void main() { int total; total = add(10, 20); } int add(int opperand1, int opperand2) { int sum = opperand1 + opperand2; return sum; }
The first line in the code example is the prototype (the declaration) for the add() function. This tells the program that an add function exists and how the program will call it, but not what it does. We have to write the function prototype before it is called.
After the add() prototype comes the main procedure. The void before the main() is the return type, which is nothing. Putting void for a return type tells the program that the function does not return a value. The () after 'main' tells the program that the main procedure does not excpet any parameters. Recall that the main procedure is where program execution begins and ends. A couple questions are probably in your mind: How can the main procedure return a value?, and how can the main procedure have parameters? Think of it this way: when you run your program, the opperating system (dos, windows, unix, os/2, etc) calls the main procedure. So the main procedure actually can return a value, and it is returned to the opperating system. The opperating system does not usually do anything with this returned value, but for debugging purposes it can indicate an error status. The main procedure can also accept parameters, and these come from the opperating system also. For many programs that you have probably seen, you can have a parameter list (also known as an argument list) for the program. For example, if you want to run Microsoft Word with the name of a Word document after the name of the program as follows:
C:\>WORD QUIZ.DOC
Then Quiz.Doc is opened up in Word. In Windows95, you can drag the icon for a document on to the Microsoft Word icon, and Word will open up with that program. The name of the program is an parameter to the main procedure in the code for Microsoft word! We will get into how the main function actually deals with these arguments later in this tutorial.
Back to our code example. In the main procedure an integer called 'total' is created. in this example we are using the add() function to compute the sum of 10 and 20, and then assign the sum to 'total'. When the program comes across the line
total = add(10, 20);It executes the add function with the paramters 10 and 20, and the add function returns the sum of these two numbers, so the function call in a sense simplifies to the return value.
That's really all there is to say about functions: functions are just a block of code that perform some task and return a value to the place where it was called. I'll be using functions extensively for the remainder of this tutorial, so it is essential that you understand them now.
Up to this point if you have tried to most of the programs they have failed. With all compilers, you write C++ code in a source file (usually a .cpp or .cp file), tell the program to compile it, and an executable will be generated (an .exe or .com on the PC platform). There is more than just typing in the code--it has to be in a format that the compiler can recognize. Here is a list of guidelines that I will make reference to throughout this section and that you should keep in mind for the rest of the tutorial.
void FunctionOne() { int x; } void FunctionTwo() { int y; }The variable x is not visible in FunctionTwo, and likewise the variable y is not visible in FunctionOne. This means that if we try to define x in FunctionTwo (or y in FunctionOne) or refer to it in any way, we will get an error when we try to compile the code. The variables within the function have a block (or local scope). They cannot be accessed from anywhere else in the program except for within the functions they are declared in.
The Class scope will be covered in the next chapters when we cover classes in detail. If a variable is declared outside of any function, it is said to have a File scope, meaning that it can be accessed from anywhere in the file. So you wonder, if we can create variables that can be accessed from anywhere in the file so easily, why not just declare all of our variables at the top of the program and not worry about scope? This is a bad idea for three reasons that scream out immediately from any programmer's mind. One, you can only use each variable name once, so eventually you will have to do what is called name mangling, meaning that you put a number or a letter at the end of a variable name to distinguish it from another (like counter1, counter2, name_a, name_b, etc). If you declare variables mostly on the block scope, then you can use the same variable name in different block scopes and not have to worry about masking the value of a variable in a higher scope (more on this shortly). Two, variables are organized by scope to give the reader of your code a hint as to where it is used. If you've got a function that has a for loop in it, it would be much better to define that variable at the block scope to tell the reader that it is going to be used in the block scope. Three, it's a waste of memory to declare all variables on the global scope because all global variables must be loaded up when the program loads up. When a variable goes out of scope, the memory it used is freed up so the rest of the program can use it. For instance, an integer variable consumes two bytes of RAM. If we know that we're going to have to use, say, fifty integers in a given program, and we declare them all globally, then the program has to reserve 100 bytes of RAM when it starts up. On the other hand, say we have five procedures and we can put ten of the integers we need in each of these procedures. Then the program would only require 20 bytes of RAM while it is running! More available RAM means better performance! Of course, this is an over simplification, and if we're only using integers in a program then we won't have to worry too much about conserving memory, but if we're writing a big program will gadzillions of variables, we'll want to place them in the block scopes that they are being used in. There is a time when we'll need to use global variables, but not all the time.
Finally, variables can be declared on the global scope, meaning that they can be accessed from anywhere in the program. The difference between file and global scope is that you can only access variables declared at the file scope from within that source code file (a source code file usually has the extension .cpp or .cp). Since a program may have several .cpp or .cp files, it may be beneficial to declare a variable as global to all source files. You do this using the extern keyword preceeding the variable declaration, which just tells the compiler, "Hey! This variable is declared here, but defined in another file, so it can be used in any file." Here's an example:
extern int x; // global variable extern double hi(); // global functionThen you could define and use them in another file as you wish. I had a problem with this when I was learning C++, so I'll take it easy on you and explain code when it gets complicated.
2. Preprocessor directives are placed at the top of the file before any user code. In C++, you don't have to write all the code. That's right--the hard workers at Microsoft, Borland, Watcom, Symantec (or whoever you bought your compiler from) have already written what is known as the standard C++ libraries and put them on your computer when you installed your compiler. All programming languages provide features for printing text to the screen, writing to files, and most provide functionality for graphics programming, databases, and windows/mac OS programming. The only way you can do anything with C++ is if you use C++ libraries, which come with your compiler and which you can also make when you get better at the language. A library is defined as a set of commands that have a common purpose. For instance, the C++ I/O Stream library provides commands for printing text to the screen, writing to files, and printing to the printer. The C++ String library provides string manipulation functions; Console I/O provides functions for manipulating a DOS-style console, and the Windows library provides (many) functions for writing windows program (API calls--for your Windows programmers). Every library has what is called a header file (some have several) in which all the declarations for the classes and functions that are used in the library are contained. Note that you usually don't get the source code for a library--you just get the header file. To use a library in your program, you simply #include the header file in the source file (.cpp or .c) you want to use it in. You do this using the #include preprocessor directive. A preprocessor directive is a command that is executed first in compilation, and do special things to the program such as defining global symbols that set flags, include header files, disable warnings, and provide special if...then statements that can take out blocks of code before the source code is even compiled! All preprocessor directives start with a #. For now all you need to be concerned with is #include, which we'll take a look at right now.
Say we want to print some text on the screen. Rather than writing assembly code to load the address of a string into the DX register, loading function 09h into the AH register and calling interrupt 21h, we'll just let the C++ I/O stream libraries do this work for us.
#include < iostream.h > main() { cout << "Hello World!" << endl; return 0; }The first line is our preprocessor directive that #includes the C++ I/O Stream library. The cout statement seen in the code comes from iostream.h, and if we didn't #include iostream.h then the compiler wouldn't recognize cout. The first line in the main procedure writes the string "Hello World!" to the console. The << marks are called the inserter opperator (note: you may recall from section 2 that << is also known as the left-shift opperator. This opperator has a different use when it is used in the context of I/O Streams), and insert data into the cout symbol. This is one way of printing to the console in C++. Note that the stuff to the right of the << does not have to be text, it can be a variable, symbol, or anything that has the << opperator defined for it in the context of I/O streams. The endl that is inserted into cout after the text message stands for "end line", and puts a carriage return after the text (a carriage return is like pushing the enter key--it advances the text cursor to the next line so next time we print it will be underneath the "Hello World!" text). Note that we could have put a \n after the text (within the quotes) which does the same thing under the DOS platform, but I chose to use the C++ symbol endl instead of the old C way.
A stream is a place where data can be sent to or retrieved from, and represents the flow of data from one physical part of the computer to another. Some examples of streams are the printer, the console (screen), the auxillary port, the keyboard, and an error handler. You write to a stream by using the << opperator, and read from a stream using the >> opperator. You'll see plenty of uses for this in the next chapter where I cover programming techniques in depth.
At any rate, most preprocessor directives should come at the top of the file, and then comes the executable code.
3. Header files should contain no executable code (just declarations). We talked about using existing libraries by including their header files in part 2, but you can also create your own header files where you keep commonly used declarations. For future reference, keep in mind that header files should contain no executable code (or else they cannot be precompiled), so the only thing that should be written in header files are declarations and classes. You'll see the full power of writing your own header files in the next two chapters when you learn how to design projects, classes, and libraries.
There are a lot of compilers out there, and nobody wants to read pages upon pages of compiler specific information on an internet tutorial. One person reading this tutorial may be using Borland C++ 4.5, while another may be using Microsoft Visual C++ 4.0, while yet another may be using Symantec C++ for the Macintosh. To describe in detail how to opperate a program that many readers may not have would be pointless and a waste of many people's time. It isn't an easy task to learn how to create projects, how to manage these projects, debug them, and compile them, however. I am going to leave it up to you, the programmer, to learn how to use the compiler you bought. It undoubtedly has pages of reference on how to compile your programs and group source files together in a project. My job is to teach you how to program software, not how to use it. I propose that, if I have enough time, I will write an appendix to this tutorial that covers all such compiler-specific information.