| I l @ ve RuBoard |
So far, these programs have used the standard
printf()
function. Listing 2.3 shows you how to
| I l @ ve RuBoard |
| I l @ ve RuBoard |
So far, these programs have used the standard
printf()
function. Listing 2.3 shows you how to
/* two_func.c -- a program using two functions in one file */
#include <stdio.h>
void butler (void); /* ANSI C function prototyping */
/* pre-ANSI C uses void butler(); instead */
int main(void)
{
printf(I will summon the butler function.\n);
butler();
printf(Yes. Bring me some tea and writeable CD-ROMS.\n);[sr]
return 0;
}
void butler(void) /* start of function definition */
{
printf("You rang, sir?\n");
}
The output looks like this:
I will summon the butler function. You rang, sir? Yes. Bring me some tea and writeable CD-ROMS.
The
butler()
function appears three times in this program. The first appearance is in the
prototype
, which informs the compiler about the functions to be used. The second appearance is in
main()
in the form of a
function call
. Finally, the program
Pre-ANSI C supports a more limited form of function declaration in which you just specify the return type but omit describing the arguments.
void butler();
Older C code uses function declarations like the
Finally, the function
butler()
is defined in the same manner as
main()
, with a function header and the body
One point to note is that it is the location of the butler() call in main() ”not the location of the butler() definition in the file”that determines when the butler() function is executed. You could, for instance, put the butler() definition above the main() definition in this program, and the program would still run the same, with the butler() function executed between the two calls to printf() in main() . Remember, all C programs begin execution with main() , no matter where main() is located in the program files. However, C practice is to list main() first because it normally provides the basic framework for a program.
The ANSI C standard recommends that you provide function prototypes for all functions you use. The standard include files take care of this task for the standard library functions. For example, under ANSI C, the stdio.h file has a function prototype for printf() . When we introduce defining functions with return values in Chapter 6, "C Control Statements: Looping," we'll show you how to extend prototyping to non- void functions.
Now that you can write a simple C program, you are in a position to make simple errors. Listing 2.4 presents a program with some errors. See how many you can spot.
/* nogood.c -- a program with errors */
#include <stdio.h>
int main(void)
(int n, int n2, int n3;
/* this program has several errors
n = 5;
n2 = n * n;
n3 = n2 * n2;
printf(n = %d, n squared = %d, n cubed = %d\n", n, n2, n3)
return 0;)
Listing 2.4 contains several
syntax errors
. You commit a syntax error when you don't follow C's rules. It's analogous to a
So what syntax errors did nogood.c make? First, it uses parentheses instead of braces to mark the body of the function”it uses a valid C symbol in the wrong place. Second, the declaration should have been this:
int n, n2, n3;
or perhaps this:
int n; int n2; int n3;
Next, the example omits the */ symbol pair necessary to complete a comment. Finally, it omits the mandatory semicolon that should terminate the printf() statement.
How do you detect syntax errors? First, before compiling, you can look through the source code and see if you spot anything obvious. Second, you can examine errors found by the compiler because part of its job is to detect syntax errors. When you compile this program, the compiler
However, the compiler can get
Semantic errors are errors in meaning. For instance, consider the following sentence: Furry inflation thinks greenly . The syntax is fine because adjectives, nouns, verbs, and adverbs are in the right places, but the sentence doesn't mean anything. In C, you commit a semantic error when you follow the rules of C correctly but to an incorrect end. The example has one such error:
n3 = n2 * n2;
Here, n3 is supposed to represent the cube of n , but I've set it up to be the fourth power of n .
The compiler does not detect semantic errors, for they don't
/* stillbad.c -- a program with its syntax errors fixed */
#include <stdio.h>
int main(void)
{
int n, n2, n3;
/* this program has a semantic error */
n = 5;
n2 = n * n;
n3 = n2 * n2;
printf("n = %d, n squared = %d, n cubed = %d\n", n, n2, n3);
return 0;
}
Its output is this:
n = 5, n squared = 25, n cubed = 625
If you are cube-wise, you'll see that 625 is the wrong value. The next stage is to track down how you wound up with this answer. For this example, you probably can spot the error by inspection. In general, however, you need to take a more systematic approach. One method is to
The body of the program starts by declaring three variables:
n
,
n2
, and
n3
. You can simulate this situation by drawing three boxes and labeling them with the variable
Well, perhaps this procedure is overkill for this example, but going through a program step-by-step in this fashion is often the best way to see what's going on.
By tracing the program step-by-step, keeping track of each variable, you monitor the program state. The program state is simply the set of values of all the variables at a given point in program execution. It is a snapshot of the current state of computation.
We just discussed one method of tracing the state: executing the program step-by-step yourself. In a program that makes, say, 10,000 iterations, you might not feel up to that task. Still, you can go through a few iterations to see if your program does what you intend. However, there is always the possibility that you will execute the steps as you intended them to be executed instead of as you actually wrote them, so try to be faithful to the actual code.
Another approach to locating semantic problems is to sprinkle extra printf() statements throughout to monitor the values of selected variables at key points in the program. Seeing how the values change can illuminate what's happening. After you have the program working to your satisfaction, you can remove the extra statements and recompile.
A third method for examining the program states is to use a debugger. A
debugger
is a program that enables you to run another program step-by-step and examine the value of that program's variables. Debuggers come in various levels of ease of use and sophistication. The more advanced
| I l @ ve RuBoard |