Meet and Make a friend
Sunday, June 28, 2009
SLC result 2066
www.ntc.net.np
or
www.bhaktapuronline.com
or
www.yetiwebsoft.com
or
www.silicon7.blogspot.com
Sunday, June 21, 2009
INTRODUCTION TO C
Let us begin with a quick introduction in C. Our aim is to show the essential elements of the language in real programs, but without getting bogged down in details, rules, and exceptions. At this point, we are not trying to be complete or even precise (save that the examples are meant to be correct). We want to get you as quickly as possible to the point where you can write useful programs, and to do that we have to concentrate on the basics: variables and constants, arithmetic, control flow, functions, and the rudiments of input and output. We are intentionally leaving out of this chapter features of C that are important for writing bigger programs. These include pointers, structures, most of C's rich set of operators, several control-flow statements, and the standard library.
This approach and its drawbacks. Most notable is that the complete story on any particular feature is not found here, and the tutorial, by being brief, may also be misleading. And because the examples do not use the full power of C, they are not as concise and elegant as they might be. We have tried to minimize these effects, but be warned. Another drawback is that later chapters will necessarily repeat some of this chapter. We hope that the repetition will help you more than it annoys.
In any case, experienced programmers should be able to extrapolate from the material in this chapter to their own programming needs. Beginners should supplement it by writing small, similar programs of their own. Both groups can use it as a framework on which to hang the more detailed descriptions that begin in Chapter 2.
1.1 Getting Started
The only way to learn a new programming language is by writing programs in it. The first program to write is the same for all languages:
Print the words
hello, world
This is a big hurdle; to leap over it you have to be able to create the program text somewhere, compile it successfully, load it, run it, and find out where your output went. With these mechanical details mastered, everything else is comparatively easy.
In C, the program to print ``hello, world'' is
#include
main()
{
printf("hello, world\n");
}
Just how to run this program depends on the system you are using. As a specific example, on the UNIX operating system you must create the program in a file whose name ends in ``.c'', such as hello.c, then compile it with the command
cc hello.c
If you haven't botched anything, such as omitting a character or misspelling something, the compilation will proceed silently, and make an executable file called a.out. If you run a.out by typing the command
a.out
it will print
hello, world
On other systems, the rules will be different; check with a local expert.
Now, for some explanations about the program itself. A C program, whatever its size, consists of functions and variables. A function contains statements that specify the computing operations to be done, and variables store values used during the computation. C functions are like the subroutines and functions in Fortran or the procedures and functions of Pascal. Our example is a function named main. Normally you are at liberty to give functions whatever names you like, but ``main'' is special - your program begins executing at the beginning of main. This means that every program must have a main somewhere.
main will usually call other functions to help perform its job, some that you wrote, and others from libraries that are provided for you. The first line of the program,
#include
tells the compiler to include information about the standard input/output library; the line appears at the beginning of many C source files. The standard library is described in Chapter 7.
One method of communicating data between functions is for the calling function to provide a list of values, called arguments, to the function it calls. The parentheses after the function name surround the argument list. In this example, main is defined to be a function that expects no arguments, which is indicated by the empty list ( ).
#include
main() define a function called main
that received no argument values
{ statements of main are enclosed in braces
printf("hello, world\n"); main calls library function printf
to print this sequence of characters
} \n represents the newline character
The first C program
The statements of a function are enclosed in braces { }. The function main contains only one statement,
printf("hello, world\n");
A function is called by naming it, followed by a parenthesized list of arguments, so this calls the function printf with the argument "hello, world\n". printf is a library function that prints output, in this case the string of characters between the quotes.
A sequence of characters in double quotes, like "hello, world\n", is called a character string or string constant. For the moment our only use of character strings will be as arguments for printf and other functions.
The sequence \n in the string is C notation for the newline character, which when printed advances the output to the left margin on the next line. If you leave out the \n (a worthwhile experiment), you will find that there is no line advance after the output is printed. You must use \n to include a newline character in the printf argument; if you try something like
printf("hello, world
");
the C compiler will produce an error message.
printf never supplies a newline character automatically, so several calls may be used to build up an output line in stages. Our first program could just as well have been written
#include
main()
{
printf("hello, ");
printf("world");
printf("\n");
}
to produce identical output.
Notice that \n represents only a single character. An escape sequence like \n provides a general and extensible mechanism for representing hard-to-type or invisible characters. Among the others that C provides are \t for tab, \b for backspace, \" for the double quote and \\ for the backslash itself. There is a complete list in Section 2.3.
Exercise 1-1. Run the ``hello, world'' program on your system. Experiment with leaving out parts of the program, to see what error messages you get.
Exercise 1-2. Experiment to find out what happens when prints's argument string contains \c, where c is some character not listed above.
Variable and Arithmetic
The next program uses the formula oC=(5/9)(oF-32) to print the following table of Fahrenheit temperatures and their centigrade or Celsius equivalents:
1 -17
20 -6
40 4
60 15
80 26
100 37
120 48
140 60
160 71
180 82
200 93
220 104
240 115
260 126
280 137
300 148
The program itself still consists of the definition of a single function named main. It is longer than the one that printed ``hello, world'', but not complicated. It introduces several new ideas, including comments, declarations, variables, arithmetic expressions, loops , and formatted output.
#include
/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300 */
main()
{
int fahr, celsius;
int lower, upper, step;
lower = 0; /* lower limit of temperature scale */
upper = 300; /* upper limit */
step = 20; /* step size */
fahr = lower;
while (fahr <= upper) { celsius = 5 * (fahr-32) / 9; printf("%d\t%d\n", fahr, celsius); fahr = fahr + step; } } The two lines /* print Fahrenheit-Celsius table for fahr = 0, 20, ..., 300 */ are a comment, which in this case explains briefly what the program does. Any characters between /* and */ are ignored by the compiler; they may be used freely to make a program easier to understand. Comments may appear anywhere where a blank, tab or newline can. In C, all variables must be declared before they are used, usually at the beginning of the function before any executable statements. A declaration announces the properties of variables; it consists of a name and a list of variables, such as int fahr, celsius; int lower, upper, step; The type int means that the variables listed are integers; by contrast with float, which means floating point, i.e., numbers that may have a fractional part. The range of both int and float depends on the machine you are using; 16-bits ints, which lie between -32768 and +32767, are common, as are 32-bit ints. A float number is typically a 32-bit quantity, with at least six significant digits and magnitude generally between about 10-38 and 1038. C provides several other data types besides int and float, including: char character - a single byte short short integer long long integer double double-precision floating point The size of these objects is also machine-dependent. There are also arrays, structures and unions of these basic types, pointers to them, and functions that return them, all of which we will meet in due course. Computation in the temperature conversion program begins with the assignment statements lower = 0; upper = 300; step = 20; which set the variables to their initial values. Individual statements are terminated by semicolons. Each line of the table is computed the same way, so we use a loop that repeats once per output line; this is the purpose of the while loop while (fahr <= upper) { ... } The while loop operates as follows: The condition in parentheses is tested. If it is true (fahr is less than or equal to upper), the body of the loop (the three statements enclosed in braces) is executed. Then the condition is re-tested, and if true, the body is executed again. When the test becomes false (fahr exceeds upper) the loop ends, and execution continues at the statement that follows the loop. There are no further statements in this program, so it terminates. The body of a while can be one or more statements enclosed in braces, as in the temperature converter, or a single statement without braces, as in while (i < i =" 2" celsius =" 5">
/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300; floating-point version */
main()
{
float fahr, celsius;
float lower, upper, step;
lower = 0; /* lower limit of temperatuire scale */
upper = 300; /* upper limit */
step = 20; /* step size */
fahr = lower;
while (fahr <= upper) { celsius = (5.0/9.0) * (fahr-32.0); printf("%3.0f %6.1f\n", fahr, celsius); fahr = fahr + step; } } This is much the same as before, except that fahr and celsius are declared to be float and the formula for conversion is written in a more natural way. We were unable to use 5/9 in the previous version because integer division would truncate it to zero. A decimal point in a constant indicates that it is floating point, however, so 5.0/9.0 is not truncated because it is the ratio of two floating-point values. If an arithmetic operator has integer operands, an integer operation is performed. If an arithmetic operator has one floating-point operand and one integer operand, however, the integer will be converted to floating point before the operation is done. If we had written (fahr-32), the 32 would be automatically converted to floating point. Nevertheless, writing floating-point constants with explicit decimal points even when they have integral values emphasizes their floating-point nature for human readers. The detailed rules for when integers are converted to floating point are in Chapter 2. For now, notice that the assignment fahr = lower; and the test while (fahr <= upper) also work in the natural way - the int is converted to float before the operation is done. The printf conversion specification %3.0f says that a floating-point number (here fahr) is to be printed at least three characters wide, with no decimal point and no fraction digits. %6.1f describes another number (celsius) that is to be printed at least six characters wide, with 1 digit after the decimal point. The output looks like this: 0 -17.8 20 -6.7 40 4.4 ... Width and precision may be omitted from a specification: %6f says that the number is to be at least six characters wide; %.2f specifies two characters after the decimal point, but the width is not constrained; and %f merely says to print the number as floating point. %d print as decimal integer %6d print as decimal integer, at least 6 characters wide %f print as floating point %6f print as floating point, at least 6 characters wide %.2f print as floating point, 2 characters after decimal point %6.2f print as floating point, at least 6 wide and 2 after decimal point Among others, printf also recognizes %o for octal, %x for hexadecimal, %c for character, %s for character string and %% for itself. Exercise 1-3. Modify the temperature conversion program to print a heading above the table. Exercise 1-4. Write a program to print the corresponding Celsius to Fahrenheit table. 1.3 The for statement There are plenty of different ways to write a program for a particular task. Let's try a variation on the temperature converter. #include
/* print Fahrenheit-Celsius table */
main()
{
int fahr;
for (fahr = 0; fahr <= 300; fahr = fahr + 20) printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); } This produces the same answers, but it certainly looks different. One major change is the elimination of most of the variables; only fahr remains, and we have made it an int. The lower and upper limits and the step size appear only as constants in the for statement, itself a new construction, and the expression that computes the Celsius temperature now appears as the third argument of printf instead of a separate assignment statement. This last change is an instance of a general rule - in any context where it is permissible to use the value of some type, you can use a more complicated expression of that type. Since the third argument of printf must be a floating-point value to match the %6.1f, any floating-point expression can occur here. The for statement is a loop, a generalization of the while. If you compare it to the earlier while, its operation should be clear. Within the parentheses, there are three parts, separated by semicolons. The first part, the initialization fahr = 0 is done once, before the loop proper is entered. The second part is the test or condition that controls the loop: fahr <= 300 This condition is evaluated; if it is true, the body of the loop (here a single ptintf) is executed. Then the increment step fahr = fahr + 20 is executed, and the condition re-evaluated. The loop terminates if the condition has become false. As with the while, the body of the loop can be a single statement or a group of statements enclosed in braces. The initialization, condition and increment can be any expressions. The choice between while and for is arbitrary, based on which seems clearer. The for is usually appropriate for loops in which the initialization and increment are single statements and logically related, since it is more compact than while and it keeps the loop control statements together in one place. Exercise 1-5. Modify the temperature conversion program to print the table in reverse order, that is, from 300 degrees to 0. Symbolic Constants A final observation before we leave temperature conversion forever. It's bad practice to bury ``magic numbers'' like 300 and 20 in a program; they convey little information to someone who might have to read the program later, and they are hard to change in a systematic way. One way to deal with magic numbers is to give them meaningful names. A #define line defines a symbolic name or symbolic constant to be a particular string of characters: #define name replacement list Thereafter, any occurrence of name (not in quotes and not part of another name) will be replaced by the corresponding replacement text. The name has the same form as a variable name: a sequence of letters and digits that begins with a letter. The replacement text can be any sequence of characters; it is not limited to numbers. #include
#define LOWER 0 /* lower limit of table */
#define UPPER 300 /* upper limit */
#define STEP 20 /* step size */
/* print Fahrenheit-Celsius table */
main()
{
int fahr;
for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); } The quantities LOWER, UPPER and STEP are symbolic constants, not variables, so they do not appear in declarations. Symbolic constant names are conventionally written in upper case so they can ber readily distinguished from lower case variable names. Notice that there is no semicolon at the end of a #define line. Character Input and Output We are going to consider a family of related programs for processing character data. You will find that many programs are just expanded versions of the prototypes that we discuss here. The model of input and output supported by the standard library is very simple. Text input or output, regardless of where it originates or where it goes to, is dealt with as streams of characters. A text stream is a sequence of characters divided into lines; each line consists of zero or more characters followed by a newline character. It is the responsibility of the library to make each input or output stream confirm this model; the C programmer using the library need not worry about how lines are represented outside the program. The standard library provides several functions for reading or writing one character at a time, of which getchar and putchar are the simplest. Each time it is called, getchar reads the next input character from a text stream and returns that as its value. That is, after c = getchar(); the variable c contains the next character of input. The characters normally come from the keyboard; input from files is discussed in Chapter 7. The function putchar prints a character each time it is called: putchar(c); prints the contents of the integer variable c as a character, usually on the screen. Calls to putchar and printf may be interleaved; the output will appear in the order in which the calls are made. 1.5.1 File Copying Given getchar and putchar, you can write a surprising amount of useful code without knowing anything more about input and output. The simplest example is a program that copies its input to its output one character at a time: read a character while (charater is not end-of-file indicator) output the character just read read a character Converting this into C gives: #include
/* copy input to output; 1st version */
main()
{
int c;
c = getchar();
while (c != EOF) {
putchar(c);
c = getchar();
}
}
The relational operator != means ``not equal to''.
What appears to be a character on the keyboard or screen is of course, like everything else, stored internally just as a bit pattern. The type char is specifically meant for storing such character data, but any integer type can be used. We used int for a subtle but important reason.
The problem is distinguishing the end of input from valid data. The solution is that getchar returns a distinctive value when there is no more input, a value that cannot be confused with any real character. This value is called EOF, for ``end of file''. We must declare c to be a type big enough to hold any value that getchar returns. We can't use char since c must be big enough to hold EOF in addition to any possible char. Therefore we use int.
EOF is an integer defined in
The program for copying would be written more concisely by experienced C programmers. In C, any assignment, such as
c = getchar();
is an expression and has a value, which is the value of the left hand side after the assignment. This means that a assignment can appear as part of a larger expression. If the assignment of a character to c is put inside the test part of a while loop, the copy program can be written this way:
#include
/* copy input to output; 2nd version */
main()
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
The while gets a character, assigns it to c, and then tests whether the character was the end-of-file signal. If it was not, the body of the while is executed, printing the character. The while then repeats. When the end of the input is finally reached, the while terminates and so does main.
This version centralizes the input - there is now only one reference to getchar - and shrinks the program. The resulting program is more compact, and, once the idiom is mastered, easier to read. You'll see this style often. (It's possible to get carried away and create impenetrable code, however, a tendency that we will try to curb.)
The parentheses around the assignment, within the condition are necessary. The precedence of != is higher than that of =, which means that in the absence of parentheses the relational test != would be done before the assignment =. So the statement
c = getchar() != EOF
is equivalent to
c = (getchar() != EOF)
This has the undesired effect of setting c to 0 or 1, depending on whether or not the call of getchar returned end of file. (More on this in Chapter 2.)
Exercsise 1-6. Verify that the expression getchar() != EOF is 0 or 1.
Exercise 1-7. Write a program to print the value of EOF.
1.5.2 Character Counting
The next program counts characters; it is similar to the copy program.
#include
/* count characters in input; 1st version */
main()
{
long nc;
nc = 0;
while (getchar() != EOF)
++nc;
printf("%ld\n", nc);
}
The statement
++nc;
presents a new operator, ++, which means increment by one. You could instead write nc = nc + 1 but ++nc is more concise and often more efficient. There is a corresponding operator -- to decrement by 1. The operators ++ and -- can be either prefix operators (++nc) or postfix operators (nc++); these two forms have different values in expressions, as will be shown in Chapter 2, but ++nc and nc++ both increment nc. For the moment we will will stick to the prefix form.
The character counting program accumulates its count in a long variable instead of an int. long integers are at least 32 bits. Although on some machines, int and long are the same size, on others an int is 16 bits, with a maximum value of 32767, and it would take relatively little input to overflow an int counter. The conversion specification %ld tells printf that the corresponding argument is a long integer.
It may be possible to cope with even bigger numbers by using a double (double precision float). We will also use a for statement instead of a while, to illustrate another way to write the loop.
#include
/* count characters in input; 2nd version */
main()
{
double nc;
for (nc = 0; gechar() != EOF; ++nc)
;
printf("%.0f\n", nc);
}
printf uses %f for both float and double; %.0f suppresses the printing of the decimal point and the fraction part, which is zero.
The body of this for loop is empty, because all the work is done in the test and increment parts. But the grammatical rules of C require that a for statement have a body. The isolated semicolon, called a null statement, is there to satisfy that requirement. We put it on a separate line to make it visible.
Before we leave the character counting program, observe that if the input contains no characters, the while or for test fails on the very first call to getchar, and the program produces zero, the right answer. This is important. One of the nice things about while and for is that they test at the top of the loop, before proceeding with the body. If there is nothing to do, nothing is done, even if that means never going through the loop body. Programs should act intelligently when given zero-length input. The while and for statements help ensure that programs do reasonable things with boundary conditions.
1.5.3 Line Counting
The next program counts input lines. As we mentioned above, the standard library ensures that an input text stream appears as a sequence of lines, each terminated by a newline. Hence, counting lines is just counting newlines:
#include
/* count lines in input */
main()
{
int c, nl;
nl = 0;
while ((c = getchar()) != EOF)
if (c == '\n')
++nl;
printf("%d\n", nl);
}
The body of the while now consists of an if, which in turn controls the increment ++nl. The if statement tests the parenthesized condition, and if the condition is true, executes the statement (or group of statements in braces) that follows. We have again indented to show what is controlled by what.
The double equals sign == is the C notation for ``is equal to'' (like Pascal's single = or Fortran's .EQ.). This symbol is used to distinguish the equality test from the single = that C uses for assignment. A word of caution: newcomers to C occasionally write = when they mean ==. As we will see in Chapter 2, the result is usually a legal expression, so you will get no warning.
A character written between single quotes represents an integer value equal to the numerical value of the character in the machine's character set. This is called a character constant, although it is just another way to write a small integer. So, for example, 'A' is a character constant; in the ASCII character set its value is 65, the internal representation of the character A. Of course, 'A' is to be preferred over 65: its meaning is obvious, and it is independent of a particular character set.
The escape sequences used in string constants are also legal in character constants, so '\n' stands for the value of the newline character, which is 10 in ASCII. You should note carefully that '\n' is a single character, and in expressions is just an integer; on the other hand, '\n' is a string constant that happens to contain only one character. The topic of strings versus characters is discussed further in Chapter 2.
Exercise 1-8. Write a program to count blanks, tabs, and newlines.
Exercise 1-9. Write a program to copy its input to its output, replacing each string of one or more blanks by a single blank.
Exercise 1-10. Write a program to copy its input to its output, replacing each tab by \t, each backspace by \b, and each backslash by \\. This makes tabs and backspaces visible in an unambiguous way.
1.5.4 Word Counting
The fourth in our series of useful programs counts lines, words, and characters, with the loose definition that a word is any sequence of characters that does not contain a blank, tab or newline. This is a bare-bones version of the UNIX program wc.
#include
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
/* count lines, words, and characters in input */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '\n')
++nl;
if (c == ' ' c == '\n' c = '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
Every time the program encounters the first character of a word, it counts one more word. The variable state records whether the program is currently in a word or not; initially it is ``not in a word'', which is assigned the value OUT. We prefer the symbolic constants IN and OUT to the literal values 1 and 0 because they make the program more readable. In a program as tiny as this, it makes little difference, but in larger programs, the increase in clarity is well worth the modest extra effort to write it this way from the beginning. You'll also find that it's easier to make extensive changes in programs where magic numbers appear only as symbolic constants.
The line
nl = nw = nc = 0;
sets all three variables to zero. This is not a special case, but a consequence of the fact that an assignment is an expression with the value and assignments associated from right to left. It's as if we had written
nl = (nw = (nc = 0));
The operator means OR, so the line
if (c == ' ' c == '\n' c = '\t')
says ``if c is a blank or c is a newline or c is a tab''. (Recall that the escape sequence \t is a visible representation of the tab character.) There is a corresponding operator && for AND; its precedence is just higher than . Expressions connected by && or are evaluated left to right, and it is guaranteed that evaluation will stop as soon as the truth or falsehood is known. If c is a blank, there is no need to test whether it is a newline or tab, so these tests are not made. This isn't particularly important here, but is significant in more complicated situations, as we will soon see.
The example also shows an else, which specifies an alternative action if the condition part of an if statement is false. The general form is
if (expression)
statement
1
else
statement2
One and only one of the two statements associated with an if-else is performed. If the expression is true, statement1 is executed; if not, statement2 is executed. Each statement can be a single statement or several in braces. In the word count program, the one after the else is an if that controls two statements in braces.
Exercise 1-11. How would you test the word count program? What kinds of input are most likely to uncover bugs if there are any?
Exercise 1-12. Write a program that prints its input one word per line.
1.6 Arrays
Let is write a program to count the number of occurrences of each digit, of white space characters (blank, tab, newline), and of all other characters. This is artificial, but it permits us to illustrate several aspects of C in one program.
There are twelve categories of input, so it is convenient to use an array to hold the number of occurrences of each digit, rather than ten individual variables. Here is one version of the program:
#include
/* count digits, white space, others */
main()
{
int c, i, nwhite, nother;
int ndigit[10];
nwhite = nother = 0;
for (i = 0; i < c =" getchar())">= '0' && c <= '9') ++ndigit[c-'0']; else if (c == ' ' c == '\n' c == '\t') ++nwhite; else ++nother; printf("digits ="); for (i = 0; i < space =" %d," other =" %d\n" digits =" 9" space =" 123," other =" 345">= '0' && c <= '9') determines whether the character in c is a digit. If it is, the numeric value of that digit is c - '0' This works only if '0', '1', ..., '9' have consecutive increasing values. Fortunately, this is true for all character sets. By definition, chars are just small integers, so char variables and constants are identical to ints in arithmetic expressions. This is natural and convenient; for example c-'0' is an integer expression with a value between 0 and 9 corresponding to the character '0' to '9' stored in c, and thus a valid subscript for the array ndigit. The decision as to whether a character is a digit, white space, or something else is made with the sequence if (c >= '0' && c <= '9') ++ndigit[c-'0']; else if (c == ' ' c == '\n' c == '\t') ++nwhite; else ++nother; The pattern if (condition 1) statement1 else if (condition2) statement2 ... ... else statementn occurs frequently in programs as a way to express a multi-way decision. The conditions are evaluated in order from the top until some condition is satisfied; at that point the corresponding statement part is executed, and the entire construction is finished. (Any statement can be several statements enclosed in braces.) If none of the conditions is satisfied, the statement after the final else is executed if it is present. If the final else and statement are omitted, as in the word count program, no action takes place. There can be any number of else if(condition) statement groups between the initial if and the final else. As a matter of style, it is advisable to format this construction as we have shown; if each if were indented past the previous else, a long sequence of decisions would march off the right side of the page. The switch statement, to be discussed in Chapter 4, provides another way to write a multi-way branch that is particulary suitable when the condition is whether some integer or character expression matches one of a set of constants. For contrast, we will present a switch version of this program in Section 3.4.
int power(int m, int n);
/* test power function */
main()
{
int i;
for (i = 0; i <>= 0 */
int power(int base, int n)
{
int i, p;
p = 1;
for (i = 1; i <= n; ++i) p = p * base; return p; } A function definition has this form: return-type function-name(parameter declarations, if any) { declarations statements } Function definitions can appear in any order, and in one source file or several, although no function can be split between files. If the source program appears in several files, you may have to say more to compile and load it than if it all appears in one, but that is an operating system matter, not a language attribute. For the moment, we will assume that both functions are in the same file, so whatever you have learned about running C programs will still work. The function power is called twice by main, in the line printf("%d %d %d\n", i, power(2,i), power(-3,i)); Each call passes two arguments to power, which each time returns an integer to be formatted and printed. In an expression, power(2,i) is an integer just as 2 and i are. (Not all functions produce an integer value; we will take this up in Chapter 4.) The first line of power itself, int power(int base, int n) declares the parameter types and names, and the type of the result that the function returns. The names used by power for its parameters are local to power, and are not visible to any other function: other routines can use the same names without conflict. This is also true of the variables i and p: the i in power is unrelated to the i in main. We will generally use parameter for a variable named in the parenthesized list in a function. The terms formal argument and actual argument are sometimes used for the same distinction. The value that power computes is returned to main by the return: statement. Any expression may follow return: return expression; A function need not return a value; a return statement with no expression causes control, but no useful value, to be returned to the caller, as does ``falling off the end'' of a function by reaching the terminating right brace. And the calling function can ignore a value returned by a function. You may have noticed that there is a return statement at the end of main. Since main is a function like any other, it may return a value to its caller, which is in effect the environment in which the program was executed. Typically, a return value of zero implies normal termination; non-zero values signal unusual or erroneous termination conditions. In the interests of simplicity, we have omitted return statements from our main functions up to this point, but we will include them hereafter, as a reminder that programs should return status to their environment. The declaration int power(int base, int n); just before main says that power is a function that expects two int arguments and returns an int. This declaration, which is called a function prototype, has to agree with the definition and uses of power. It is an error if the definition of a function or any uses of it do not agree with its prototype. parameter names need not agree. Indeed, parameter names are optional in a function prototype, so for the prototype we could have written int power(int, int); Well-chosen names are good documentation however, so we will often use them. A note of history: the biggest change between ANSI C and earlier versions is how functions are declared and defined. In the original definition of C, the power function would have been written like this: /* power: raise base to n-th power; n >= 0 */
power(base, n)
int base, n;
{
int i, p;
p = 1;
for (i = 1; i <= n; ++i) p = p * base; return p; }
int power(int base, int n)
{
int p;
for (p = 1; n > 0; --n)
p = p * base;
return p;
}
The parameter n is used as a temporary variable, and is counted down (a for loop that runs backwards) until it becomes zero; there is no longer a need for the variable i. Whatever is done to n inside power has no effect on the argument that power was originally called with.
When necessary, it is possible to arrange for a function to modify a variable in a calling routine. The caller must provide the address of the variable to be set (technically a pointer to the variable), and the called function must declare the parameter to be a pointer and access the variable indirectly through it. We will cover pointers in Chapter 5.
The story is different for arrays. When the name of an array is used as an argument, the value passed to the function is the location or address of the beginning of the array - there is no copying of array elements. By subscripting this value, the function can access and alter any argument of the array. This is the topic of the next section.
1.9 Character Arrays
The most common type of array in C is the array of characters. To illustrate the use of character arrays and functions to manipulate them, let's write a program that reads a set of text lines and prints the longest. The outline is simple enough:
while (there's another line)
if (it's longer than the previous longest)
(save it)
(save its length)
print longest line
This outline makes it clear that the program divides naturally into pieces. One piece gets a new line, another saves it, and the rest controls the process.
Since things divide so nicely, it would be well to write them that way too. Accordingly, let us first write a separate function getline to fetch the next line of input. We will try to make the function useful in other contexts. At the minimum, getline has to return a signal about possible end of file; a more useful design would be to return the length of the line, or zero if end of file is encountered. Zero is an acceptable end-of-file return because it is never a valid line length. Every text line has at least one character; even a line containing only a newline has length 1.
When we find a line that is longer than the previous longest line, it must be saved somewhere. This suggests a second function, copy, to copy the new line to a safe place.
Finally, we need a main program to control getline and copy. Here is the result.
#include
#define MAXLINE 1000 /* maximum input line length */
int getline(char line[], int maxline);
void copy(char to[], char from[]);
/* print the longest input line */
main()
{
int len; /* current line length */
int max; /* maximum length seen so far */
char line[MAXLINE]; /* current input line */
char longest[MAXLINE]; /* longest line saved here */
max = 0;
while ((len = getline(line, MAXLINE)) > 0)
if (len > max) {
max = len;
copy(longest, line);
}
if (max > 0) /* there was a line */
printf("%s", longest);
return 0;
}
/* getline: read a line into s, return length */
int getline(char s[],int lim)
{
int c, i;
for (i=0; i < c="getchar())!="EOF" c ="="" i =" 0;">
#include
#define MAXLINE 1000 /* maximum input line size */
int max; /* maximum length seen so far */
char line[MAXLINE]; /* current input line */
char longest[MAXLINE]; /* longest line saved here */
int getline(void);
void copy(void);
/* print longest input line; specialized version */
main()
{
int len;
extern int max;
extern char longest[];
max = 0;
while ((len = getline()) > 0)
if (len > max) {
max = len;
copy();
}
if (max > 0) /* there was a line */
printf("%s", longest);
return 0;
}
/* getline: specialized version */
int getline(void)
{
int c, i;
extern char line[];
for (i = 0; i < c="getchar))" c ="="" i =" 0;">
Since the specialized versions of getline and copy have no arguments, logic would suggest that their prototypes at the beginning of the file should be getline() and copy(). But for compatibility with older C programs the standard takes an empty list as an old-style declaration, and turns off all argument list checking; the word void must be used for an explicitly empty list. We will discuss this further in Chapter 4.
You should note that we are using the words definition and declaration carefully when we refer to external variables in this section.``Definition'' refers to the place where the variable is created or assigned storage; ``declaration'' refers to places where the nature of the variable is stated but no storage is allocated.
By the way, there is a tendency to make everything in sight an extern variable because it appears to simplify communications - argument lists are short and variables are always there when you want them. But external variables are always there even when you don't want them. Relying too heavily on external variables is fraught with peril since it leads to programs whose data connections are not all obvious - variables can be changed in unexpected and even inadvertent ways, and the program is hard to modify. The second version of the longest-line program is inferior to the first, partly for these reasons, and partly because it destroys the generality of two useful functions by writing into them the names of the variables they manipulate.
At this point we have covered what might be called the conventional core of C. With this handful of building blocks, it's possible to write useful programs of considerable size, and it would probably be a good idea if you paused long enough to do so. These exercises suggest programs of somewhat greater complexity than the ones earlier in this chapter.
Exercise 1-20. Write a program detab that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns. Should n be a variable or a symbolic parameter?
Exercise 1-21. Write a program entab that replaces strings of blanks by the minimum number of tabs and blanks to achieve the same spacing. Use the same tab stops as for detab. When either a tab or a single blank would suffice to reach a tab stop, which should be given preference?
Exercise 1-22. Write a program to ``fold'' long input lines into two or more shorter lines after the last non-blank character that occurs before the n-th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column.
Exercise 1-23. Write a program to remove all comments from a C program. Don't forget to handle quoted strings and character constants properly. C comments don't nest.
Exercise 1-24. Write a program to check a C program for rudimentary syntax errors like unmatched parentheses, brackets and braces. Don't forget about quotes, both single and double, escape sequences, and comments. (This program is hard if you do it in full generality.)
QBASIC INTRODUCTION
In the early days of programming, it was usually the scientific elite doing the programming and they were usually trained above and beyond the average American to do their programming work. It was not until 1964 at Dartsmouth college that the Beginner's All-purpose Symbolic Instruction Code would be introduced -- more commonly known as BASIC. Using common English to perform processor tasks, BASIC became quickly popular, although it was disliked by programmers of more "low-level" languages such as assembly and FORTRAN. In 1985 Microsoft released their own version of BASIC called QBasic with their MS-DOS 5.0 operating system. Since then, nearly every PC user owns their own copy of QBasic, making it a widely known language.
QBasic is a very simple language to pick up, and yet it can accomplish a great deal. Granted you will probably never write Doom or Word Perfect with QBasic, but it has it's strong points. One of them is to introduce people to programming without having to worry about the internal workings of the computer. It's simple to create games, business applications, simple databases, and graphics. The best aspect of the language is it's close resemblance to English.
This small tutorial introduces the simple concepts of programming to get you started, so if you already know another language or are already familiar with programming, you may want to skim through the first couple sections. Good luck!
VARIABLES
A variable, simply defined, is a name which can contain a value. Programming involves giving values to these names and presenting them in some form to the user. A variable has a type which is defined by the kind of value it holds. If the variable holds a number, it may be of integer, floating decimal, long integer, or imaginary. If the variable holds symbols or text, it may be a character variable or a string variable. These are terms you will become accustomed to as you continue programming.
Here are some examples of values a variable might contain:
STRING "hello, this is a string"
INTEGER 5
LONG 92883
SINGLE 39.2932
DOUBLE 983288.18
The first is a string. Strings contain text. The last four are number types. But the computer does not know what kind of value you are trying to give a variable unless you tell it! There are two methods of telling the computer what kind of variable you are using:
Explicitly declare the variable AS a type. This is done by using the DIM statement. Say you wanted to make a variable called number which would contain an integer (whole number, no digits after the decimal point). You would do it like this:
DIM number AS INTEGER
Then you would use that variable as an integer. The word DIM actually originates from the word Dimension, but you won't see why until we discuss arrays.
Put a symbol after the variable name which is defined as representing that type. QBasic has a set of symbols which represent each variable type:
$ String% Integer& Long! Single# Double
Appending one of these symbols to the name of a variable when you use it in the program tells the computer that you are using it as that type.
This is actually a difficult concept to grasp for newcomers to programming. The most common error in QBasic is the infamous Type Mismatch which you will see a lot. This means that you are trying to put a value into a variable of the wrong type. You might be trying to put the letters "hi there" into an integer variable. If you don't define the type of the variable, then QBasic assumes it is of the Single type, which can often yield unexpected results. I personally prefer to use the type symbols after variable names, but come explicitly declare them usually at the head of their programs.
INTERACTING WITH THE COMPUTER
You know what a variable is and how to control them, it's time you learned some programming. QBasic (like all other languages) is set up using pre-defined statements according to the syntax specified for that statement. It may be helpful to look in the help index to learn a statement, although I've heard many complaint's that the help index is too hard. Indeed it is too hard for new programmers, but as you learn more and more statements and their syntaxes, you'll become accustomed to the index and use it as a casual reference. Lets make a program that prints some text on the screen. Type qbasic at the DOS prompt and enter the following program.
CLS
PRINT "This text will appear on the screen"
END
The first statement -- CLS -- stands for "clear screen." It erases whatever was on the screen before it was executed. PRINT simply displays its argument to the screen at the current text cursor location. The argument in this case is the text enclosed in quotes. PRINT displays text within quotes directly, or it can display the value of a variable, like this:
CLS
a% = 50
b% = 100
PRINT "The value of a is "; a%; " and the value of b is "; b%
END
This will yield the output; The value of a is 50 and the value of b is 100. The semicolons indicate that the next time something is printed, it will be right after where the last PRINT statement left off. Remember that PRINT prints literally what is inside quotes, and the value of the variable which is not in quotes. a% and b% are integers containing values in this example, and their values are printed using the PRINT statement. Say you want to interact with the user now. You'll need to learn a statement called INPUT. INPUT displays a prompt (the first argument) and assigns what the user types in to a variable (the second argument)
CLS
INPUT "What is your name? ", yourName$
INPUT "How old are you? ", age%
PRINT "So, "; yourName$; ", you are "; age%; " years old. That's interesting."
END
This firsts asks the user for their name and assigns it to the string variable yourName$. Then the age is requested, and the result is printed in a sentence. Try it out! So what happens if you input I DON'T KNOW for the age prompt? You'll get a weird message that says REDO FROM START. Why? The program is trying to assign a string (text) to an integer (number) type, and this makes no sense so the user is asked to do it over again. Another cornerstone of programming is the conditional test. Basically, the program tests if a condition is true, and if it is, it does something. It looks like English so it's not as hard as it sounds.
CLS
PRINT "1. Say hello" ' option 1
PRINT "2. Say nice tie" ' option 2
INPUT "Enter your selection ", selection%IF selection% = 1 THEN PRINT "hello"IF selection% = 2 THEN PRINT "nice tie"
END
The user is given a set of options, and then they input a value which is assigned to the variable selection%. The value of selection% is then tested, and code is executed based on the value. If the user pressed 1, it prints hello, but if they pressed 2, it prints nice tie. Also notice the text after the ' in the code. These are remark statements. Anything printed after a ' on a line does not affect the outcome of the program. Back to the actual code -- but what if the user doesn't input 1 or 2? What if they input 328? This must be taken into account as part of programming. You usually can't assume that the user has half a brain, so if they do something wrong, you can't screw up the program. So the ELSE statement comes into play. The logic goes like this: IF the condition is true,THEN do something, but if the condition is anything ELSE, then do something else. You follow? The ELSE statement is used with IF...THEN to test if a condition is anything else.
CLS
INPUT "Press 1 if you want some pizza.", number%IF number% = 1 THEN PRINT "Here's your pizza" ELSE PRINT "You don't get pizza"
END
That's a fairly simple example, and real life things will be much more complex. Lets try a "real life" program. QBasic is capable of fairly sophisticated math, so lets put some of it to use. Say your Algebra teacher tells you to find the areas of the circles with the following radiuses (radii, whatever), and he gives you a sheet with hundreds of radii. You decide to boot up your computer and write the following program:
CLS
pi! = 3.1415
INPUT "What is the radius of the circle? ", radius!area! = pi! * radius! ^ 2
PRINT "The area of the circle is ", area!
END
First, we're defining the variable pi. It's a single number, which means that it can be a fairly large number with some decimal places. The exclamation mark tells QBasic that pi is of the single type. Next, the user is prompted for the radius of their circle. Then the area is calculated. The * means "times," and the ^ (carrot) means "to the power of." radius! ^ 2 means "radius squared." This could also be written as pi! * radius! * radius!.
There's one big problem with that program. The teacher gave you a sheet with hundreds of radii (please email me if you know how to spell this!). For every radius, you must run the program over again. This is not practical. If we had some kind of a loop until we wanted to quit that just kept on repeating over and over it would be much more useful. Of course, QBasic has the means of performing this feat. Loop structures. They start with the statement DO, and end with the statement LOOP. You can LOOP UNTIL or WHILE , or DO UNTIL or WHILE a condition is true. Another option (which we will use) is to break out of the loop manually as soon as a condition is true. Lets revise the previous code:
CLS
pi! = 3.1415DO ' Begin the loop here
INPUT "What is the radius of the circle? (-1 to end) ", radius!IF radius! = -1 THEN EXIT DO area! = pi! * radius! ^ 2
PRINT "The area of the circle is ", area!
PRINT LOOP ' End the loop here
END
Now we can end the program by entering -1 as the radius. The program checks the radius after the user inputs it and checks if it is -1. If it is, it exits the loop. If it isn't it just keeps going it's merry way. The PRINT with no arguments prints a blanks line so we can separate our answers. I highly recommend entering this program into QBasic just so you can see exactly how it works.
Say you want to print something in a certain pre-defined format. Say you want to print a series of digits with only 2 places after the decimal point and a dollar sign before the first digit. To do this requires the PRINT USING statement, which is very handy in applications for businesses. The PRINT USING statement accepts two types of arguments. The first is a string which has already been defined. This is a special type of string, in that it contains format specifiers, which specify the format of the variables passed as the other arguments. Confused? You won't be. Here's a quick list of the most common format specifiers
### digits& Prints an entire string\ \ Prints a string fit within the backslashes. Any thing longer is truncated$$ Puts a dollar sign to the left of a number. Prints a decimal point, Prints a comma every third digit to the leftof the decimal point.
And these can be combined in a format string to make a user defined way to print something. So $$#,###.## will print a number with a dollar sign to the left of it. If the number has more than two decimal places, it is truncated to two. If it is more than four digits long to the left of the decimal place, it is also truncated to fit. To use a PRINT USING statement, you must first define the format string containing the format specifiers. Then you use PRINT USING, then the name of the format string, and variable values to fill the places defined in the format string. Here's a code example
CLS
' get user input
INPUT "Enter item name: ", itemname$
INPUT "How many items?: ", numitems%
INPUT "What does one cost?: ", itemcost!
CLS ' display inputsformat$ = "\ \ #,### $$#,###.## $$#,###,###.##"
PRINT "Item Name Quantity Cost Total Cost "PRINT "-------------- -------- ---------- --------------"totalcost! = numitems% * itemcost!
PRINT USING format$; itemname$; numitems%; itemcost!; totalcost!
END
First, we get the item name, number of items, and cost per item from the user. Then we clear the screen and define the format string to be used. It contains a static length string (text that will be truncated if it is too long), up to 4 digits for the quantity, 4 digits and two decimals for the item cost, and 7 digits and two decimals for the total cost. Then we print out some column headers so we know what each value will represent, and some nice lines to go under the column headers. Then the total cost is calculated by multiplying the number of items by the item cost. Finally, the four variable's values are displayed under the column headers using the PRINT USING statement.
MORE ADVANCED DATA MANIPULATION
There are numerous methods to manipulate data and present it to the user in QBasic. One is called an array. An array is a variable which can contain more than one value. For example, you might have an array called a, and you could assign data to the members of that array. There might be a value for a(1), and a different value for a(6). Before an array can be used, it must be declared. Arrays are declared with the DIM statement used in section 1. Here is an example of an array declaration:
DIM a(1 TO 100) AS INTEGER
There are now 100 different values that can be assigned to the array a, and they must all be integers. It could also look like this:
DIM a%(1 TO 100)
Using the symbol % for integer. We call the different values for the array members of the array. Array a has 100 members. Array members can be assigned values by using a subscript number within parentheses after the array name, like this:
a%(1) = 10
a%(2) = 29
a%(3) = 39
And so on. Now you're probably wondering why the statement for declare is DIM. This comes from a term used in earlier programming languages that means dimension. That still doesn't answer the question... why not use the statement DECLARE? Well, an array can have more than one dimension. Arrays with multiple dimensions have y members in the second dimension for every x member of the first dimension in the following algorithm:
DIM array( 1 TO x, 1 TO y) AS INTEGER
So if the actual declaration looked like this:
DIM a$( 1 TO 3, 1 TO 3)
You would have the following members to assign values to:
a$(1,1) a$(1,2) a$(1,3)a$(2,1) a$(2,2) a$(2,3)a$(3,1) a$(3,2) a$(3,3)
A two dimensional array is useful for tracking the status of each piece in a checkers game, or something of the like. Recall the last example program of section that we had a program that would ask the user for the item name, the item cost, and the quantity of that item, the spit out the data just given in a nice format with the total in the right hand column. Well, with only one item, this program isn't very practical. But now with our newfound knowledge of arrays and the knowledge we already have of loops, we can create a somewhat useful application. The process will start with the program prompting the user for the number of items that will be calculated. Then the program loops for the number of times that the user entered at the beginning, assigning the data entered into a member of an array we will declare. A variable called netTotal! will be displayed at the end of the program which will contain the total costs of the items. netTotal! will accumulate each time through the loop as the current totalCost! is added to it. Type the following code:
CLS
INPUT "How many items to be calculated? ", totalItems%
DIM itemName$(1 TO totalItems%) ' Declare our arrays
DIM itemCost!(1 TO totalItems%)
DIM numItems%(1 TO totalItems%)
DIM totalCost!(1 TO totalItems%)FOR i% = 1 TO totalItems% 'First loop: get inputs
CLS
PRINT "Item "; i% ' Display the current item number
INPUT "Item name -- ", itemName$(i%)
INPUT "Item cost -- ", itemCost!(i%)
INPUT "Quantity --- ", numItems%(i%)
totalCost!(i%) = itemCost!(i%) * numItems%(i%)NEXT i%
CLS
PRINT "Summary"PRINTformat$ = "\ \ $$#,###.## #,### $$#,###,###.##"PRINT "Item name Item Cost Quantity Total Cost "PRINT "----------------- ---------- -------- --------------"FOR i% = 1 TO totalItems%
PRINT USING format$; itemName$(i%); itemCost!(i%); numItems%(i%); totalCost!(i%)netTotal! = netTotal! + totalCost!(i%)NEXT i%
PRINT "Net Total = "; netTotal!
END
This program is much larger than anything we've done as of yet. It is kind of a review of everything we've done so far and one additional feature: the FOR...NEXT loop. This kind of loop loops for the number of times specified. A value is given to a variable and the program loops until that variable is equal or greater than the number specified after the TO.
FOR i% = 1 TO 10PRINT i%NEXT i%
Will loop 10 times, printing the numbers 1 through 10. The loop ends with a NEXT statement followed by the variable the loop increments for. So in our program, we have loops with index numbers (i%) starting at 1 and increasing for every number between 1 and the totalItems%, which is given by the user in the first part of the program. After the user inputs the number of items that will be calculated, four arrays are DIMensioned. They are one dimensional arrays, so they aren't very complex. The first FOR...NEXT loop prompts the user for each item. Then the format string is defined and the column headers are printed. The second FOR...NEXT loop cycles through the members of the four arrays and prints the data using the format string. The data for each member was assigned in the first FOR...NEXT loop. Each cycle through the second loop, the totalCost! of the current item being printed is added to the variable netTotal!. The netTotal! is the total sum of the total costs. After the second FOR...NEXT loop, the net total is printed and the program ends.
Say we have a game and when the user makes a record score, they get to write their name on a list of the 10 best scores. But the next time the user plays the game, we want the name and position they recorded the last time they played to still be there. To do this, we must write to what is called a file, and then read it again later. If you are computer literate, then no doubt you know what a file is, and since you are using the internet to read this, I'm assuming you are. If you don't know what a file is and you really want me to explain it, then email me and I will. So we need to write to file. Before you can do anything to a file, you must open it, and there are different ways you can open a file, believe it or not. A file can be opened so you can read from it or write to it, or it can be opened and split into records like a database. Here is a quick table of the different ways you can open a file:
Input: Read data from the fileOutput: Write data to the fileAppend: Write data to the end of a fileBinary: Read from or write to a file in binary modeRandom: Read from or write to a file which is split up in records like a database
The syntax for the OPEN statement is quite peculiar. It's arguments require us to specify a file name, an access type (the 5 types defined above), and a file number. When the file is open, QBasic recognizes it by a number which we assign to the file when we open it. All references made to the file use this number. It can be any number from 1 to 255. An open statement may look like this:
OPEN "sample.txt" FOR INPUT AS #1
We will be reading data from this file because it was opened for input. Back to our problem about the game scores. Lets set up a program which will ask for their name and give them a random score. Then it will put their name on the list at the appropriate place on the top 10 (if it makes the list). We'll call the file "top10.dat." But say when the user buys the game, there are already 10 names and scores in there. We write the following program to put default names and scores into top10.dat:
CLS
OPEN "top10.dat" FOR OUTPUT AS #1FOR i% = 1 TO 10playername$ = "Player" + STR$(i%)playerscore% = 1000 - (i% * 100)WRITE #1, playername$, playerscore%NEXT i%CLOSE #1PRINT "Data written to file"
END
There are a couple strange features of this program that we have not seen yet. In the second line of the program the file is opened for output so we can write to it. In the fourth line of the program we get into some light string manipulation. A name is generated from the word player, and is concatenated with string form of the current loop number. You can concatenate two strings by using the + operator. The STR$ function returns the string representation of the number passed to it. The opposite of the STR$ function is the VAL function, which returns the numeric value of the string passed to it. Lastly, the WRITE statement writes to the file number specified as the first argument the values of the following arguments. Data is written to file in a format readable by the INPUT # statement which we will use in the actual program. We need this short program for the big one to work so we can give the program data to read from, or else we will get a nasty INPUT PAST END OF FILE error when we try to run it. Note that the file should be closed when we are done with it by using the CLOSE statement followed by the file number.
And now we come to the big program, as I have referred to it. It is quite large and complex, and I have not fully described all the statements used in it, so I have broken it down to five sections which I will describe in detail afterwards. Here, at last, is the code:
' section 1CLSRANDOMIZE TIMERyourScore% = INT(RND * 1000)PRINT "Game Over"PRINT "Your score is "; yourScore%DIM playername$(1 TO 10) 'Declare arrays for the 10 entries on the listDIM playerscore%(1 TO 10)
' section 2OPEN "top10.dat" FOR INPUT AS #1DO WHILE NOT EOF(1) ' EOF means "end of file"i% = i% + 1INPUT #1, playername$(i%) 'Read from fileINPUT #1, playerscore%(i%)LOOPCLOSE #1PRINT
' section 3FOR i% = 1 TO 10IF yourScore% >= playerscore%(i%) THENFOR ii% = 10 TO i% + 1 STEP -1 'Go backwards (i% <>=) the current playerscore% being tested. If it is, then we have to shift each existing score below the current one down one to make room for the new score being added. The user is congratulated and prompted for their name. The loop is then exited using the EXIT FOR statement, which then goes to section 4.
Section 4: This short section simply opens the file for output so we can write to it. Then we write each of the members of the array to file.
Section 5: In this final section we define the format string, print the headers, and then print all the members of the top 10 and their scores. And that's the end of the program!
Now on to a new topic, which will later become related to the previous. User defined types. Recall that a type is the type of value a variable can have, such as integer, string, long, double, or single. You can create your own types which contain one or more of the already defined types. Here is an example of a user defined type:
TYPE employeeTypefirstname AS STRING * 30lastname AS STRING * 30age AS INTEGERwage AS SINGLEEND TYPE
We have defined a new type, which consists of four data members, as I call them. We can now declare a variable of this type:
DIM employee AS employeeType
A variable of a user defined type is like an array, in that it can have more than one value assigned to it. But you can have an array of a variable of a user defined type as well, so things can get rather complex. Anyway, now that you have a user defined type, you can assign values to the data members of that variable. Use a period to access a data member of a type, like this:
employee.firstname = "Bob"employee.lastname = "Foster"employee.age = 24employee.wage = 6.78
This could have been helpful in the last program we made with the top 10 list. We could have declared a user defined type called playerType, like this:
TYPE playerTypename AS STRING * 20score AS INTEGEREND TYPE
and then declared an array of variables of that type
DIM player(1 TO 10) AS playerType
That would have made our code more efficient, but not necessarily more readable. Notice when we declare a string in a user defined type that it seems as if we are multiplying it by a number. Actually, we the number after the * defines the maximum length of the string. You must define this because the size of a user defined type must be known by the computer. Any value assigned to this string data member which exceeds the length specified is truncated.
User defined types can serve more than to be efficient. They are the heart of the random access file mode, which is commonly used in database files. A database is a method of organizing large quantities of information in records and fields. In a record, there are a set of fields which are constant in every record. A field's value changes from record to record, however. Just the name of the field remains constant. So how does this relate to user defined types? Well think of a variable of a user defined type as a record in thedatabase, and the data members fields of the records. Employee may be a record, and firstname, lastname, age, and wage may be fields. Values can be assigned to the fields in each record, thus constructing a database. A file opened for random access is organized in this fashion, with records split into fields. Each record in the random access file is given a record number which can be convenient in a database environment. In the OPEN statement for opening a random access file there is one extra argument. We must specify the length in bytes of how much space one record will occupy -- the record length. This can be easily taken by taking the LENgth of a variable defined as the user defined type we are going to use. So back to our employee example, we could use the LEN function to get the size in bytes of the employee variable, which is an employeeType. Here's the code:
recordLen# = LEN(employee)OPEN "database.dat" FOR RANDOM AS #1 LEN = recordLen#
LEN stands for length. You can also use the LEN function to get the number of characters in a string, but that is kind of irrelevant right now. So let's construct a simple database that will keep track of the employees of a business.
' Section 1CLSTYPE employeeTypefirstname AS STRING * 30lastname AS STRING * 30age AS INTEGERwage AS SINGLEEND TYPEDIM employee AS employeeType
' Section 2PRINT "1.) Create new recordset"PRINT "2.) View existing recordset"INPUT "Which option? ", selection%
' Section 3IF selection% = 1 THEN INPUT "How many employees are in the company? ", numRecords%recordLen# = LEN(employee)OPEN "database.dat" FOR RANDOM AS #1 LEN = recordLen#FOR i% = 1 TO numRecords%CLSINPUT "First name: ", employee.firstnameINPUT "Last name: ", employee.lastnameINPUT "Age: ", employee.ageINPUT "Wage: ", employee.wagePUT #1, ,employee NEXT i%CLSCLOSE #1PRINT "Recordset creation complete"ENDEND IF
' Section 4IF selection% = 2 THENrecordLen# = LEN(employee)OPEN "database.dat" FOR RANDOM AS #1 LEN = recordLen#format$ = "\ \,\ \ ### $$##.##"PRINT "Last name First name Age Wage "PRINT "------------------ ------------------ --- -------"DO WHILE NOT EOF(1)GET #1, ,employee 'Sorry about the length of this line!!! PRINT USING format$; employee.lastname; employee.firstname; employee.age; employee.wageLOOPCLOSE #1ENDEND IF
I've split this program into sections again because that seems to work well for the larger ones.
Section 1: We're defining the user defined type and declaring a variable of that type.
Section 2: The first thing the user sees is a menu with the option to either create a new database (recordset) or view the existing one. The user is prompted to make a selection which is stored in the variable selection%.
Section 3: If the user chose option 1 -- create a new recordset -- then this code is executed. First we prompt the user for how many employees are in the company so we know how many times to go through a loop. Then we open the file, prompt the user for the data for each variable, and write the whole record to file. The record is written using the PUT statement. The first argument in PUT is the file number, the second is the record number, and the third is the data to be written to file. If no record number is specified for the second argument, the current file position is used, which will just append what we specify after what is already there. This works fine, so we don't need to worry about explicit record numbers. Notice that we are writing the whole employee variable to file. This is because we write records to file, and the whole variable contains the data for the data members (fields).
Section 4: If the user chooses to view the existing recordset, then we first open the file, define a format string for the printout, and print the headers. Next we have a loop until the end of file is encountered. Notice the GET statement, which is used to read from a random access file. The first argument is the file number we want to read from, the second is the record number (which we are leaving blank because we can read from the current position [CP] like we did in the PUT statement), and the third is the variable inwhich we read the data in to. This variable must be of the same type that we wrote with or else the types will be incompatible. You'd probably get a TYPE MISMATCH error if a different variable is used because the fields are not equal, so the program does not know what to assign the data to.
Well that's it for random access. If you have understood half of what I've said, feel good. You have a good knowledge of what QBasic is about. Now on to some more advanced programming!
GRAPHICS
Graphics programming in QBasic can get fairly complex. Lets start from the beginning. Your screen is made up of hundreds of pixels. The number of pixels horizontally and the vertically determines the resolution of your monitor. Right now, your monitor is set up in a video graphics mode which determines how many pixels can be displayed on screen. My resolution is set to 800x600 right now, but the most common is 640x480. Your graphics mode is determined by the screen resolution in pixels, the text resolution (how many lines and columns of text can fit on your screen), the number of pages of video memory, and the color palette. There are 13 screen graphics modes in QBasic, and each has its different purpose. You can look in the help index in QBasic for a listing of the screen graphics modes and their specifications. Each of the aspects of a screen graphics type can be changed to create effects.
There are a number of graphics routines used in QBasic which allow a variety of graphical effects. Lets try a few:
SCREEN 12LINE (0,0)-(640,480), 1CIRCLE (320, 240), 20, 2PSET (10,10), 14DRAW "c15 bm100,400 l5e5f5l5"END
The first line initializes the graphics mode to 12, which is 16 colors, 1 page of video memory, and 640x480 resolution.
LINE draws a line from one coordinate to another. The first optional argument after the coordinates (which are not optional) is the color. After that, a B ("box") or BF ("box fill") can be used to draw a box or a box filled with the color specified. The first coordinate can be omitted and the - left in to draw a line from the current graphics position (CP) to the relative coordinates specified. LINE -(100,0) will draw a line from the current graphics position to 100 pixels to the right.
CIRCLE draws a circle with the center at the coordinates specified. The first argument (required) after the coordinates is the radius of the circle. Then comes the color. After that, if you want to draw an arc, is the starting angle of the arc in radians, then the ending angle of the arc. To make an arc, first touch up on your geometry, then recall that to convert from degrees to radians is PI (3.14159265) divided by 180. The last argument is used if you want to make an ellipse, and is the ratio of the y axis to the x axis. So CIRCLE (320,240), 20, 2, 3.1415, 0, .5 would draw an elliptical green arc with the center at the middle of the screen, starting at 180 degrees (PI) and going to 0 degrees, with a compression ratio of 1 to 2 (x axis twice as big as the y). This looks like a wide smiley face mouth.
PSET fills a pixel at the screen coordinate you specify with the color you specify. In this case, yellow.Finally, the DRAW statement. The DRAW statement has it's own commands which I strongly suggest you memorize. When we get in to scaling and rotation you will need to know your draw commands pretty well. The draw command in the above code example can be read as "color 15 (white), move without drawing to screen coordinate 100,400, draw left 5 units, draw up and right 5 units, draw down and right 5 units, and draw left 5 units." In other words, a triangle. A unit is set by the current scale mode, which bydefault is 4. Since default scale mode is 4, one unit represents 4 pixels. So our triangle is 40 pixels wide at the base.
There are 16 defined colors in QBasic. The COLOR statement sets the current color for text output. I highly recommend memorizing the colors as well. Run this program:
SCREEN 12FOR i% = 0 TO 15COLOR i%PRINT "COLOR"; i%NEXT i%
This will print out the 16 colors used in QBasic. 0 is black, so that obviously won't show up. An quick reference for colors while you're in the QBasic IDE (integrated development environment) is to look under the OPTIONS DISPLAY menu. The colors listed there are in the QBasic order, starting with black and ending with bright white.
Now you know the basic graphics routines and their uses... lets make a couple programs that demonstrate them to a greater extent. First, a program which prompts the user for a radius, calculates the area and circumference, and draws the circle in a random color on the screen.
SCREEN 12RANDOMIZE TIMERCONST pi! = 3.1415DOCOLOR 15: INPUT "Radius (-1 to quit) --> ", radius!IF radius! = -1 THEN EXIT DOarea! = pi! * radius! ^ 2circum! = pi! * 2 * radius!COLOR 14PRINT "Area = "; area!PRINT "Circumference = "; circum!CIRCLE (320,240), radius!, INT(RND * 15 + 1)DO: LOOP WHILE INKEY$ = ""CLSLOOPCOLOR 9: PRINT "Good Bye!"END
We first set the screen graphics mode and generate a random seed number based on the system timer. Then we prompt for the radius in a vivid bright white, and test to see if we should end the program. We then calculate the area and circumference, and print the results in yellow. Then we draw the circle from the middle of the screen at the radius given in a random color. This random color is set by first generating a random number from 0 to 14, adding 1 to it, and converting it to an integer with the INT function. The next line seems weird. The INKEY$ statement reads the keyboard and returns the string representation of the key pressed. We are looping while INKEY$ is nothing, or in other words, while the user isn't pressing anything. The loop goes on forever until the user presses any key, and at this time a value will be given to INKEY$ which you might decide to use. The screen is then cleared for the next entry. If the user breaks the loop by entering -1 for the radius, we print Good Bye! in bright blue letters.
There are a lot more colors than just 16. In fact, you can change the values of each of the 16 colors to represent some other color that you specify. You do this with the PALETTE statement. The following applies to screen modes 12 and 13. This statement has two arguments: the color you want to change and the color you specify. Specifying a color is the hard part. Here is my version of the syntax of the palette statement
PALETTE color, blueValue * 256 ^ 2 + greenValue * 256 + redValue
color is the color you are changing. The _Values are numbers from 0 to 63 which specify the intensity of that color. You must use the multipliers after the values and use the addition operator to separate them. So lets make a program that fades the screen in and out, from black to purple. (blue and red make purple).
SCREEN 12DOFOR i% = 1 TO 63PALETTE 0, i% * 256 ^ 2 + i%NEXT i%FOR i% = 63 TO 1 STEP -1PALETTE 0, i% * 256 ^ 2 + i%NEXT i%LOOP WHILE INKEY$ = ""END
We start by changing the value of black (0), which is the background color to purple, from one degree of blue + red to the next. Then we bring it back down to black by decreasing the blue + red value. We do this over and over until the user presses a key or begins to have seizures.
Scaling and rotation can be accomplished quite easily with the DRAW statement, although it involves some weird looking code. First, lets define a shape that we can scale and rotate.
box$ = "bu5 l5 d10 r10 u10 l5 bd5"
Interpretation: "move up 5 spaces without drawing, draw 5 spaces left, draw 10 spaces down, draw 10 spaces right, draw 10 spaces up, draw 5 spaces left, and move 5 spaces down without drawing." This forms a box. Notice that I started at the center and not at a corner or side which would seem to be easier. Well, when you rotate something, it draws based on the starting point of the object, and we want it to rotate so if we put a pen at each corner of the box, it would draw a perfect circle. Therefore we set the center of the box as the starting point of the object. I call this the "object handle," not to be confused with the handle used in the Windows API. The ta draw command stands for "turn angle," and obviously turns the object in the degrees you specify. So if we turned the box from 0 to 360 degrees, drawing the box at each step and erasing the previous image, we would get a rotating box. But we need one more function: VARPTR$. VARPTR$ stands for "variable pointer," a term you can completely ignore unless you get into C or Assembly programming. We need to somehow get the box$ shape into the draw string command we use implement in the loop, so we have to take the address of the object string and plug it into the draw string. This can be accomplished by using the X command, which tells VARPTR$ where to plug in the string's address so it can be used. With box$ defined above, here's the code for a rotating box:
DOangle% = angle% + 1IF angle% >= 360 THEN angle% = 1DRAW "c0 bm320,240 ta" + STR$(angle% - 1) + "X" + VARPTR$(box$)DRAW "c1 bm320,240 ta" + STR$(angle%) + "X" + VARPTR$(box$)LOOP WHILE INKEY$ = ""END
Not that hard is it? We draw the box at the previous angle in black, and then draw the box at the current angle in blue.
Scaling is done pretty much the same way, but instead of changing the angle and erasing the previous image, we change the scale factor and erase the previous image. Recall that the default scale factor for the DRAW statement is 4 pixels per unit. Well, if we increase this factor then we will have more pixels per unit, thus giving the image the effect of enlargement. So if we set up a FOR...NEXT loop which will increase the scale factor from 2 to, say, 200, we will get the effect of scaling. But lets start with a smaller image which is maybe 8 pixels wide from the start instead of 40 so we get a more dramatic effect.
SCREEN 12box$ = "bu4 l4 d8 r8 u8 l4 bd4"FOR s% = 2 TO 200 DRAW "c0 bm320,240 s" + STR$(s% - 1) + "X" + VARPTR$(box$)DRAW "c2 bm320,240 s" + STR$(s%) + "X" + VARPTR$(box$)NEXT s%END
Notice what we're doing here. We are starting the scale factor at one half of default (2) because the FOR...NEXT loop starts with s% at 2. The s draw command sets the scale factor. Notice also that we must continuously anchor the object handle at a point to keep it scaling about the handle. We do this by moving the object handle to 320,240 (center of screen) each time through the loop. Whenever we want to put a number into the draw string, we must concatenate the string format (STR$) of the number within the string. Instead of concatenating the box$ with the rest of the string, it is faster to only pass the address of the substring with the VARPTR$ function.
So what if you want to scale and rotate something at the same time? Simple, just set up a FOR...NEXT loop which increases the scale factor as before, and within the loop increase the angle. But instead of subtracting a factor for the angle to erase the previous angle, lets do it this way: erase the previous image with the current angle, increase the angle, then draw the current image with the new current angle. This way if we want to change the factor at which the angle increases, we will only have to change one number instead of two
SCREEN 12box$ = "bu4 l4 d8 r8 u8 l4 bd4"FOR s% = 2 TO 250DRAW "c0 bm320,240 ta" + STR$(a%) + "s" + STR$(s% - 1) + "X" + VARPTR$(box$)a% = a% + 1IF a% >= 360 THEN a% = 1DRAW "c1 bm320,240 ta" + STR$(a%) + "s" + STR$(s%) + "X" + VARPTR$(box$)NEXT s%END
Try it out! Draw strings can get fairly complex, but you'll get used to them with practice and when you memorize the draw string commands.
The screen coordinates for different screen modes can be fairly difficult to work with, and they do tend to be weird numbers. To make your code simpler to write, you can define a logical plane over the physical plane. An example of a physical plane is the 640x480 resolution established by the SCREEN 12 screen mode. You can define a logical, or alternate user-defined plane with the WINDOW statement.
SCREEN 12WINDOW (0,0)-(100,100)CIRCLE (50,50),10,4LINE (0,0)-(50,50),2END
This trivial example defines a logical plane which is 100x100. 50,50 is now the center of the screen, so this draws a red circle from the center with a radius of 10. The line statement draws a green line from the lower left corner to the center of the screen. Notice that defining a logical plane sets the origin (0,0) to the bottom left of the screen, instead of the default upper left. If you want the origin to be in the upper left with a logical plane, add the SCREEN keyword after WINDOW. So to define the graphics mode 12 screen resolution, the code is:
SCREEN 12WINDOW SCREEN (0,0)-(640,480)
Use whatever is more comfortable, but I would recommend using WINDOW SCREEN because there is less confusion when converting from logical to physical planes.
Finally, a little information on creating DRAW effects with the other QBasic graphics routines. Hope you know some trigonometry for this part. Recall that in the unit circle, which has a radius of 1, that the coordinates of a point on the circle given an angle is defined as ( COS(angle), SIN(angle) ). Furthermore, if we are given a point on the circle, we can find the angle by drawing a vertical line perpendicular to the x axis from the point. If we take the arctangent of the vertical length of this line divided by the horizontal distance of this line from the origin, we will get the angle. So the angle is defined as ATN(Y/X). With this knowledge, it would be possible to create a spinning line using only the line command. If we create a loop which increments the angle from 0 to 360 then we can take to COS,SIN of the angle to get the point we should draw to. But there's only one more problem. The QBasic functions COS and SIN think in radians, so we must first convert the angle to radians by multiplying PI / 180. That is quite easily done . Here is the code:
SCREEN 12CONST PI = 3.1415WINDOW SCREEN (-1,1)-(1,-1)DOLINE (0,0)-(COS(a% * PI / 180),SIN(a% * PI / 180)), 0a% = a% + 1IF a% >= 360 THEN a% = 1LINE (0,0)-(COS(a% * PI / 180),SIN(a% * PI / 180)), 14LOOP WHILE INKEY$ = ""END
We start by initializing the graphics mode, then defining PI as a constant - a variable which will never change in the program execution. Then define the logical plane, and start the loop. The line starts from the center of the screen and goes to the coordinate specified by the COS,SIN of the angle. We loop until the user presses a key.
There is one more type of graphics that QBasic has a strong point for : text. Graphical effects can be made quite easily using only text in QBasic. There are a few functions that are quite useful when dealing with text. The first is the CHR$ function. If you pass a number to the CHR$ function, it will return the ASCII (American standard code for information interchange) text value of that number. To find a listing of the ASCII character codes, look in the help contents, and there is a listing there. For example, to print a smiley face on the screen, the code is this:
CLSPRINT CHR$(1)END
Since the ASCII character code for a smiley face is 1, you can use the CHR$ function to get this. Another useful function is ASC, which returns the ASCII value of a text value you pass to it. So ASC("A") will return 65 because the ASCII value of A is 65. Every printable character (and then some) have an ASCII value, so these two functions make it quite easy.
Finally, the LOCATE statement is extremely useful for any text based program. LOCATE sets the text CP to the coordinates you specify. The first argument is the column, and the second is the row. So
CLSLOCATE 5,10PRINT CHR$(219)END
Will print a solid white block at column 5, row 10. And that's it for graphics! You now know nearly every graphics routine in QBasic, and have the knowledge to make a game or highly graphical program. Graphics depend on how you arrange them, so it requires an artistic skill to some degree. If you get creative with these graphics commands, you can create nearly any effect you need.
It is not practical in real world terms to set up an application in one long list of code. Many early programming languages were purely linear, meaning that they started from one point on a list of code, and ended at another point. All of the code I have written in this tutorial so far has been purely linear. However, linear programming is not practical in a team environment. If one person could write one aspect of code, and another write another part of the program, things would be much more organized. QBasic contains the capability to meet these needs, called modular programming. You can break a program into different "modules" which are separate from the main program and yet can be accessed by any part of it. I highly recommend the use of separate modules in programming applications, although it is not a simple task to learn.
These separate modules are also known as procedures in the QBasic environment. There are two types of procedures: subs and functions. Subs merely execute a task and return to the main program, which functions execute a task and return a value to the main program. An example of a sub might be a procedure which displays a title screen on the screen, while a function may be a procedure that returns a degree in degrees given a number in radians. Function procedures are also used in Calculus, so you Calculus people should already be familiar with functions.
Procedures can accept arguments in what is called an argument list. Each argument in the argument list has a defined type, and an object of that type must be passed to the procedure when it is called. For example, the CHR$ QBasic function accepts a numeric argument. The function itself converts this numeric argument into a string representation of the ASCII value of the number passed, and returns this one character string.
Procedures in QBasic are given their own screen. When you enter the QBasic IDE, you are in the main procedure which can access all the others. Other procedures are created by typing the type of procedure (SUB or FUNCTION), the procedure name, followed by the complete argument list. You can view your procedures through the VIEW menu. Here is an example of a sub procedure which performs some operations for a program that will be using graphics, random numbers, and a logical plane.
SUB initProgram()RANDOMIZE TIMERSCREEN 12WINDOW (0,0)-(100,100)COLOR 15END SUB
The only thing you need to type is SUB initProgram (), and the screen will be switched to that procedure. The END SUB is placed there for you, so the only thing you need to type then is the code within the sub. Try typing this out on your own to see how this works. This procedure is called by simply typing initProgram in the main procedure. An alternative method is CALL initProcedure (). Right here the parentheses are optional, but if you were to pass arguments to the procedure, parentheses would be required with the CALL statement. Now lets try passing an argument to a procedure. We will pass two arguments to a procedure called center which are a string containing the text to be centered, and the horizontal location on the screen at which you wish to center it.
SUB center( text$, hLoc% )LOCATE hLoc%, 41 - (LEN(text$) / 2)PRINT text$END SUB
The first line after the sub declaration positions the starting point of the text at the horizontal location we passed at the second argument and vertical coordinate. The vertical coordinate is calculated by subtracting one half the screen's width in characters (41) and half the LENgth of the text we passed as the first argument. We would call center from the main procedure like this:
center "Programmed by qp7@pobox.com", 12
Or like this
CALL center ("Programmed by qp7@pobox.com", 12)
It's quite simple actually. Functions are slightly different and involve an additional part which subs do not: a return value. The return value is specified by assigning the value you want to return to the function name from within the function definition. When calling the function from within the main procedure, the name of the function is treated as a value which is evaluated at compile-time. Here is an example of a function definition:
FUNCTION convert.To.Radians (degree!)
LET PI = 3.1415convert.To.Radians = degree! * PI / 180
END SUB
The function is implicitly called in this program
CLSINPUT "Enter a value in degrees: ", degreeValue!radianValue! = convert.To.Radians(degreeValue!)
PRINT "The radian equivalent is"; radianValue!; "radians"
END
We treat the value returned from the function as a value we can immediately assign to another value. The variable radianValue! is given the value returned from convert.To.Radians. These concepts are supported in all programming languages, so this information will be beneficial to you in the future.
There is one final concept which has proven to be very successful in programming: a message loop. With QBasic, you can construct a loop which runs for the length of the program, receives input from the user, and executes a message based on what the user does. We will construct a basic application which receives input from the user in the form of an arrow key, and moves a box on the screen based on the direction the user pressed. The arrow keys are different from normal inputted keys received with INKEY$. On the enhanced 101 keyboards which have arrow keys, INKEY$ returns two values: the ASCII text epresentation of the key pressed, and the keyboard scan code of the key pressed. Since the arrow keys do not have an ASCII text representation, we must use the keyboard scan codes for them. The keyboard scan codes can be viewed in the HELP CONTENTS section of the QBasic menus. For this program, we will have two procedures in addition to the main procedure. The first will initialize the program settings and position the character in his starting position. The other will move the guy in the direction which we pass to the function. The main procedure will call the sub procedures and contains the main message loop which retrieves input from the user. First of all, here is the code for the main procedure:
CONST UP = 1CONST DOWN = 2CONST LEFT = 3CONST RIGHT = 4
TYPE objectTypex AS INTEGERy AS INTEGEREND TYPEDIM object AS objectTypeinitScreenobject.x = 41object.y = 24
DOSELECT CASE INKEY$CASE CHR$(0) + CHR$(72): move UP, objectCASE CHR$(0) + CHR$(80): move DOWN, objectCASE CHR$(0) + CHR$(75): move LEFT, objectCASE CHR$(0) + CHR$(77): move RIGHT, objectCASE CHR$(32): EXIT DOEND SELECTLOOP LOCATE 1,1: PRINT "Thank you for playing"
END
This code is fairly self explanatory with the exception of the SELECT CASE... END SELECT structure which I have not yet explained. This type of conditional testing format tests a condition, and several cases for that condition are then tested. In this case, we are seeing IF INKEY$ = CHR$(0) + CHR$(72), IF INKEY$ = CHR$(0) + CHR$(80), and so on. This is just a more legible format than IF...THEN...ELSE. Note that in the QuickBasic compiler, a CASE ELSE statement is required in the structure for what reason I have no idea. The above code is the driver for the rest of the program. First some CONSTants aredeclared which remain constant for the duration of the program and in any module. A user defined type is declared to store the coordinates of the character. Then an endless loop is executed, calling the appropriate procedure for the arrow key pressed until the user presses the space bar (CHR$(32)). Here is the code for the initScreen procedure:
SUB initScreen ()
SCREEN 12COLOR 9WIDTH 80,50LOCATE 24,41PRINT CHR$(1)
END SUB
The WIDTH 80,50 statement sets the screen text resolution to 80 columns and 50 rows. We then print a smiley face in the middle of the screen in a nice bright blue color. Next we need to write the move procedure, and then we will be done with the program.
SUB move (way AS INTEGER, object AS objectType)
LOCATE object.y, object.xPRINT CHR$(0) ' erase previous image SELECT CASE wayCASE UP: IF object.y > 1 THEN object.y = object.y - 1CASE DOWN: IF object.y < y =" object.y"> 1 THEN object.x = object.x - 1CASE RIGHT: IF object.x < x =" object.x">




