This series of articles are the study notes of "An Arduino platform and C Programming", by Prof. Harris,Department of Computer Science, University of California, Irvine. This articleis the notes of week 2, C Programming, lessen 3.
3. Lesson3: Functions
3.1 Lecture3-1:Functions
This lecture wil ltalk about the use of functions in C. A very important concept. It's basically a way of encapsulating a bunch of instructions and giving them a name so you can refer to them again and again.
3.1.1 Exampleof function
- Functions can replace groups of instructions
- Define a function: call a function
A function is a way of encapsulating a group of instructions. That's the main thing it's for.
We got two examples of code. The top, we got a main, and it executes these instructions. It has three variables, x y z. And it assigns Y equal to two, Z equal to three. It performs some operations Y equals Y plus Z, X equals Y, so on and it prints X. So fine. Simple. Three real instructions plus the definition of variables and then below that we have another example.
We have another example. A program that does the same thing as the top main. The program down below you got this new function called Foo. So there's a main there. If you look at the main down near the bottom you see this main. But it doesn't have the code that we just had. It changed it. All it does is it says foo, open paren closed paren. And then looking above it you see we've defined this function called foo. Void foo. And that function called foo does everything that the main did before. In fact it has basically the same code that the main had. We took that same code, we put it into this foo. Now what happens is, so we've defined that into foo. So we've given a name to those instructions. And every time we wanna execute that sequence of instructions we just call foo. So we see where it says function call. We just give the name foo, open paren, closed paren to let it know it's a function.
3.1.2 Benefit of using function
What's the benefit of using functions?
(1) Reduce space make the code easier to understand
One benefit is if these operations that foo is doing, these set of instructions, if these are something that's done over and over and over again, many times in your code, rather than copying it over and over and over again into your main, it is much more convenient to write a function that does those instructions. And then every time you just call that function. So now notice in my main, when I wanted to execute those instructions I just called foo. foo(), and it executed those instructions. Say foo is something I did 100 times in my code. Rather than writing all those instructions over and over again a hundred times, I just call foo a hundred times. So it just reduces space right there, it makes the code simpler and easier to understand.
(2) The meaningful naming of the function make it easy to understandand use
Another thing that I did not cover, that I didn't mention here, which is important though, is that the naming of the functions is very important. Now I'm calling it foo, which means nothing, but when you write these function, you wanna give them names that have meaning. So for instance maybe foo was performing fibinocci or something, doing some kind of exotic calculation, say it was taking a derivative. You wouldn't call it foo, you would call it derivative. And then just looking at the name you can say oh, it's called derivative it must be taking a derivative and you wouldn't have to think, as a program, you wouldn't have to think of what goes on when taking a derivative You just, no look I call this function derivative and derivative is taken. And this is much like the advantage that you get from using libraries in Arduino.
For example, in Ethernet controller, therewas a connect function, and this connect function, you're a programmer you don't have to know all the details of how connecting happens. You just callconnect and connecting happens. So it's important that the name that has meaning like connect. Connect is clear. So you give it a meaningful name and it can help make the code easier to understand.
3.1.3 Function Arguments
- Data can be passed to functions as arguments
Functions can take arguments. So data can be passed to a function. Now if you look at the example on the left, that's what we had in the last slide. And in that case you're calling foo, and when you call foo it executes the instructions in foo. But it doesn't pass any data to foo. There's no data to pass to foo. Foo has all of its data internally, as int x, int y, int z. Y is equal to two, z equals to three, so all the data it needs is inside foo. It doesn't need data. But if we look on the right, we've changed the definition of the function. So, take a look at, let's look at the main first. In the main, where we call foo, in parentheses you say foo(2, 3). Now we're passing it to number 2 and number 3.
And that makes the average function a heck of a lot more useful because now you can call it with whatever arguments you want. You can use it in a more generic way in other contexts. So functions can be passed arguments like that, so you're passing this data to the function and the function uses that data in performing whatever its operations are.
3.1.4 Function Return Value
- Functions can return a value to the caller
- The type of the return value must be declared
In addition to taking arguments, a function can return values. Now, if we look at the foo on the left, that function doesn't return any values. It just does it's job and it prints something and that's the end of that. No need for returning values. Now, if we look at the foo on the right, though, this one. Actually, let's look at how the structure of the main is changed first. We look at the main. It calls foo still, but notice that now we've declared this new variable, p, I've called it.
Have a function return a value, because then the function can do some job that you needed to do, and give you a useful result in the end. So, sometimes functions do have return values. Sometimes they don't. It depends on the type of function. We'll get more into that when we talk about Arduino specific problems. But, so functions can take arguments and they can also return values.
3.2 Lecture3-2:Global Variables
We're gonna talk about variables a little bit more, specifically a type of variables called global variables. They are used a lot, so you need to know what they are. Generally, they are dangerous. They are risky, but you certainly need to know what they are. So let's explain them.
3.2.1Global Variables
- A variable is global if it’s defined outside of any function
- A global variable must be declared as an extern in any function using it
- Extern not needed if global declaration is before the function
- Variables can be global across files.
(1) Local variable
So generally, regular variables are declared inside a function and they only exist inside that function. in the last lecture, you have a function called foo and inside that function called foo you declare a variable int i. Now that int i, that i is alive when you're executing foo. Only within foo, so it's scope is limited to the execution of the function where it is defined. So what that means is if you have a main function, which calls foo, the main doesn't see i. Since you defined i inside foo, i is only visible inside foo and then main can't see it.
(2) Global variable
- Declare outside any functions
A global variable is basically a variable where more than one function can see that variable. They share a copy of the same variable. So if you look at the top, int global_i. So I've defined some variable called global_i and it's an i variable, which is global. I want everybody to see it. So I declare it, int global_i. Note that in order to make a variable global, first thing you've got to do is declare it outside of any function. I could have ten functions, but I declare this global variable outside of any function. Because if you declare it inside a function, it becomes local to that function.
- Add extern in function that will use that global variable
Now in addition, what you have to do in order for this variable to be globally seen by other functions, every function where you want this variable to be seen, to be used, you have to declare it as an extern. So look at that foo, it says, extern into global_i. So, it looks like I'm re-declaring the same variable. But notice I put that extern in front of it, which means it's an external variable. So that means that this is gonna be global. But extern is not needed if global declaration is before the function.
First, define the variable outside of any function. Then inside every function where you wanna use that global variable, you have to declare it as an extern.
3.2.2 Global variable are dangerous
- Global variables van propagate bugs
- Bug in foo can cause bar to crash
- Debugging can become harder
- Reduce modularity of code
Global variables can be dangerous in the sense that the program is less compartmentalized, so they're sharing variables. So what that means is if two functions share the same variable, then one function can alter and mess up another function. So in this case, we see a foo and we see bar. So foo writes to global_ i, global_i equals a plus b, whatever it is and then bar uses global_i to assign x. Now if somehow foo does something wrong say, foo is written incorrectly, say it has a bug. So it writes to global_i, but it writes the wrong value to global_i, then that bug is not just contained inside foo, that bug now spreads to bar. Because not only does foo do something wrong, but now bar is forced to do something wrong, because bar is getting the wrong data from foo. So it's just another connection between functions and it's not an obvious connection between functions. So that's another bad problem, because one way to connect functions to trade data between functions is if one function calls another function, it passes that data in the form of an argument and that's a nice narrow interface. Meaning, you know this is the pipeline of information, but global variables are these Information type lines between functions that are not obvious to the programmer. So, it's hard to necessarily remember all the global interactions. So these type of things are more difficult to see, they're connections between functions that are more difficult to see for a programmer. So it makes debugging hard, because when the problem happens it's hard to remember.
So generally, you
try to avoid using global variables. But especially in these Arduino sketches, they're so short people use them anyway and they are so small that maybe it doesn't matter at that scale. It's sort of a bad programming practice to get into though, but I tell you I see it a lot in Arduino sketches.