Book 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 ); // 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?

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 11. For now, just know that you need to double the start of every function like that, unless it is main().

Previous | Next

This entry was posted in C Tutorial. Bookmark the permalink.

14 Responses to Book 5: Saving us Some Typing

  1. Daniel says:

    I’m still a bit confused on the isLeft thing and how it’s already assigned a value. I also don’t really get what isLeft being true means.

    • Uli Kusterer says:

      You may want to revisit the previous chapter, which describes what a “boolean” is. Basically, it is a type for variables that can only contain two values, “true” or “false”. If a variable is “true”, that simply means that someone wrote that value into it. I.e. just like an integer variable can contain the value 1 or 2 or 3, a boolean value can contain true. Or false. “isLeft” is just a (fairly arbitrary) name we chose for our variable.

  2. Bill F says:

    Why am I getting this result in the console?

    What operation do you want to do?
    +
    Enter left argument: 99999999999

    Enter right argument: 9999999999

    1215752191 + 1410065407 = -1669149698
    What operation do you want to do?

    Why after adding the series of nines, does it return:

    1215752191 + 1410065407 = -1669149698

    ?

    Thanks

    • Uli Kusterer says:

      There is a limit on the numbers you can store in a variable (it’s roughly 2 billion in either direction from 0, so -2 billion and +2 billion). You’re asking for 9 billion, which means you get a garbage result.

      • Bill F says:

        Ok, thank you for the clarification!

        And thanks for this tutorial, I’ve been hesitant to dive into C until I happened upon your course. It’s great, thank you!

    • Katie says:

      This may be a stupid question, so bear with me, but new to computer science and curious about the following:

      “…what this function does is ask for the left argument if isLeft is true, for the right argument if it isn’t…”

      Why is it inherently understood that by writing:

      if( isLeft )

      That the loop is checking to see if isLeft is True? Why wouldn’t it check to see if isLeft False?

      When you write a conditional like that in C — where it’s checking a Boolean variable —is it automatically assumed it’s checking if the variable is True instead of checking if it’s False? Why aren’t we saying something like (forgive the erroneous syntax) if( isLeft = true ) ?

      • Uli Kusterer says:

        That’s simply how “if” is defined in C. Its argument is a boolean, and it does something if that is true. Saying:

        if( true )
        doFoo();
        else
        doBar();

        will run doFoo(), not doBar(). If you write

        if( myNumber == 5 )
        doFoo();

        what actually happens is that it == compares myNumber and 5, as if it was a function bool IsEqual( int a, int b ), and then ==’s return value is a bool, and if only ever sees that return value, it doesn’t see the concrete comparison you did.

        • Katie says:

          Thanks for the response/explanation! So just to be clear, you’re saying that anytime, in C, a boolean is passed as an argument in a conditional statement (e.g. if(SomeBooleanVariable) ) it’s always checking if that boolean variable is true?

          Can you explain what exactly does isLeft being True mean in this example?
          Does that mean that, in the case where isLeft is True, that there is no value entered for the left argument and that’s why the user is prompted to enter one?

          Why if isLeft is False, is the user prompted to enter a right argument?

          I guess what I’m asking is, what’s happening to make isLeft True or False? How is the Boolean value being assigned to isLeft or isRight?

          Thanks again for any help you can provide here.

          • Uli Kusterer says:

            Almost. isLeft is something we made up for our program. It is a note which our classmate “main” handed us, instructing us to do something. We know that, since it is on the “isLeft” note, if it is true, we should ask for a left argument, and if it is false, we should ask for the other kind of argument (right). We (i.e. the GetArgument() function) do not know whether there is or isn’t a left argument, we do not verify that in any way. We only look at isLeft and do what it tells us to.

  3. Roland says:

    I got endless loop of “Enter left argument :”

    where did I miss? enlighten me please

    #include
    #include
    int GetArgument( bool isLeft );

    int GetArgument( bool isLeft )
    {
    int vNum;

    if( isLeft )
    printf( “Enter left argument: ” );

    else
    printf( “Enter right argument: ” );

    scanf( “%d”, &vNum );
    fpurge( stdin );

    return vNum;
    }

    int main()
    {
    int vFirstArg;
    bool vFinished;

    // vFinished = false;

    while (vFinished != true)

    vFirstArg = GetArgument(true);

    printf( “VFirst Arg = %d”,vFirstArg);

    fpurge(stdin);

    return 0;
    }

    • Uli Kusterer says:

      Your loop is missing the curly brackets. If you don’t put { and } around the commands that should be repeated, it will just grab the first command, which in your case is “GetArgument(true)”.

      But note that even if you do put the { before the “vFirstArg =…” line and the } after the “fpurge” line, you’d be asked endlessly, because you’re using an incomplete version of the calculator. At the least you’d need to ask for the operation to do. So, better finish the previous chapter before you continue with this one.

  4. Ziv says:

    I keep getting an error, and I don’t know what it means.
    Here’s the transcript:
    I’d love a pointer…

    CompileC “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/main.o” “First Program/main.c” normal x86_64 c com.apple.compilers.llvm.clang.1_0.compiler
    cd “/Applications/Programming/First Program”
    setenv LANG en_US.US-ASCII
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x c -arch x86_64 -fmessage-length=0 -std=gnu99 -Wno-trigraphs -fpascal-strings -O0 -Wno-missing-field-initializers -Wno-missing-prototypes -Wreturn-type -Wformat -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wenum-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -DDEBUG=1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -mmacosx-version-min=10.7 -g -Wno-sign-conversion -iquote “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-generated-files.hmap” “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-own-target-headers.hmap” “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-all-target-headers.hmap” -iquote “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-project-headers.hmap” -I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/DerivedSources/x86_64” “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/DerivedSources” -F/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug -MMD -MT dependencies -MF “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/main.d” –serialize-diagnostics “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/main.dia” -c “/Applications/Programming/First Program/First Program/main.c” -o “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/main.o”

    CompileC “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/Basic_Calculator.o” “First Program/Basic_Calculator.c” normal x86_64 c com.apple.compilers.llvm.clang.1_0.compiler
    cd “/Applications/Programming/First Program”
    setenv LANG en_US.US-ASCII
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x c -arch x86_64 -fmessage-length=0 -std=gnu99 -Wno-trigraphs -fpascal-strings -O0 -Wno-missing-field-initializers -Wno-missing-prototypes -Wreturn-type -Wformat -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wenum-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -DDEBUG=1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -mmacosx-version-min=10.7 -g -Wno-sign-conversion -iquote “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-generated-files.hmap” “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-own-target-headers.hmap” “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-all-target-headers.hmap” -iquote “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/First Program-project-headers.hmap” -I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -I/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/DerivedSources/x86_64” “-I/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/DerivedSources” -F/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug -MMD -MT dependencies -MF “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/Basic_Calculator.d” –serialize-diagnostics “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/Basic_Calculator.dia” -c “/Applications/Programming/First Program/First Program/Basic_Calculator.c” -o “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/Basic_Calculator.o”

    Ld “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug/First Program” normal x86_64
    cd “/Applications/Programming/First Program”
    setenv MACOSX_DEPLOYMENT_TARGET 10.7
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk -L/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug -F/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug -filelist “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/First Program.LinkFileList” -mmacosx-version-min=10.7 -o “/Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Products/Debug/First Program”

    duplicate symbol _main in:
    /Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/main.o
    /Users/Horace/Library/Developer/Xcode/DerivedData/First_Program-bnyyxlqmvvkfklfbwqcrenrwzfzm/Build/Intermediates/First Program.build/Debug/First Program.build/Objects-normal/x86_64/Basic_Calculator.o
    ld: 1 duplicate symbol for architecture x86_64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)

  5. Uli Kusterer says:

    Craig, good point. I call them ‘argument’ in the printf(), after all. Fixed. Though I guess I should probably explain why I call them “arguments” somewhere…

  6. Craig says:

    Paragraph 5 should read:
    “So, what this function does is ask for the left argument if isLeft is true, for the right argument if it isn’t….”

    Excellent introduction to custom functions, thank you very much!

Leave a Reply

Your email address will not be published. Required fields are marked *


*