We will begin the analysis with a look at the various
functions, starting at the top and working our way down.
#include "mathtest.h" // include our special header file
I hope you noticed this line at the top of the file under the other
#include directive. Up to this point, we have not used our own include
directives, but we will begin to do so here.
The include directive simply includes the contents of another file into
this file before it is compiled. This is done by the preprocessor.
Header files which have definitions, prototypes, and other useful
information (like #define directions among other things). The tigcclib.h
header file for example includes all the other header files, so we don't
have to do this manually. This is not a good idea when you start making
real programs, but it is acceptable for now.
There are two ways to include a file, the local include, and the global
include. Local include directives are specified by enclosing the name of
the file in quote marks. This specifies a local header file, which will
be in the same directory as the other C Source Files. Global includes
are for environment specific includes (like the tigcclib.h header file).
Global include files are stored in a special directory you don't need to
worry about. All you need to know about it is that there are special
header files kept there which store the TIGCC library headers. These
headers contain the functions used by the AMS. The special headers also
include information relative to the TIGCC library. The syntax for a
global include is as follows: #include <filename>, where filename
is the name of the header to include. As you have seen in the programs
thus far, the only header we have included is the tigcclib.h file.
When you use local header files, like the one we have defined, you
always use the local include directive. When you are using the header
files from the TIGCC library, you always use the global include
directive. This is another one of the many quirks of C. This is all you
really need to know about header files.
// return double the value of the argument
int times2(int number) {
return (number * 2);
}
This function should be relatively self-explanatory. The only new
concept is the concept of using a function. This function takes a single
argument, an integer, and returns the value of the integer multiplied by
two. We also see a new keyword, the return keyword. The return keyword
does two things. First, the return keyword tells C to exit the function.
Second, the return keyword sends the value of the argument back to the
function that called this particular function. When this happens, we can
intercept this value and assign it to a variable. You have seen this
demonstrated when we use the ngetchx() function. We will also see this
demonstrated again in the _main() function. The last thing which we
should notice about this function is that it has the basic three pieces
of any function declaration: the return type/value, the function name,
and the function arguments. These will be present on every function you
ever declare, so it is important that you notice them now.
// returns the square of the number given
int square(int number) {
return (number * number);
}
As this function is very similar to the previous one, we will not spend
much time on it. The only important concept to reiterate here, is that
this function declaration contains the three basic pieces of any
function signature. With what we have discussed above, you should
understand his function completely.
// returns the base number raised to the power of the exponent
long int power(int base, int exponent) {
int loop;
long int bigNumber = base;
// since exponentials are just multiplication the number of
// the exponent, loop and multiply
for (loop = 1; loop < exponent; loop++) {
bigNumber*=base;
}
// return this big number
return bigNumber;
}
The power() function, in terms of style alone is very similar to the
functions we have discussed already. It is simply more complicated.
There are however no new concepts introduced within the body of this
function. The power() function, simply calculates the value of an
exponent. We raise the base number to the power of the exponent. To do
this, we take the value of the base number and multiply that number by
itself in a loop until we reach the value of the exponent. However, we
do not start the numbering and zero, because we already have the base
number which is equal to the value of the base number raised to the
exponent power 1.
// return the sum of the numbers from start to end
int sum(int start, int end) {
int loop, sum = 0;
// keep track of the sun
for (loop = start; loop <= end; loop++) {
sum+=loop;
}
// return the calculated sum
return sum;
}
This function, being based on simple mathematical principles, should
also be very easy for you to understand. This summation function simply
adds the numbers between start and end together and returns the value of
that summation. This is equivalent to the Sigma summation function (i,
i, start, end), where the first i represents the expression, and the
second i represents the variable needed for the TI calculator to
correctly process this function. After the body of the loop is finished,
the function returns the value calculated by this for loop. I will now
once again reiterate that this function declaration also has the three
basic elements present in every function signature: the return type, the
function name, and the function arguments. This is all you should have
to know about this sum() function.
// return a number between 0 and 9 based on user input
short int getNumber(void) {
short int key = 0;
// wait for valid input
while (key < '0' || key >'9') {
key = ngetchx();
}
// calculate the number 0 as the value of keycode minus
// the value of the keycode '0'
key -= '0';
// return the corrected number
return key;
}
Here we see another relatively simple function. Again you should notice
that it has all three pieces associated with a function signature. Past
that, it is relatively straightforward. However, there is one line in
particular that you should pay attention to:
// calculate the number 0 as the value of keycode minus
// the value of the keycode '0'
key -= '0';
You will notice that we are looking for characters, but we actually want
numeric input. However, the number 0 and the character '0' are not the same,
keycode-wise. The character '0' actually has a keycode of 30. When we
want to find the integer value for a number's keycode, all we need to do
is subtract the lowest number's keycode from this value. In other words,
key -= '0' is the same thing as keycode -= 30;. The TI calculators share
most keycode values with a normal ASCII keyboard. So C can find the
integer value of '0' and replaces it automatically before it gets
compiled. So, when the user presses the '0' key, the value of key is
assigned the keycode of the character '0', which is not the integer
value 0. So, to find this value, we simply subtract the character value
'0' from the keycode value. This gives us the adjusted value as an
integer.
// pause the program
inline void pause(void) {
printf("Press any key...");
ngetchx();
}
The final function we define is a bit different than the other functions
we have been using. You will see that we have used a special keyword:
'inline' to specify this function as an inline function. Inline
functions are treated a bit differently than normal functions. Normal
functions reside somewhere else in the program, and when we call a
function, the program jumps to that place in memory to execute the
function code. When the end of the function is reached, or the return
keyword is encountered, we go back to the place we were before we called
the function. However inline functions are special. Inline functions do
not get stored in a separate place in memory. Every place in the code
where an inline function is used, the compiler simply replaces the
function call with the function body. In other words, every time you
call the pause() function, the compiler actually replaces this code with
the body of the pause() function. So we do not jump anywhere in memory.
Inline functions are therefore not real functions, but pseudo-functions.
We use them in places where we want to be able to harness the power of a
function (i.e. the use of arguments, or easy code reuse), but do not
want the program to jump around in memory. Inline functions take up more
space in your program (as their code is copied every time it's used),
but are faster because no jump is needed. This advantage is rarely
needed, but it's nice to have complete knowledge about functions.
// if we pressed ESC, invalidate the loop
if (key == KEY_ESC) {
done = 1;
// go to the next loop iteration
continue;
}
This small section of code deserves a look because it introduces a new
keyword, the continue keyword. The continue keyword is used
within loops like for and while loops. The continue keyword simply means
skip all the rest of the commands in the body of the loop and start over
again at the beginning. We also reevaluate the condition. So for while
loops, we see if the while condition is still true before we run the
loop again. In for loops, we do the variable change statement (the
loop++ in most cases), and evaluate the conditional to see if the loop
needs to go again. Continue, basically means, continue with the next
iteration of the loop. It is equivalent to reaching the end of the loop
body, but we can use continue anywhere.
switch (key) {
// we chose the double function (times2() function)
case 1:
// wait for input and display the selection
printf("Enter a number (0-9) ? ");
key = getNumber();
printf("%d\n\n", key);
// calculate the value and print the result
number = times2(key);
printf("If you double the number\n");
printf("%d, you get %d\n\n", key, number);
// exit the switch statement
break;
// we chose the square() function
case 2:
// wait for input and display the result
printf("Enter a number (0-9) ? ");
key = getNumber();
printf("%d\n\n", key);
// calculate the value and print the result
number = square(key);
printf("If you square the number\n");
printf("%d, you get %d\n\n", key, number);
// exit the switch statement
break;
// we chose the exponent function (power() function)
case 3:
// ask for the base number
printf("Choose base (0-9) ? ");
key = getNumber();
base = key;
// print the result
printf("%d\n", key);
// ask for the exponent
printf("Choose exponent (0-9) ? ");
key = getNumber();
exponent = key;
// print the result
printf("%d\n\n", key);
// calculate the value and print the result
bigNumber = power(base, exponent);
printf("If you raise the number\n");
printf("%d to the power of %d, you\n", base, exponent);
printf("get the number %ld\n\n", bigNumber);
// exit the switch statement
break;
// we chose the sum function
case 4:
// ask for the start number
printf("Choose Start Number? ");
key = getNumber();
start = key;
// print the result
printf("%d\n", key);
// ask for the end number
printf("Choose End Number? ");
key = getNumber();
end = key;
// print the result
printf("%d\n\n", key);
// if we have valid input
if (end >= start) {
// calculate the value and print the result
number = sum(start,end);
printf("If you summed the numbers\n");
printf("from %d to %d, you would\n", start, end);
printf("end up with the number %d\n\n", number);
} else {
// otherwise yell at the user
printf("You have made an invalid\n");
printf("selection. Try again.\n\n");
}
// exit the switch statement
break;
}
Here we encounter a new kind of statement, the switch statement. Switch
statements are very powerful in C, and are similar to if-then-else
statements, but have a different form and can be more efficient. Switch
statements operate on a single variable, and compare it to various
values. In other words, switch statements check a variable against a
value to see if they are equivalent. If they are equal, we evaluate a
set of statements. This is similar to the body of an if-then-else
clause. The syntax of a switch statement is switch
(variable) { ... one or more case statements ... }. Case statements
are the conditionals of a switch statement. The syntax is case
value: .. put statements to execute if this is equal to the variable in
the switch statement... break;. The break keyword is very important,
because it tells the case statement that we have reached the end of this
condition body. It is equivalent to the } which is the end of a
conditional body. So, you can see above how the switch statement checks
the value of the variable 'key' against the values 1, 2, 3, and 4, which
are the menu choices we printed on the display. Depending upon which key
was hit, one of the four case statements will be evaluated. There is
another kind of case statement, the default: case statement which does
not use the case keyword. The default: case statement is used for
conditions which are not equal to any of the above values. We did not
need this case statement here, so it is not used. Let's examine the
individual case statements to see how they work.
// we chose the double function (times2() function)
case 1:
// wait for input and display the selection
printf("Enter a number (0-9) ? ");
key = getNumber();
printf("%d\n\n", key);
// calculate the value and print the result
number = times2(key);
printf("If you double the number\n");
printf("%d, you get %d\n\n", key, number);
// exit the switch statement
break;
You can see the first case statement is for the value 1. In this case
statement, we compare the value 1 to the value of the variable 'key'
specified by the switch statement (switch (key) {). If these
values are equal, we execute the statements following the semi-colon.
We stop executing at the break keyword. This keyword means exit from
this body. We can also use the break keyword to exit out of a loop, like
we used to the continue keyword to go to the next loop iteration, only
in this case, we stop executing altogether.
The statements in between the case 1: and the break; are rather simple,
but let's identify the function calls.
key = getNumber();
You will see that the function call is always an rvalue (remember that
is the right side of an equation). You can never do getNumber() = key;
These statements are not the same in C. In this equation, we assign the
return value of the getNumber() function to the key variable. Remember
that the getNumber() function returns a number between 0 and 9 which the
user has selected. You will also notice that you do not have to add the
void as a function argument, even though it is specified in the function
declaration. In the function declaration, it's just a placeholder. It's
not an actual value, so we don't use it when we make a function call.
number = times2(key);
The second function call is very similar to the first. We assign the
return value of the times2() function to the number variable. Only in
this case, we pass a function argument to the times2() function. The
value of the variable key will be sent to the function times2() so it
can use it in its processing. The compiler will automatically replace
the variable with its value when the program is run. Since you have
already seen examples, you have probably already figured out how this
worked.
We will skip the rest of the case statements, because they are all
relatively similar. But there is one last function call we should examine
after the switch statement has ended.
// pause the program so the user can see the result
pause();
You will remember that this function does not have a return value (we
specified it as a void function, which means it does not return a
value). It also does not take any arguments. So we can make this call
without storing a return value in any variable. In fact, you do not have
to store the return value at all. If you do not need the value the
function returns (and there are many times that you do not), you do not
have to store their return value. For example, the DlgMessage()
function (which we used in the review 1 program) returns a short integer
value. This value tells us whether the user pressed the ESC key or the
ENTER key, but usually we don't care about this, so we just call the
function without storing the return value. (For an example of this,
examine the Slider Puzzle program from review 1)
I hope the rest of the program is trivial. You should be getting good at
writing simple C programs now, and if you have been reading the TIGCC
library documentation, you should be able to write programs that
actually do something. Remember the Slider Puzzle Game. It was done
using only the knowledge you had after the first three lessons.