![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
kWoody_uk
Joined: 29 Jan 2015 Posts: 47 Location: United Kingdom
|
How to avoid using global vars? |
Posted: Tue May 24, 2016 5:49 am |
|
|
Hi all,
I have very little formal C training, but a fair amount of experience - however I find it difficult to avoid using global variables.
For example: I need a coms. function to flag that a reply hasn't been received within a certain period of time and the result of this is that the LCD displays a message etc.
How do you ensure that the two separate functions (coms. handling and LCD handling) do not become blurred but can still relay information to each other, without using global variables? I know that it is appropriate to use them sometimes, but they seem to be creeping into my code more often...
I know there are a certain element of people who disregard recommended programming practices, but my old boss was the language guru for a defence company and he gave me a good foundation in the basics which I'd like to aspire to.
Any advice would be gratefully received.
Thanks,
Keith |
|
![](templates/subSilver/images/spacer.gif) |
newguy
Joined: 24 Jun 2004 Posts: 1903
|
|
Posted: Tue May 24, 2016 6:44 am |
|
|
At my old position we briefly had a "software guru" who also positively HATED global variables. His projects were filled with "get_" functions. The idea being that a module could request some value from a different module through the use of these get_ functions. Define the get_ functions in a .h file which is included in each module that uses the functions but they're only defined inside the module that handles the data.
A word of caution, however: this person's code was a nightmare in terms of speed of execution and stack usage. Function after function, each one line, each calling a different nested function. Finding where he was actually doing the nitty-gritty work was a pain. I designed a dsPIC based communication bridge (CAN - serial) supporting 2 CAN networks and 2 serial streams. He wrote the code that interfaced to the bridge, but was resident on a beaglebone black. He actually managed to bog that thing down even though it was running at 1GHz vs the 80MHz of my dsPIC. His RAM usage was absolutely unreal.
Globals, on a PIC, aren't a bad thing. On larger projects running a RTOS - the encapsulated programming approach has its place. On a PIC without an operating system - don't bother trying to avoid them. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19382
|
|
Posted: Tue May 24, 2016 7:32 am |
|
|
It is better on RAM space, and reduces the risk of variables becoming corrupted, if you keep all variables that are local to a function, local. So everything that is only used inside a function, should always be a local variable.
Using global for stuff that genuinely is 'global' on the other hand makes sense. So something that is talked to by multiple separate functions, really is an excuse for a global variable. Often though it is surprising how few things this really applies to.
Having a global variable for 'comm_timeout', as such may be perfectly sensible.
Keep global definitions in one place. This helps massively, since if you have a include file 'global_defines.h' for example, you can at any time look in this and first, see what variables are global, and also putting a variable here may well make you think again.
If a routine is split into two subroutines, pass these a master variable from the calling function, or have each generate a return status value, rather than using globals.
For things like interrupt driven serial, timers etc., then globals really are going to be used.
Avoid like the plague, 'global' variables for things like local counters etc..
So 'treat with care', 'try to reduce', but accept that they _are_ part of programming for things like embedded applications..... |
|
![](templates/subSilver/images/spacer.gif) |
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Tue May 24, 2016 8:00 am |
|
|
Let me add one comment to MR.T's excellent summary: BEWARE any global VAR of MORE than 8 bits that is modified by an interrupt function.
let's say you have an INT32 that can be changed by interrupt.
I've found it necessary to declare a foreground
or local var to insure integrity of the data,
Code: |
int32 myglovar, myglovarcopy;
// use myglovarcopy in the forground - but before using it
do{myglovarcopy=myglovar;}while (myglovarcopy!=myglovar);
|
and use the 'copy' in the foreground |
|
![](templates/subSilver/images/spacer.gif) |
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
Re: How to avoid using global vars? |
Posted: Tue May 24, 2016 9:13 am |
|
|
kWoody_uk wrote: | I have very little formal C training, but a fair amount of experience - however I find it difficult to avoid using global variables.
|
There isn't much formal C training available these days, while there is comparatively plenty for other C derived languages such as C++ and C#. If there were formal C training, especially for small-scale embedded environments such most PIC projects, then there would not be an obsession with eliminating global variables. They are pretty much inherent to some degree to PIC scale projects. They are inherent to embedded code, where things like hardware services are talked to through what amounts to global variables. In PIC-land we call them SFRs (special function registers I believe).
The techniques and practices that demand the elimination of globals are ones based around large scale projects and the management of the complexity that goes with them. Such practices trade off performance and memory for "encapsulation". They are especially useful when multiple people are working on one project. But just how many times do you see people come to this forum and ask "we have this problem..."? No, they ask, "I have this problem..." Why? Because they are working alone, and they are working alone on processors with very limited memory and processing power. Processors and projects where dynamic memory allocation is actively discouraged due to those limitations. Any form of object orientation and encapsulation is essentially wasteful, and on PICs, there simply isn't enough processor power and memory to waste, even on the highest end examples.
Quote: |
I know there are a certain element of people who disregard recommended programming practices, but my old boss was the language guru for a defence company and he gave me a good foundation in the basics which I'd like to aspire to.
|
Well, in big defense projects that's all fine and dandy, but you're almost certainly not doing that now. Such practices are appropriate for some things, but they are not for everything. One size doesn't fit all, and I would never call anyone a "guru" who didn't realise that.
It's up to you. Do you waste a lot of time trying to find ways of getting rid of all global variables just because of some rather misguided, inappropriate hand-wavy practices, or do you use your time getting your projects working? That is the choice.
Personally, obviously, I don't get too wound up about global variables. I tend to think of the project as encapsulating itself. Yes, in my PIC projects I do tend to write simple hardware abstraction layers, though primarily as a means of separating out function from implementation. I do not try to provide operating system-like shared access to peripherals. Yes, I do tend to write drivers as pseudo-objects where appropriate, but I don't do so slavishly. I never use dynamic memory. I try not to use unions and other things that rely on hardware specific memory layouts.
If I need a global variable then I use one, especially when it relates to hardware resources. I try not to use them for everything, but I make no attempt to eliminate them.
I too have come across such principle-guided horror projects which have had as many as seven layers of call before a single line of active code! And mains that had a simple call to another routine. At least mine tend to have at least two lines!
Then again, when I'm working in a less resource constrained environment, I use different techniques. Being able to be flexible and adapt to different languages and hardware environments is a key indicator of a skilled programmer. Anyone that tries a one-size fits all approach is not, in my opinion, all that skilled: they are a one-trick pony.
I say, embrace your globals, but try not to go overboard. Just remember, there is no protection on PICs, and none in C to prevent you from writing anywhere in memory at any time. All the encapsulation in the world can't help you if you have wayward pointers! |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9177 Location: Greensville,Ontario
|
|
Posted: Tue May 24, 2016 10:00 am |
|
|
OK, I'll admit I'm a 'sloppy C programmer' and most of the time my variables are always global. Perhaps in the good old days when memory was expensive, there was a real need to be 'creative' in memory management but these days I just don't see it.
There is no 'right or wrong' to coding, heck if it works...great !
Jay |
|
![](templates/subSilver/images/spacer.gif) |
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Tue May 24, 2016 1:01 pm |
|
|
newguy wrote: | At my old position we briefly had a "software guru" who also positively HATED global variables. A word of caution, however: this person's code was a nightmare |
Hi newguy,
This guy apparently works for me now..... Please let me know if you want him back!! ![Rolling Eyes](images/smiles/icon_rolleyes.gif) _________________ John
If it's worth doing, it's worth doing in real hardware! |
|
![](templates/subSilver/images/spacer.gif) |
jeremiah
Joined: 20 Jul 2010 Posts: 1329
|
|
Posted: Tue May 24, 2016 4:12 pm |
|
|
I try to keep it balanced though I tend to lean more on the "less globals" side than some others.
For me, I use globals for sure under certain conditions:
1. If I need to use a variable in an interrupt that is also accessed in either another interrupt or the main code (unavoidable here)
2. If I am managing some sort of state or device parameters for the program lifetime (READ: not the device lifetime) and multiple functions need to access those parameters.
3. The code becomes too complex to pass it through a series of functions.
Otherwise, I at least see what it looks like using parameter passing or some other method. We've had global variables bite us too many times to not at least ask the question of "should we" before using them.
I do like using some encapsulation in my code though I don't know if it fits the personal definitions mentioned here (I like to try and separate the implementation of a peripheral from the actual usage code if it makes sense to do so). If I do, I try to make sure that proper use of inlining is at least adhered to to remove any bloat caused and that it doesn't get way out of hand.
At the end of the day though, I have to meet the user requirements, so I do what I have to in order to ensure that goal is met. |
|
![](templates/subSilver/images/spacer.gif) |
newguy
Joined: 24 Jun 2004 Posts: 1903
|
|
Posted: Tue May 24, 2016 9:09 pm |
|
|
ezflyr wrote: |
Hi newguy,
This guy apparently works for me now..... Please let me know if you want him back!! ![Rolling Eyes](images/smiles/icon_rolleyes.gif) |
No thanks. I've moved on and so did he. Just before I left I finally fixed a long nagging issue with a family of programs (all ran on the same hardware but they had different implementations/functions) that, upon digging back through the releases, had existed for 18 years (not kidding). This guru had attempted to fix the issue but had actually made it worse by his coding style (strict encapsulation, no globals if he could avoid them).
The issue was memory corruption - specifically stack corruption. It was made worse by our use of a long obsolete and very poorly documented RTOS and similarly long obsolete and poorly documented compiler (not CCS). I knew the memory was being corrupted but I could never find the cause until just before I left (took 6 years - very ashamed of that).
Anyway, guru's coding style literally blew up the stack usage to the point that making it way bigger than the memory map supposedly showed it needed (and each module had its own stack) made no difference. We were still getting memory corruption.
I got really angry one day and started poring through all the documentation we had on the RTOS, the compiler, you name it. Took quite some time but I noticed an oddly named compiler option (something to the effect of auto stack fix or somesuch) was set to disable in our makefiles. ??? Looked up the option in the compiler documentation and nearly put my fist through the wall. Turns out it's an option for the compiler to automatically insert extra code to monitor stack usage and automatically compensate/fix overflows.
Turned that option on, recompiled, and all of our "weird #$!*" instantly went away. Turns out the original designer of the system had intentionally turned that option off 18 years prior. Back then all the projects were much leaner. Through the years feature bloat and various "gurus" had incrementally made the issue much worse. |
|
![](templates/subSilver/images/spacer.gif) |
kWoody_uk
Joined: 29 Jan 2015 Posts: 47 Location: United Kingdom
|
|
Posted: Wed May 25, 2016 1:43 am |
|
|
Thanks for the ideas and opinions guys. You know how it is, you're taught by someone and their style tends to rub off on you, for better or worse.
In the end, I just want to make code that works in the simplest way possible, and that I can come back to later without having to pull my hair out to understand (I'm starting to lose it already!)
I know that if I really do have problems, I can always ask for advice on here.
Best regards,
Keith |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19382
|
|
Posted: Wed May 25, 2016 2:15 am |
|
|
Well done for asking.
Personally, I tend to have a 'status' variable of some form, that is global.
This then may contain bitfields, or integer values, reflecting the status of various components. It's usually designed so that there is at least one part, which 'if zero', reflects everything being OK.
So this might well have a 'serial_comms_error' bit, a 'calibration_error' bit, a 'usb_error' bit, a 'temperature_error' bit, etc.. Then every software component, can at any time check this for zero, and 'know' that the major parts of the system are working. |
|
![](templates/subSilver/images/spacer.gif) |
kWoody_uk
Joined: 29 Jan 2015 Posts: 47 Location: United Kingdom
|
|
Posted: Wed May 25, 2016 5:21 am |
|
|
Thanks Ttelmah,
The status bits suggestion sounds like a good idea. I think I'll borrow that from you for my current and future projects :-)
Cheers,
Keith |
|
![](templates/subSilver/images/spacer.gif) |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|