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 ); // Ignore this line for now.

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 argument if isLeft is true, for the right argument 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 10 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?

Movie showing how main calls GetFirstArgument, and how the parameter and result get transferred back and forth

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;

Summarized, 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.

So, what about that extra line at the start, that looks just like the first line of our function, but has a semicolon after it? It’s not a function call. This is called a prototype, and you will learn why you need it and what for in Chapter 10. For now, just know that you need to double the start of every function like that, unless it is main().

Previous Next