C: Global Variables versus Parameters

I am reading "Learn C on the Mac" by Dave Mark, which I am finding to be a good book. However, Mark states that global variables are to be avoided if possible, and that it is better to use parameters. Is this correct? His book doesn't really explain why they should be avoided, other than saying that "they do save time but at the cost of proper program design."

Global variables seem a lot easier to grasp and use than parameters. Is using global variables really considered improper program design? Why?

One thing you will want to do though is distinguish between globals that are available anywhere in the program, or globals that are only available within the scope of that source file. The reason you want to keep your globals within the scope of that file is because it's easy to forget that you used a variable name in one file and then tried to use it again for something else in another file. The way you keep the global local to that file is by simply prefixing the declaration with the keyword, static. Example:

static int gMyGlobal;

It's a great habit to get into so you can keep your code more modular and easy to deal with.

So, with all that said, do be aware that over-use of global variables is considered "bad form" by many. It's also a source of poor performance in some cases because the global has to be loaded, whereas if you can keep a variable local it can be stored in a register. Those performance details aren't really all that important to know as you're learning, but just something to keep in mind.

One tip I would highly recommend (even insist) is that when you create a global variable it is an absolute must that it be named in such a way as to make it clear what it is. If you can't do that, at least be sure to write a good comment next to it where you declare it to describe what it's for. Local variables often document themselves, just by what they're used for in a particular function, but globals, being global in scope, don't always have that context to help hint at what they're for.

1) You'll find that when you want to re-use a function in some other program, it's a lot easier to cut and paste it if all you need to do is the function. If it requires a global variable with it to use it, then you have to hunt down the variable's declaration and copy-paste that too. So in this sense, using parameters is definitely more convenient.

2) It's a nice touch, if you can, to prefix your global variables with "g", so that when you're reading your code later (or someone else is reading it!), you'll instantly recognize it as a global. Example (as above):

I remember asking the exact same question when I was learning C. I agree with with everything AnotherJake said, and would just suggest a few more things you could do if you wanted.

Say you have a source file called main_menu, and in that file you have a global variable, g_is_main_menu_open, obviously functions outside of the source will want to query it so it's helpful to create an accessor function like this:

BOOL is_main_menu_open() {
return g_is_main_menu_open;
}

When you iterate through a list of game entities(and eventually you will), you may want want to use pointer parameters, rather than passing indices, it's quicker, more elegant, and makes your code easier to read.

NelsonMandella Wrote:Say you have a source file called main_menu, and in that file you have a global variable, g_is_main_menu_open, obviously functions outside of the source will want to query it so it's helpful to create an accessor function like this:

BOOL is_main_menu_open() {
return g_is_main_menu_open;
}

When you iterate through a list of game entities(and eventually you will), you may want want to use pointer parameters, rather than passing indices, it's quicker, more elegant, and makes your code easier to read.
...

This is where it gets subjective and takes experience, and is kind of the "art" of C programming. Sometimes I prefer to use an accessor to make things cleaner, but sometimes I just go bare-bones and access the global directly from the other file to make things cleaner. One thing I will say that I've learned about C over the years: try to listen carefully to what everybody says and then disregard them and do whatever you want. Rebellion is liberating!

Globals are available, and thus, very probably, also used everywhere in the program. This means that they are accessed and written to from many places. If you want to implement, say, checking that you never write -1 into gMySupervar, you have to implement this check everywhere. If you forget to do that in SupervillianDoStuff(), you just introduced a bug by *not* changing code.

And that's bitrot.

If you need global state, which you most likely do (even OOP concepts like singletons are just globals in reality), at least use accessor functions, in which you can implement sanity checks, or debug output.

Of course, designing things in such a way that you don't need global state at all would probably get you maximum points for style, but sometimes it's just not worth the hassle, as for simple things, the boilerplate code you have to add for proper encapsulation can take more time than writing actual functionality. However, as a program evolves, there will most likely come a point at which you should make the switch to keep the code sane.

And even though I wanted this post to have only a single paragraph, I also gotta say that a variable local to a module, eg prefixed with "static", is usually ok. As long as it is relevant to a large number of functions in a module.

Again with the example of the variable that should never be -1. It's easy enough to say that, but hard to enforce as DoG said. Now, lets say your program is crashing because the value is -1:

As a global variable, somewhere in your program you are writing myGloblal = (a - b)/c into the variable. Not exactly obvious that it could actually be -1 given the right a, b, and c. Then at some other point in the program you are reading the value back and crashing because you are trying to get the -1 item out of an array or something like that. Now you have to find all the places that can write into that variable and figure out which of them is writing -1 into it.

If it was passed as a parameter to a function when the program crashed, you would be able to open the program up in your debugger and see which function passed -1 to it and where it got it -1 from. This can make it much easier to debug your programs.

C programs can become very complex and difficult to manage once they reach anywhere from approximately 10k to 20k lines of code. Modular code helps to mitigate this complexity, but it can also add complexity to what would be a short, simple program.

NelsonMandella Wrote:C programs can become very complex and difficult to manage once they reach anywhere from approximately 10k to 20k lines of code. Modular code helps to mitigate this complexity, but it can also add complexity to what would be a short, simple program.

Yeah, that's pretty well put.

The best way to learn when and how to use globals is experience. For small programs, like say less than 10k lines of code, accessors are probably just getting in the way of getting the program done. Larger than that and using techniques for modularity (and debugging as DoG and Skorche pointed out) definitely start coming into play.

I will add this: As you are learning, I wouldn't worry toooo much about whether you're using globals or not, or how you specifically use them. As you start to become familiar with the language it does become more obvious what globals and parameters are really useful for.