Anonymous Shadow Anonymous Shadow - 1 month ago 6
C Question

A method of avoiding global variables in C

Suppose I have the variable

counter
. If I need to:


  • access and modify the variable from many places in the code

  • make sure that the variable is modified in the "correct" way,



is this solution adequate, or are there more efficient/cleaner ways to do it?

int counter_access(int value) {
static int counter = 0;

if (value > 100) {
printf("there is a bug in the code");
return counter;
}

counter += value;
return counter;
}


And then when I need to modify the variable:

counter_access(10); /* increase counter by 10 */
counter_access(-2); /* decrease counter by 2 */


And when I need to access the variable:

if (counter_access(0) == 100) do_something();


This solution seems rather kludgy to me. However, I can't think of very many good ways to do this. I could use global variables (which cause bugs). I could pass the address of
counter
to the functions which need it, but that doesn't make sure that the variable isn't modified in an incorrect way (in the example above, if
counter
is incremented by more than 100, there is an error).

Essentially, the problem with using a function to access the variable is that there isn't a satisfactory way to tell the caller that the value is incorrect.

Answer

Using a single function for things like this is a good option for single threaded programs, you just need to set up things in the proper way.

To signal that something went wrong you can use some "out of the range" value. In your case the counter range is 0 .. 100.

You may have something like:

#define COUNT_OVERFLOW            -1
#define COUNT_UNDERFLOW           -2

#define counter_get()    counter_add(0)

int counter_add(int incr) 
{ 
    static int counter = 0;
    int counter_temp;

    counter_temp = counter +incr;

    if (counter_temp < 0)    return COUNT_UNDERFLOW;
    if (counte_temp > 100)   return COUNT_OVERFLOW;

    counter = counter_temp;
    return counter;
}

Now, to detect an error you may check if the return value is < 0:

cnt = counter_add(x);
if (cnt < 0) {
  fprintf(stderr,"There is a bug in the code\n");
}
....
if (counter_get() == 100) {
  printf("DONE!\n");
}

Note as the value of counter is preserved even if there's an error. Also, it's better not to have functions like your counter_access() printing error messages, it's better to check the return value and make the caller print it (if it is so inclined).

I added the macro counter_get() to avoid having the user remember that adding 0 has the side effect of returning the current counter value.

As mentioned before, in more complicated cases you shouldn't use static variables (or equivalently, global variables). In those cases the proper way is to have a struct that is instatiated for each thread and keeps the variables that are relevant for that thread state. You will have to pass a pointer to that structure around and having the counter_access() function accepting it as a parameter.

Looking closely, you can see that here we are trying to mimick the object-oriented approach of encapsulating data and operations. In this case we implemented (implicitly) a single instance of an object (the counter) that has two methods: one to change the value and one to get the value.