|
5. Saving us Some Typing
Previous | Next
If you did the previous exercise, you will probably have noticed that you had to retype
lots of lines over and over again (Or maybe you just had to copy and paste them a couple of
times). You will probably agree that such copying and pasting seems awfully wasteful. After
all, in a large program, you could end up using a huge amount of disk space for thousands of
copies of the same code. Not to mention that if you discovered a mistake in one of the copies,
you'd have to manually fix all the others. Wouldn't it be cool if we could just write this
once in the code and then tell the compiler just to use the same code as above? As a matter
of fact, we can!
Go to the top of your "Main.c" file, and write the following on a new line
between the #include lines and the main() function:
int GetArgument( bool isLeft )
{
int vNum;
if( isLeft )
printf( "Enter left argument: " );
else
printf( "Enter right argument: " );
scanf( "%d", &vNum );
fpurge( stdin );
return vNum;
}
Yes, it is a second function, just like "main". The first thing you will notice is that we added a "bool isLeft" between the
brackets. That is a parameter. You can think of a parameter as a mailbox. When you
call upon this function to do its work, you can drop something into this mailbox and
the function can then "read its mail" and use the information you sent it to
perform its work differently.
As far as the body of the function GetArgument() is concerned, isLeft is simply another variable.
The only exception is that it already had a value assigned to it (How you do that will be
explained below). That's why I can simply use isLeft in my "if" even though I
never put anything into it. You define a parameter pretty much like a variable: You
specify its type and then whitespace and its name. To define several parameters,
separate them with commas, but be aware that you can't define parameters of the same
type without specifying their data type three times. I.e. it must be:
int name( int a, int b, int c )
So, what this function does is ask for the left parameter if isLeft is true, for the right
parameter if it isn't, and then it reads a number from the keyboard and stashes the number read
into the local variable vNum. And then it makes the contents of this variable, vNum, its "return
value" (more on that follows).
Great, now we've added another 9 lines ...
Sure. But what we just did is that we defined our own command. Our own kind of
instruction, which we can use in C. Just like printf() or scanf(). So, whenever we need to read an argument, where
we used to write:
printf( "Enter left argument: " );
scanf( "%d", &vFirstArg );
fpurge( stdin );
we can now write:
vFirstArg = GetArgument(true);
So, what does this do?
Well, the half to the right of the equals sign ("GetArgument(true)") causes the program
to "jump" from the current line to the GetArgument() function we just defined,
and execute the commands in it. The true in brackets behind the function's
name is a parameter value, which will go in the variable for the corresponding parameter.
When you write something like:
foo = name( 1, 2, 3 );
and the function name looks like:
int name( int a, int b, int c )
The compiler will automatically assign 1 to a, 2 to b, and 3 to c. So, that's how easy it
is to get data into our new function. Now, since GetArgument() above is intended to do the
same as our old code, we also have to get data back from our function, namely, we have to
write the number the user entered into vFirstArg. How do we do that?
Well, as you may remember from the start of our course, a function can have a return value. You already
know that, by using the return vNum; statement in the last line of our function above, we
are handing a value to whoever called us. In the case of main(), the operating system received
the return value. But what happens when our main() function calls our function?
What happens is that the computer simply pretends the return value of our function was
there *instead* of our call to the function. That is, if the user entered a "15" as the left
argument, our line:
vFirstArg = GetArgument(true);
becomes equivalent to
vFirstArg = 15;
Summarised, this means that wherever you write a function in an expression, the computer
will first execute all the commands you give it inside the definition of your function, and
then it will continue executing the expression, using your function's return value in its place.
So, what you can do now, is replace all occurrences of
printf( "Enter left argument: " );
scanf( "%d", &vFirstArg );
fpurge( stdin );
with:
vFirstArg = GetArgument(true);
and all occurences of
printf( "Enter right argument: " );
scanf( "%d", &vSecondArg );
fpurge(stdin);
with:
vSecondArg = GetArgument(false);
And your program should work just like before, the difference being that you saved a
couple of lines and your code has become a tiny bit more readable.
 Some of you may wonder why we aren't simply writing
vSecondArg = vNum;
at the end of our GetArgument() function. There is a simple reason for this: Variables
defined inside curly brackets only exist as long as the computer is executing commands
between these curly brackets. Since every function's list of commands is enclosed in
curly brackets, nobody outside these brackets can use these variables.
"But wait," you may say now, "main() calls GetArgument(). That must
mean that since the computer has simply taken a detour into GetArgument(), main()'s
variables must exist!" True, they do exist. However, if you were really allowed
to access the variables of the function that called you, this would mean that a careless
change in a function could actually change the behavior of the function being called...
We wouldn't want that.
For this reason, and for various other reasons I'll hopefully have the opportunity to
go into later, the inventors of C decided that variables defined inside a function are
local, and can only be seen by the function itself, not by functions it calls, and not
by the functions calling it.
As I mentioned above, the parameters to a function are just another local variable as far
as your function is concerned. That also means that they can't really be seen from the
outside. So, how does this work? Well, although they really are just local variables,
they are actually a little special after all. By defining them between the brackets
after the function name, you tell the computer to copy the parameters there.
Yes, you heard right, they are copied. What this means, is that you can
change these variables all you like, it will not change the value of the parameter that
was passed in. Which also explains why we need a return value: Because otherwise we
would have no way to get some information back to the function that called us.
Several input values and only one output value may seem a little limited, but don't worry, we'll talk about ways to get around this in a couple of chapters.
Previous | Next
Snakeman555 writes: I am having some problems
first here is my code:
#include <stdio.h>
#include <stdbool.h>
int GetArgument( bool isLeft )
{
int vNum;
if( isLeft )
printf( "Enter first number: ");
else
printf( "Enter second number: ");
scanf( "%d" , &vNum );
fpurge( stdin );
return vNum;
}
int main()
{
int vFirstArg,
vSecondArg;
char vOperation;
bool vFinished;
//Make sure our flag is initialized!
vFinished = false;
//Now loop until our flag is initialized!
while( vFinished != true )
{
printf( "What operation do you want to do?(+,-,*,/)\n" );
scanf( "%c" , &vOperation );
fpurge( stdin );
if( vOperation == '+' )
{
vFirstArg = GetArgument(true);
vFirstArg = GetArgument(false);
printf( "\n%d + %d = %d\n" ,
vFirstArg,
vSecondArg,
vFirstArg + vSecondArg );
}
else
{
if( vOperation == '-' )
{
vFirstArg = GetArgument(true);
vFirstArg = GetArgument(false);
printf( "\n%d - %d = %d\n" ,
vFirstArg,
vSecondArg,
vFirstArg - vSecondArg );
}
else
{
if( vOperation == '*' )
{
vFirstArg = GetArgument(true);
vFirstArg = GetArgument(false);
printf( "\n%d x %d = %d\n" ,
vFirstArg,
vSecondArg,
vFirstArg * vSecondArg );
}
else
{
if(vOperation == '/' )
{
vFirstArg = GetArgument(true);
vFirstArg = GetArgument(false);
if(vSecondArg == 0)
{
printf("You can't divide by zero stupid!\n");
}
else printf( "\n%d / %d = %d\n" ,
vFirstArg,
vSecondArg,
vFirstArg / vSecondArg );
}
else vFinished = true;
}
}
}
}
printf( "Finished.\n" );
return 0;
}
and this is what it is doing
What operation do you want to do?
-
Enter first number: 7
Enter second number: 5
5 - -1881139893 = 1881139898
What operation do you want to do?
+
Enter first number: 9
Enter second number: 9
9 + -1881139893 = -1881139884
What operation do you want to do?
|
Uli Kusterer replies: ★ Snakeman, you're doing vFirstArg = GetArgument(true) and then vFirstArg = GetArgument(false). So, you put the first argument in vFirstArg, then overwrite it with the second argument, then calculate using vFirstArg (containing the second argument) and vSecondArg (which has never been assigned a value, and thus just contains a garbage number.
|
David Hunter writes: I did the same thing at first.
you have:
if( vOperation == '+' )
{
vFirstArg = GetArgument(true);
vFirstArg = GetArgument(false);
in each of your operations, you want to have:
if( vOperation == '+' )
{
vFirstArg = GetArgument(true);
vSecondArg = GetArgument(false);
otherwise you are setting vFirstArg and then resetting it and not defining vSecondArg.
See what I mean, I did the same thing some how.
|
Charles Marshall writes: I'm confused. I was under the impression that the compiler (or computer, not sure which term to use here) reads the code in our file from top to bottom. And a lesson or two ago you said a boolean variable needed to be initialized. I don't see where this is happening, unless the computer isn't reading "GetArgument" first. Is the parameter variable getting initialized from the "vFirstArg=GetArgument(true);" line?
For that matter, could the entire "GetArgument" block be somewhere else, like at the bottom of the file?
|
Uli Kusterer replies: ★ Charles, these are two separate things: The program needs to make sense to the compiler when read from top to bottom in the sense that you can't use anything that hasn't been mentioned before. The compiler has to know what GetArgument() is before you can use it. However, all that is needed when the compiler turns our C text into actual machine code. Once that is done, the compiler knows where the main() function is, etc., so it doesn't need to do everything in order, it can start at main() directly.
The part about *initializing* a variable is necessary when you *run* the program. If you do not provide some initial value, it will contain garbage. Since a parameter (like isLeft in the case of GetArgument()) is a sort of "mail box" into which values from outside come into the function, you can't give it an initial value inside the function (after all, then that would replace whatever was put in it from the outside). Instead, you do that in main() or wherever else you call GetArgument, by writing the value you want in isLeft between the brackets after the function name.
More questions?
|
Uli Kusterer replies: ★ Charles,
it may help to look at the movie again. You see that the parameter, isLeft, is initialized with the "true", that flies over from where we call it. This happens because the "int isLeft" was written between the brackets, not between the curly brackets where we put "normal" local variables. Regular local variables only exist inside the function and cease to exist once we have returned to the calling function (in this case, main()). Parameters are special, and hence go between the brackets to tell the compiler that is where we want our stuff from outside to come in. Since the stuff from outside gets put in them, there's no need to initialize them any further.
|
Charles Marshall writes: Thanks, Uli! Okay. I think I was confusing "compiling" and "running". So let me see if I got this straight: the compiler needs to see everything in order from top to bottom or else it wouldn't know that GetArgument() existed. And when the program runs, it does so directly from main(). The various "vFirstArg=GetArgument(true);" lines in main(), in effect, 'initialize' the boolean "isLeft" in the GetArgument() function (setting the value to true or false as the case may be). The end result is the return value stored in vNum which 'replaces' the right side of the "vFirstArg=GetArgument(true);".
Is that right?
Also, would it be safe to describe the entire GetArgument() function as a sort of "macro"?
(maybe I shouldn't go there...)
One final thing: with an "if" statement, it seems from this example that a boolean condition doesn't have to be written out like an equation (condition=true) but the condition can simply be a boolean variable defined (maybe elsewhere in the code) as "true" for the "if" command to take place. Do I have that right?
|
Charles Marshall writes: I thought this seemed like a logical way to economize even more:
if(myOperator=='+')
printf("%d + %d = %d\n", GetArgument(true), GetArgument(false), GetArgument(true)+GetArgument(false));
And although it eventually works, it's obviously not correct because it requires typing the arguments twice in the console:
Please select a function (+,-,*,/ or q to quit):+
Please input your first number:3
Please input your second number:4
Please input your second number:4
Please input your first number:3
3 + 4 = 7
Can you tell me what's happening here? or should I just stop messing around and move on to the next lesson? ;)
|
Uli Kusterer replies: ★ Charles, what do you expect to happen? *every* occurrence of a function that you write in an expression causes that function to be run once. Your line contains four calls to GetArgument(), so the user will be asked four times. I guess you just optimized to far :-)
|
Charles Marshall writes: I thought I was trying to be too clever! GetArgument() is a function... I knew that. Onwards into the Void!
Thanks Uli!
|
Garotas* writes: One question about the function getArgument(). Why can't I put it after main()?
|
steven writes: PS, like the Captcha phrase. Usually, I hate captchas, but this was good
anyway, I am having trouble with division producing amazing long strings.
the code is :
else if (vOperation=='/')
{
vFirstArg=GetArgument(true);
vSecondArg=GetArgument(false);
if (vSecondArg==0) {
printf("This would cause a division by zero error. Also, Proffessor Friedman's arguments are never null \n");
vSecondArg=GetArgument(false);
}
printf("\n%f / %f = %f\n",
vFirstArg,
vSecondArg,
vFirstArg/vSecondArg);
}
else {
Anyway, the question is, is C so strongly typed that I can't massage D into F at run time?
|
MathNinja writes: What is the range of integers that we can use for this crude calculator?
|
Benoît writes: Garotas*, because C's design doesn't allow you to call a function unless you've already declared it earlier in the file. To solve this problem, you can state the return type, the name, and the arguments (if any) of GetArgument() at the top, then define it (implement it with actual code) after the main function.
MathNinja, if the variable is defined as an unsigned int, which is 4-byte long, it can hold a number up to +2^32-1. A signed int, capable of representing negative integers, is in the range of -2^16 to +2^16-1.
|
CMacRun writes: Uli,
First - I've done some surfing on the web for tutorials on C, and yours is definitely a cut above the rest!
I am having problem with my code after I added in the GetArgument stuff. When I compile and run, it stops after I input the answer to the first question about +,-,/, etc.
Here is my code. Thanks for your help!
#include <stdio.h> // Defines printf etc.
#include <stdbool.h>
int GetArgument( bool isLeft )
{
int vNum;
if (isLeft )
printf("Enter 1st number: ");
else
printf("Enter 2nd number: ");
scanf( "%d", &vNum);
fpurge(stdin);
return vNum;
}
int main()
{
int vFirstArg,
vSecondArg;
char vOperation;
bool vFinished;
// Make sure our flag is initialized!
vFinished = false;
// Now loop until user doesn't want anymore:
while( vFinished != true)
{
printf( "Welcome to my calculator! Would you like to +, -, * or /?\n" );
scanf( "%c", &vOperation );
fpurge( stdin );
if( vOperation == '+' )
{
vFirstArg = GetArgument(true);
vSecondArg = GetArgument(false);
printf( "\n%d + %d = %d\n",
vFirstArg,
vSecondArg,
vFirstArg + vSecondArg );
}
else
{
if ( vOperation == '-')
{
vFirstArg = GetArgument(true);
vSecondArg = GetArgument(false);
printf( "\n%d - %d = %d\n",
vFirstArg,
vSecondArg,
vFirstArg - vSecondArg);
}
else
{
if (vOperation == '*')
{
vFirstArg = GetArgument(true);
vSecondArg = GetArgument(false);
printf( "\n%d * %d = %d\n\n",
vFirstArg,
vSecondArg,
vFirstArg * vSecondArg);
}
else
{
if (vOperation == '/')
{
vFirstArg = GetArgument(true);
vSecondArg = GetArgument(false);
if (vSecondArg == 0)
{
printf("\nHello! You can't divide by zero. Are you trying to crash my program? \nPick a new number!");
scanf("%d", &vSecondArg);
fpurge(stdin);
printf("\n%d / %d = %d\n\n",
vFirstArg,
vSecondArg,
vFirstArg / vSecondArg);
}
else
printf("\n%d / %d = %d\n\n",
vFirstArg,
vSecondArg,
vFirstArg / vSecondArg);
}
else
vFinished = true;
}
}
}
}
printf( "Finished.\n" );
return 0;
}
|
CMacRun writes: Uli,
Sorry, false alarm. I realized I was hitting "enter" instead of "return". It runs great now.... |
|
Home | Admin | Edit
Last Change: 2010-03-10 @809, © 2003-2010 by M. Uli Kusterer, all rights reserved. |
|
|