Is it reliable to use a macro to change the value of a global variable to be more readable in C?

Started by gokhannsahin 6 years ago7 replieslatest reply 6 years ago228 views

I want to use a Macro to set/get the global function for more readable code. How safe is it? For ex. I have a global variable to keep the setting parameter. I think that if I set it with a macro than it will be more readable. For ex.

uint8_t statePage=0; // global variable...

#define __SELECT_PAGE(x) (statePage=x)

statePage=HOME_PAGE; // instead this...

__SELECT_PAGE(HOME); // In my oppinion this is more readable...

[ - ]
Reply by RogerCJuly 16, 2018

You have really asked two questions, one about readability and the other about safety. Readability tends to be subjective. Personally I don't have any trouble reading code where values are assigned to variables using the = operator. If you prefer the function-like macro approach, you should use it.

When you ask about safety, I'm assuming that what you are really asking is "If I use a macro in this way will it increase the risk of my code not behaving as I intend?" As written, your macro definition contains two risks:

  1. Names beginning with two underscores are typically reserved for compiler and system predefined macros (e.g. __FILE__, __GNUG__, __STRICT_ANSI__). Avoid using two underscores at the beginning of your own macro names to help minimize the chance of collision with a system predefined macro.
  2. Macro arguments should always be enclosed in parentheses:
    #define SELECT_PAGE(x) (statePage=(x))
    See this answer on Stack Overflow for a nice explanation.
[ - ]
Reply by bamosJuly 16, 2018

RogerC has two very good tips to make the macro better.  There's also the question of whether or not a global variable with a function-like macro approach is the "right" path to take.  If your application is reasonably complex, you're likely doing some other work related to the various "pages", you may be better of hiding the statePage variable inside a *.c file and making the get/set functions available through a *.h file.

To ensure an invalid page isn't chosen inadvertently, you could also make "page" an enum, so you're guaranteed not to have an invalid page selected.

[ - ]
Reply by JackCrensJuly 16, 2018

I agree with bamos.  You can't create a C++ class in C, but you can do the next best thing by putting the data item and the functions that operate on it in a separate .c file.  Instead of making the variable global, make it local to that file.  Then only the set and get functions get to access it.

That said, I think perhaps you need to rethink your program structure.  By making this variable global, you are violating David Parnas' Principle of Information Hiding.  The general idea is that the only "actors" (i.e., functions and programs) who deserve access to a certain variable are the ones that really need that access.To be clear, Parnas wasn't the first person to USE the principle, only the first to express it and give it a name.  Any of us old-time Fortran programs learned the principle the hard way.

In those days, all too many programmers had a tendency to throw all their variables into unlabeled COMMON blocks, which of course meant that they were globally accessible to anybody.

I even encountered one maintenance programmer who "improved" a truly wonderful NASA N-Body simulation program (think STK or GMAT) by taking every single variable in the program -- even loop counters like I, J, and K -- and moving them to COMMON blocks.

When I asked him why he would do such a thing,  he said proudly, "You never know when you might want to see it."

Imagine the chaos that would ensue if all the loops used the

 DO 10 I = 1,100 ...


I chose the exact opposite approach, which is to use lots of little functions to operate on key variables, and let only those functions "see" the variables.

Did I invent OO?  Not hardly. But it was the best we could do in a Fortran environment.  The important thing is, I think you need to re-examine your need for a Page variable.


[ - ]
Reply by Tim WescottJuly 16, 2018

Heh.  That reminds me.  I used to work with a guy who hated breaking programs up into functions that would all fit in one page.  He wanted to see everything laid out in one nice flat 2000-line listing.  Even functions like turn_the_upper_left_blinky_light_on() confused him if they weren't in line.

[ - ]
Reply by JackCrensJuly 16, 2018

Yep, it used to be almost the model for a "good" Fortran program, back in the day.  And you're right: The excuse is usually performance. Thankfully, computer scientists like Edsger Dijkstra and David Parnas came along to show us how to get it right.

[ - ]
Reply by Tim WescottJuly 16, 2018

When to use a global or when to put something behind some sort of an information wall is a judgement call.  In general, the shorter the program the more reasonable it is to leave things as globals.

If in doubt, though, put it behind a wall.

[ - ]
Reply by SolderdotJuly 16, 2018

Many answers have been posted so far. I guess the summarized answer to your question is: Yes, you can do that, and it is safe if you do it the correct way.

I agree that one module should only export functions but not variables for outside use. Exposing a function-like macro which operates on a global variable may be done for performance reasons (reduce the function call overhead) but such a thing should be done only in cases where needed and not per se.