Techno-Plaza
Site Navigation [ News | Our Software | Calculators | Programming | Assembly | Downloads | Links | Cool Graphs | Feedback ]
 Main
   Site News

   Our Software

   Legal Information

   Credits

 Calculators
   Information

   C Programming

     Introduction to C

     Keyboard Input

     Graphics Intro

     Slider Puzzle 1

     Functions

       Part I

       Part II

     Pointers

     Dynamic Memory

     Slider Puzzle 2

     Structures

     Bit Manipulation

     Advanced Pointers

     File I/O

     Graduate Review

   Assembly

   Downloads

 Miscellaneous
   Links

   Cool Graphs

   Feedback Form

C Programming Lessons

TIGCC Programming Lessons

Lesson 4 - Functions

Step 4a - Program Analysis (Continued)

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.

Step 5 - Conclusions

In this lesson, we learned the basic tenets of functions and their usage. Basically, we just needed to look explicitly at how functions are written, declared, and properly used in order to move on to more complex topics.

Just try to look at the functions here, and how to write them so you can understand more complex source code. You've been using functions since day 1, so they should be relatively simple by now, especially after explaining explicitly how they work. But implicit knowledge is often correct, if you test your assumptions.

I should probably think of something new to tell you, but I'll leave you with this: The best way to learn programming is to guess how it could be done, then try it, and if it doesn't work, try something else. The best advice I can give you is to mess with the source code, play around with it, and see what happens. That knowledge is more valuable than anything I can teach you. So just try stuff out and see what happens. If you like reading, try reading some source code in the archives, see if you understand what it does.


Lesson 4: Functions
Questions or Comments? Feel free to contact us.


 

Copyright © 1998-2007 Techno-Plaza
All Rights Reserved Unless Otherwise Noted

Get Firefox!    Valid HTML 4.01!    Made with jEdit