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

     Pointers

     Dynamic Memory

     Slider Puzzle 2

       Part I

       Part II

       Part III

     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 Review 2 - Functions, Pointers, and Dynamic Memory

Step 2d - A look at the header file

/*
    Slider Puzzle 2.0
    Copyright (C) 2000-2002,2007 Techno-Plaza

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#ifndef _SLIDER_H
#define _SLIDER_H

/* Begin the constant definitions */

// define puzzle piece sprite height
#define PIECE_HEIGHT      16

/* Begin the static sprite definitions */

// define the puzzle sprites
// multi-dimensional array - a pointer of pointers...
// 16 sprites, 16 rows tall
static unsigned short int pieces[16][16] = {
      {0xFFFF,0xC003,0x8001,0x8181,0x8781,0x8181,0x8181,0x8181,
       0x8181,0x8181,0x8181,0x8181,0x8181,0x8001,0xC003,0xFFFF},      // piece 1
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8061,0x80C1,
       0x8181,0x8301,0x8601,0x8601,0x87E1,0x8001,0xC003,0xFFFF},      // piece 2
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8061,0x8061,0x81C1,
       0x8061,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 3
      {0xFFFF,0xC003,0x8001,0x80C1,0x81C1,0x83C1,0x83C1,0x86C1,
       0x86C1,0x8CC1,0x8FF1,0x80C1,0x80C1,0x8001,0xC003,0xFFFF},      // piece 4
      {0xFFFF,0xC003,0x8001,0x87E1,0x8601,0x8601,0x8601,0x87C1,
       0x8661,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 5
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8601,0x8601,0x87C1,
       0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 6
      {0xFFFF,0xC003,0x8001,0x87E1,0x8061,0x80C1,0x80C1,0x8181,
       0x8181,0x8181,0x8301,0x8301,0x8301,0x8001,0xC003,0xFFFF},      // piece 7
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x83C1,
       0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 8
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x8661,
       0x83E1,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 9
      {0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8CCD,0x8CCD,
       0x8CCD,0x8CCD,0x8CCD,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF},      // piece 10
      {0xFFFF,0xC003,0x8001,0x8C31,0xBCF1,0x8C31,0x8C31,0x8C31,
       0x8C31,0x8C31,0x8C31,0x8C31,0x8C31,0x8001,0xC003,0xFFFF},      // piece 11
      {0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8C0D,0x8C19,
       0x8C31,0x8C61,0x8CC1,0x8CC1,0x8CFD,0x8001,0xC003,0xFFFF},      // piece 12
      {0xFFFF,0xC003,0x8001,0x8CF1,0xBD99,0x8C19,0x8C19,0x8C71,
       0x8C19,0x8C19,0x8C19,0x8D99,0x8CF1,0x8001,0xC003,0xFFFF},      // piece 13
      {0xFFFF,0xC003,0x8001,0x8C0D,0xBC1D,0x8C3D,0x8C3D,0x8C6D,
       0x8C6D,0x8CCD,0x8CFD,0x8C0D,0x8C0D,0x8001,0xC003,0xFFFF},      // piece 14
      {0xFFFF,0xC003,0x8001,0x8CFD,0xBCC1,0x8CC1,0x8CC1,0x8CF9,
       0x8CCD,0x8C0D,0x8C0D,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF}      // piece 15
};

/* Begin Function Proto-type Definitions Here */

// initialize the puzzle array
void initPuzzle(int *);

// randomize the puzzle position pieces
void randomizePuzzle(int *);

// draw the puzzle on screen
void drawPuzzle(int *, int *, int *);

// draw the background screen text
inline void drawScreen(void);

// create and play a new game
void newGame(int *, int *, int *);

// the main method -- program execution begins here
void _main(void);

#endif

Header files are one of the most important concepts in modularized programming. They let us break down our programs into functions and data, and keep track of both within a single file. Although header files play a much bigger role when we are using multiple source files, it's still a good idea to start using them in smaller projects so you can get the feel of how they would be used when you start writing big projects that really need them. With that in mind, let's take a look at some of the things embedded in this header file.

#ifndef _SLIDER_H
#define _SLIDER_H
#endif

The first thing to notice about the header file is we have introduced new preprocessor directives, the #ifndef and #endif directives. These are very commonly used in C programming to help us in large programming projects. Although the programs we write right now are small, it's good to learn concepts which will make large program writing easier so that when we start writing large programs, we will already have several good techniques to use.

The #ifndef directive, in this context, is used to check to make sure a header file is not included more than once. Since it's possible in a large project that a header file might be included more than once, we don't want to add extra definitions or anything duplicated which would cause weird problems for the compiler. To combat this problem, we use the #ifndef directive to check for a certain preprocessor variable. So, if the variable _SLIDER_H is not defined, then we define that variable. This makes sure that the next time we try to include this header, _SLIDER_H will already be defined, and we will not include the header file twice. Only the code inside the #ifndef and the #endif directives will be included in this exclusionary clause. You can see these kinds of directives used throughout the TIGCC header library. Just open up any of the files and you will see these directives in place.

// define the puzzle sprites
// multi-dimensional array - a pointer of pointers...
// 16 sprites, 16 rows tall
static unsigned short int pieces[16][16] = {
	{0xFFFF,0xC003,0x8001,0x8181,0x8781,0x8181,0x8181,0x8181,
	 0x8181,0x8181,0x8181,0x8181,0x8181,0x8001,0xC003,0xFFFF},	// piece 1
	{0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8061,0x80C1,
	 0x8181,0x8301,0x8601,0x8601,0x87E1,0x8001,0xC003,0xFFFF},	// piece 2
	{0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8061,0x8061,0x81C1,
	 0x8061,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},	// piece 3
	{0xFFFF,0xC003,0x8001,0x80C1,0x81C1,0x83C1,0x83C1,0x86C1,
	 0x86C1,0x8CC1,0x8FF1,0x80C1,0x80C1,0x8001,0xC003,0xFFFF},	// piece 4
	{0xFFFF,0xC003,0x8001,0x87E1,0x8601,0x8601,0x8601,0x87C1,
	 0x8661,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},	// piece 5
	{0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8601,0x8601,0x87C1,
	 0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF},	// piece 6
	{0xFFFF,0xC003,0x8001,0x87E1,0x8061,0x80C1,0x80C1,0x8181,
	 0x8181,0x8181,0x8301,0x8301,0x8301,0x8001,0xC003,0xFFFF},	// piece 7
	{0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x83C1,
	 0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF},	// piece 8
	{0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x8661,
	 0x83E1,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},	// piece 9
	{0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8CCD,0x8CCD,
	 0x8CCD,0x8CCD,0x8CCD,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF},	// piece 10
	{0xFFFF,0xC003,0x8001,0x8C31,0xBCF1,0x8C31,0x8C31,0x8C31,
	 0x8C31,0x8C31,0x8C31,0x8C31,0x8C31,0x8001,0xC003,0xFFFF},	// piece 11
	{0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8C0D,0x8C19,
	 0x8C31,0x8C61,0x8CC1,0x8CC1,0x8CFD,0x8001,0xC003,0xFFFF},	// piece 12
	{0xFFFF,0xC003,0x8001,0x8CF1,0xBD99,0x8C19,0x8C19,0x8C71,
	 0x8C19,0x8C19,0x8C19,0x8D99,0x8CF1,0x8001,0xC003,0xFFFF},	// piece 13
	{0xFFFF,0xC003,0x8001,0x8C0D,0xBC1D,0x8C3D,0x8C3D,0x8C6D,
	 0x8C6D,0x8CCD,0x8CFD,0x8C0D,0x8C0D,0x8001,0xC003,0xFFFF},	// piece 14
	{0xFFFF,0xC003,0x8001,0x8CFD,0xBCC1,0x8CC1,0x8CC1,0x8CF9,
	 0x8CCD,0x8C0D,0x8C0D,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF}	// piece 15
};

The next thing of note is our use of the sprite array. Last time, to keep things simple, I defined 15 arrays for the sprites, and then a pointer to all the sprite arrays so we could access them from a single variable. Looking back, that may have been more confusing than just doing this. But here is the basic premise of this array. Each of the 15 sprites are defined within a single array, but since we want to have all the sprites accessible from a single variable, we need to have a multidimensional array. Since we have 16 sprites, and they are 16 pixels tall each, we have a 16 x 16 array, which is declared at the start as pieces[16][16]. We have not used multidimensional arrays before, but the premise is simple, we use two array indexes, the first array index points to another array, and the second array index points to the elements within that array. C gives us this way of easily thinking about arrays in this manner, but in reality, the array is just a block of memory the first index size length * the second index size length. So, this array is basically the same as a pieces[256] array, but using the multi-dimensional array marker makes it easier for us to think about it.

Although we use this giant array, all the data inside the array is static, and as such, there is no need to make a dynamic array space for it, because we already have to store all the integers associated with the piece definitions anyway, so we can declare this as a global array and save us the hassle of memory management, which won't help us here anyway. Remember that if you have data in an array that never changes, there is no reason to use dynamic memory allocation, because you can't save any space.

You might remember from last time how I said the static keyword was unnecessary. The same goes here. static in this context has nothing to do with static memory.

/* Begin Function Proto-type Definitions Here */

// initialize the puzzle array
void initPuzzle(int *);

// randomize the puzzle position pieces
void randomizePuzzle(int *);

// draw the puzzle on screen
void drawPuzzle(int *, int *, int *);

// draw the background screen text
inline void drawScreen(void);

// create and play a new game
void newGame(int *, int *, int *);

// the main method -- program execution begins here
void _main(void);

The last thing to note is the function prototypes. Remember that we should always define function prototypes for our functions. It just makes it easy for the C compiler to find things, because you can't always define functions before they are used by another function. Although so far we have done this, it's easy to get mixed up when you have a large number of functions to write.

Remember from lesson 4 that when we declare function prototypes, we use the return type, function name, and argument return types, and follow the prototype with a semicolon. There is no need to add names to the return types of arguments inside function prototypes, but you can if you want. This is up to you.

This is all I really wanted to cover about the program. The rest of the program is nearly identical to the first slider puzzle version, with the small exception of all the code being inside the _main() method. If you have more questions about this, you should consult the first review lesson.

Step 3 - Conclusion

Although we did not change how the actual program worked from the first slider puzzle version (other than making the game loop), you should be able to see that the second version of this program is much superior in design to the first program. There is much less wasted space, the program is much easier to extend, we save stack space by sharing memory from the heap (our dynamic arrays), and have modularized the program to separate the various functions. It is much easier to read and understand.

Keep in mind how functions, pointers, and dynamic memory can make your programs better. Remember that breaking programs down into smaller parts, and then putting those parts into functions is what makes C an easy language to use. Take the time to play around with these concepts. Remember that it takes time to learn C, but once you do, you can use it to write almost anything. So have fun with it. It's a great language. That's why it's still going strong after 30 some years of existence.

 


Review 2: Functions, Pointers, and Dynamic Memory
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