Corning Community College
C/C++ Programming
Task 5: Functions
~~TOC~~
To begin the exploration of functions, and their impact on code modularity/design.
Please read Chapter 4 (“Functions”) in The C Book for additional information on functions.
So far in this course you have been introduced to methods of data storage, selection/decision control, I/O, and iteration. You've been implementing all this inside int main(), which you've been required to place in every single program.
We're now moving on to this function thing.. and, guess what? As I've pointed out all along: main() is a function!
In a way, functions are like evolved variables - they have a data type, they are given a symbolic name, and they contain information.
Functions are like sub-programs - small subroutines that perform some specific task that is relevant to the larger program (or other functions).
So, instead of storing some numeric value like variables do, functions contain a group of instructions which can give way to a final numeric value (or values).
That group of functions, which performs a task, is given a name. That will allow your function to be called from wherever it is needed, and can be used to prevent the needless duplication of code to do some common task a number of times.
The data type assigned to the function correlates to the return value - the data which is returned from a function as a result of its actions.
As I've said, the absolute minimal number of functions required in a C program is 1: main()
main() is a regular ordinary function with one notable difference– it is the one function the system looks for to start executing your program. Aside from that, it is as real and normal a regular function as any other you create.
Why can't I just put everything in main()?
Nothing is preventing you from exclusively using main(). However, your program may become unnecessarily long and difficult to debug should there be problems. Functions allow you to modularize your code- so you can break it up into little pieces. Not only can this lead to increased efficiency from less duplicated code, but your code will be significantly more organized.
For this course, I will expect you to use functions from this point on.
There are two aspects to functions, which are concepts shared with that of variables:
In order to avoid compiler errors, and allow your code to work seamlessly with your functions, ANY functions must be declared prior to any other function that makes use of them.
Once the function is declared, the definition could then immediately take place (just as is the case with variables), or you can defer definition to later.
Programming is more of an art, so there is no clear-cut answer to this. However, here are a couple of guidelines you should keep in mind as good indications for needing a function:
Some thoughts to consider when implementing functions:
A function consists of the following:
Technically, a function returns exactly 1 value (corresponding to the return statement that is placed at the end). A procedure can “return” 2 or more values. I say “return”, because you are only allowed the use of one return statement per function body, and it must be at the end of the program flow of that subroutine.
But what if we want to return nothing? Well, even though it is possible with the use of the void data type, it is generally not good practice. If you are not returning a meaningful value, you should at least return an exiting value signifying success or failure.
For example:
Ok, so if we're allowed only one return statement, how can we return more than one value? The simple answer is- we cheat. By employing the use of pointers, we can perform what is called passing a parameter by reference or address; instead of making a local copy, we actually obtain direct access to the memory location of that parameter, so when we make modifications, the changes remain even outside the function.
So, by using pointers in our function's parameter list, we can simulate the notion of procedures.
In many programming text books, you will see reference to “pass by value” and “pass by reference”. As it turns out, in C, instead of pass by “reference”, we have “pass by address”.
When we pass by value, a copy of a variable is made, and is entirely independent (aside from the initial value of the data) of the source data.
Any changes made to that variable within the function remain within the function– no changes are reflected back in the original variable (unless otherwise manipulated).
Passing by value is easy, we just declare variables within the parenthesis as normal:
int funcname(int var1, int var2) {
In this case, the function funcname has 2 parameters, both of type int: var1 and var2
So, to call our function funcname:
result = funcname(value1, value2);
In this case, value1 gets sent in as the first parameter (and var1 is declared and set equal to value1). Similar case for value2/var2.
When we pass by address, we use pointers to connect the function parameters to the actual memory space of a variable outside the function.
We do this by using the * and & pointer operators.
A sample function declaration:
int funcname(int *val1, int *val2) {
And then, to call this function, we do as follows:
result = funcname(&in1, &in2);
Using functions, we are going to implement the classic “Fox, Chicken, Corn” game.
The premise of this game involves a farmer with the above-mentioned items that needs to transport them across a stream in a boat.
The boat, unfortunately, is large enough only for the farmer and one item.
There exists the following relationship with the items:
So one needs to craft a careful means of transporting all these items without losing any of them.
A couple things to keep in mind:
Just to review the compilation/execution process for working with your source code, if we had a file, hello.c, that we wished to compile to a binary called hello, we'd first want to compile the code, as follows:
lab46:~/src/cprog$ gcc -o hello hello.c lab46:~/src/cprog$
Assuming there are no syntax errors or warnings, and everything compiled correctly, you should just get your prompt back. In the event of problems, the compiler will be sure to tell you about them.
Conceptually, the arrangement is as follows:
gcc -o BINARY_FILE SOURCE_FILE
The BINARY_FILE comes immediately after the -o, and the SOURCE_FILE, must never immediately follow a -o. It can precede, and such is perfectly valid (especially if you feel that way more intuitive).
To execute your binary, we need to specify a path to it, so we use ./, which basically says “in the current directory”:
lab46:~/src/cprog$ ./hello Hello, World! lab46:~/src/cprog$
If you find yourself experiencing “anomalous” behavior in your resulting program that just cannot be explained away (ie no discernable logic errors). You can try enabling some compiler optimizations.
Compiler optimizations invoke additional functionality present in the compiler that can do some alterations of your compiled code, reordering things for more efficiency, and even correcting aberrant behavior (but also having the potential to break otherwise “working” behavior).
To use compiler optimizations, we add the “-O#” option (capital letter Oh) to the compiler command-line. There are a number of compiler optimizations available to us (this was all gleaned from the gcc(1) manual page):
option | description |
---|---|
-O0 | no optimization (this is the default, it happens if you don't specify anything) |
-O | reduce code size and execution time, plus some non-expensive optimizations |
-O1 | same as -O |
-O2 | optimize more. Compile time increases for the result of better code and execution |
-O3 | yet more optimizations. Long compile time, perhaps more efficient code |
-Os | optimize for size. Uses a lot of -O2 optimizations so long as it does not impact code size |
So, if you'd like to compile your code with level 1 optimizations:
gcc -O1 -o BINARY_FILE SOURCE_FILE
As your programs get bigger and more complex, the utilization of compiler optimizations can make a significant impact on the resulting performance of your program. For most of the stuff we're doing now, you're not likely to notice many improvements.
As you write your code, hopefully you've developed the good habit of storing all your programs in your ~/src/cprog directory (and have added/committed them to your repository).
But, in order to complete your tasks, you've been requested to place it in your ~/src/submit directory instead.
What to do?!
We'll simply make a copy of your code! Assuming we're working with a source file called myprog.c in our ~/src/cprog directory, we'll copy it into ~/src/submit/ and give it a name of: taskX.c
To do that we use the cp command, and run it as follows:
lab46:~/src/cprog$ cp myprog.c ~/src/submit/taskX.c lab46:~/src/cprog$
We can then hop over to our submit directory and add/commit it:
lab46:~/src/cprog$ cd ~/src/submit lab46:~/src/submit$ ls contact.info taskU.c taskV.c taskW.c taskX.c lab46:~/src/submit$ svn add taskX.c Added taskX.c lab46:~/src/submit$ svn commit -m "added taskX.c to the submit directory" ...
All questions in this assignment require an action or response. Please organize your responses into an easily readable format and submit the final results to your instructor per the appropriate methods.
Your assignment is expected to be performed and submitted in a clear and organized fashion- messy or unorganized assignments may have points deducted. Be sure to adhere to the submission policy.
When complete, questions requiring a response can be electronically submit using the following form:
<html><center><a href=“http://lab46.corning-cc.edu/haas/content/cprog/submit.php?task5”>http://lab46.corning-cc.edu/haas/content/cprog/submit.php?task5</a></center></html>
Additionally, the successful results of the following actions will be considered for evaluation:
As always, the class mailing list and class IRC channel are available for assistance, but not answers.