Masters of the Void [The Void]
5. Saving us Some Typing

Previous | Next

If you did the previous exercise, you will probably have noticed that you had to retypelots of lines over and over again (Or maybe you just had to copy and paste them a couple oftimes). You will probably agree that such copying and pasting seems awfully wasteful. Afterall, in a large program, you could end up using a huge amount of disk space for thousands ofcopies 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 thisonce in the code and then tell the compiler just to use the same code as above? As a matterof fact, we can!

Go to the top of your "Main.c" file, and write the following on a new linebetween 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 thebrackets. That is a parameter. You can think of a parameter as a mailbox. When youcall upon this function to do its work, you can drop something into this mailbox andthe function can then "read its mail" and use the information you sent it toperform 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 beexplained below). That's why I can simply use isLeft in my "if" even though Inever put anything into it. You define a parameter pretty much like a variable: Youspecify 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 sametype 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 rightparameter if it isn't, and then it reads a number from the keyboard and stashes the number readinto the local variable vNum. And then it makes the contents of this variable, vNum, its "returnvalue" (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 ofinstruction, which we can use in C. Just like printf() or scanf(). So, whenever we need to read an argument, wherewe 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 programto "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'sname 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 itis to get data into our new function. Now, since GetArgument() above is intended to do thesame as our old code, we also have to get data back from our function, namely, we have towrite 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 alreadyknow that, by using the return vNum; statement in the last line of our function above, weare handing a value to whoever called us. In the case of main(), the operating system receivedthe 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 wasthere *instead* of our call to the function. That is, if the user entered a "15" as the leftargument, our line:

vFirstArg = GetArgument(true);

becomes equivalent to

vFirstArg = 15;

Summarised, this means that wherever you write a function in an expression, the computerwill first execute all the commands you give it inside the definition of your function, andthen 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 acouple of lines and your code has become a tiny bit more readable.

Note: 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: Variablesdefined inside curly brackets only exist as long as the computer is executing commandsbetween these curly brackets. Since every function's list of commands is enclosed incurly brackets, nobody outside these brackets can use these variables.

"But wait," you may say now, "main() calls GetArgument(). That mustmean that since the computer has simply taken a detour into GetArgument(), main()'svariables must exist!" True, they do exist. However, if you were really allowedto access the variables of the function that called you, this would mean that a carelesschange 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 togo into later, the inventors of C decided that variables defined inside a function arelocal, and can only be seen by the function itself, not by functions it calls, and notby the functions calling it.

As I mentioned above, the parameters to a function are just another local variable as faras your function is concerned. That also means that they can't really be seen from theoutside. 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 bracketsafter 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 canchange these variables all you like, it will not change the value of the parameter thatwas passed in. Which also explains why we need a return value: Because otherwise wewould 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

Reader Comments: (RSS Feed)
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?
Uli Kusterer replies:
Yup, that sounds like you nailed it :-)
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....
Steve writes:
Hi Uli, thanks for the tutorials. When I use the + - and / operations, my program performs the calculation then finishes, only when I perform the * operation it gives me the option to calculate again. I can't figure out why, here is my code. #include <stdio.h> #include <stdbool.h> int GetArgument( bool isLeft) //a function and a place to store (parameter) { int vNum; //a variable for the integer if( isLeft ) //isLeft gets its value later printf("Enter a number: "); else printf("Enter another number: "); scanf( "%d", &vNum ); //puts the number into vNum fpurge( stdin ); //makes sure any extra character entered after numb are discarded return vNum; } int main() { int vFirstArg, vSecondArg; char vOperation; bool vFinished; vFinished = false; //make sure the flag is initialised while ( vFinished != true ) //loop until flag is initialised { printf( "What operation do you want to do? (+,-,/,*) \n"); scanf("%c", &vOperation); fpurge( stdin ); if( vOperation == '+') { vFirstArg = GetArgument(true); vSecondArg = GetArgument(false); printf( "\n %d + %d = %d \n", vFirstArg, vSecondArg, vFirstArg + vSecondArg); } if( vOperation == '-') { vFirstArg = GetArgument(true); vSecondArg = GetArgument(false); printf( "\n %d - %d = %d \n", vFirstArg, vSecondArg, vFirstArg - vSecondArg); } if( vOperation == '/') { vFirstArg = GetArgument(true); vSecondArg = GetArgument(false); if(vSecondArg == 0) { printf("You can't divide by zero, try again: "); scanf("%d", &vSecondArg); printf( "\n %d / %d = %d \n", vFirstArg, vSecondArg, vFirstArg / vSecondArg); } else { printf( "\n %d / %d = %d \n", vFirstArg, vSecondArg, vFirstArg / vSecondArg); } } if( vOperation == '*') { vFirstArg = GetArgument(true); vSecondArg = GetArgument(false); printf( "\n %d * %d = %d \n", vFirstArg, vSecondArg, vFirstArg * vSecondArg); } else vFinished = true; } printf("finito.\n"); return 0; }
Supermouse writes:
Hi Steven, I don't know how old this is, but to answer your question: you need elses after each if in your function. The program as it's written will change vFinished to true anytime your input is not *. If you put an else after each if, the programa will change vFinished to true only if your input is not +, -, / or *. Hope it helped.
isaac Senior writes:
I have a quick question. Why don't you write the scanf and fpurge under if( isLeft ) if its suppose to represent it all: the printf the scanf and the fpurge ??
isaac Senior writes:
I have a quick question. Why don't you write the scanf and fpurge under if( isLeft ) if its suppose to represent it all: the printf the scanf and the fpurge ??
Rafael Estrella writes:
I find it very confusing when you use the term "the functions that called you". What does that mean exactly.
Mike Polinske writes:
Isaac Senior, The scant and fpurge are outside of the scope of the if statement. It will always do the scant and fpurge whether isLeft is true or false. For clarity you could write: if (isLeft) { printf("Enter left argument: "); } else { printf("Enter right argument: "); } scanf("%d", &vNum); fpurge(stdin); But why would you want to do that when the commands in the if/else are only one statement.
Caroline writes:
Great tute! I have only done a little programming in basic, and the way the commands are contained in functions is new to me. Your tute is making it really easy to follow.
Katrine writes:
Hi, can someone explain how get rid of this error: Calculator.c: In function ‘getArgument’: Calculator.c:19: error: nested functions are disabled, use -fnested-functions to re-enable Calculator.c:60: error: expected declaration or statement at end of input My code is the following: #include "stdio.h" #include "stdbool.h" int getArgument( bool isLeft ){ int vNum; if(isLeft) printf("Enter left argument: "); else{ printf("nEnter right argument: "); scanf("%d", &vNum); fpurge(stdin); return vNum; } int main() { int vFirstArg, vSecondArg; char vOperation; bool vFinished; vFinished = false; while(vFinished != true){ printf("n What operation do you want to do?n"); scanf("%c", &vOperation); fpurge(stdin); if(vOperation == '+'){ vFirstArg = getArgument(true); vSecondArg = getArgument(false); printf("n%d + %d = %dn" , vFirstArg, vSecondArg, vFirstArg + vSecondArg); } else if(vOperation == '-'){ vFirstArg = getArgument(true); vSecondArg = getArgument(false); printf("n%d - %d = %dn" , vFirstArg, vSecondArg, vFirstArg - vSecondArg); } else if(vOperation == '*'){ vFirstArg = getArgument(true); vSecondArg = getArgument(false); printf("n%d * %d = %dn" , vFirstArg, vSecondArg, vFirstArg * vSecondArg); } else if(vOperation == '/'){ vFirstArg = getArgument(true); vSecondArg = getArgument(false); printf("n%d / %d = %dn" , vFirstArg, vSecondArg, vFirstArg / vSecondArg); } else vFinished = true; } printf("Finished.n"); return 0; }
Jonathan Decktor writes:
Hi Katrine. Hope this is still relevant, seems like you have a { inside the getargument that doesn't have a matching }.
Withan writes:
my Xcode is giving a warning on the "int GetArgument ( bool isLeft)" line "NO PREVIOUS PROTOTYPE FOR THE FUNCTION 'GETARGUMENT' It still runs perfectly, but why am I getting this warning, and can I turn it off? xCode 4.2
Uli Kusterer replies:
Withan, it's a warning about good style, but shouldn't really cause you any trouble. Book 11 will explain what a prototype is and how to add one.
Comment on this article:
Name:
E-Mail: (not shown, hashed for Gravatar)
Web Site URL: (optional)
Comment: (plain text only)
Please Enter the following word:
Or E-Mail Uli privately.