Not Keil specific; one for the 'C' experts:
Why would one put 'static' variables definitions in a header?
eg, in a header file:
/* * TCO count to temperature conversion table. */ static erTcoTableStruct tcoTableOPUS1[] = { /*{TCO,Temp,} */ {1,-99}, {4,-45}, {5,-40}, {8,-35}, {11,-30}, {16,-25}, {22,-20}, {29,-15}, {37,-10}, {48,-5}, {61,0}, {78,5}, {99,10}, {124,15}, {153,20}, {188,25}, {227,30}, {270,35}, {315,40}, {365,45}, {420,50}, {481,55}, {549,60}, {625,65}, {710,70}, {805,75}, {910,80}, {1010,85}, {1060,88} };
AIUI, the whole point of so-called "header" files in 'C' is to share stuff between source files;
But the whole point of the 'static' keyword (at file scope) in 'C' is to make stuff private so that it is not visible to other modules - ie, not shared.
So I can't see why one would want to have 'static' definitions in a header?!
It is also potentially a waste of memory - every inclusion of the header file will instantiate a copy of the static variable in it.
I don't think that's just "potentially" - it's for certain!
Especially in cases like the example I showed - which is quite a large table!!
To show their C prowess?
We had a guy here a while ago that took one of my projects and put ALL the contents of every header file into one super large header file.
"Why?", I asked, "So I only have to edit one file of course" came the reply.
--- Now why didn't I think of that?
This was the same guy who had a function that returned "TRUE", "FALSE" and 2.
--- I'll never forget his C prowess!
and put ALL the contents of every header file into one super large header file
I know of at least one commercial product that has that (I did not write that part, though...).
There IS something called the C specification...but who the heck bothers to read (and understand) anymore?
www.open-std.org/.../n1124.pdf
Indeed - I have a copy.
But I still don't see why having static definitions in header files would be a useful thing to do.
What am I missing?
I have a method of #inclusion that works in a highly structured environment. This does allow static to be used in a header file, but it is segregated from the rest of the included file(s).
/* ** UART.C ** */ #define UART_Module 1 #include "Includes.h" #undef UART_Module // NON MISRA, but deemed okay by me
The "Includes.H" file contains and controls all included files within the project.
/* ** Includes.H ** */ ////////////////////////////////////////////////// // COMMON TO ALL "C" MODULES // ////////////////////////////////////////////////// #include "TMS320LF2813.H" // SFR Mapping #include "SystemDefs.H" // System Definitions @include "Structures.H" // Global System Structures #include "gDefs.H // Global Definitions #incldue "gData.H" // Global Data Stores ////////////////////////////////////////////////// // SPECIFIC TO EACH "C" MODULE // ////////////////////////////////////////////////// //----------------------------------------------- #ifdef UART_Module #include "UART.H" #endif //----------------------------------------------- //----------------------------------------------- #ifdef SPI_Module #include "SPI.H" #endif //----------------------------------------------- //----------------------------------------------- #ifdef PWM_Module #include "PWM.H" #endif //----------------------------------------------- etc.
Each Header file would then be split into either module specific or 'extern' access:
/* ** UART.H ** */ #define MAX_ROWS 29 // "global" values to all that include this file #define MAX_COL 2 // "global" values to all that include this file /* ============================================ /* ============================================ ** ** Module Specific (Data Allocation) ** ** ============================================ */ #ifdef UART_Module .-----------------------------------------------. ; TCO count to temperature conversion table. ; '-----------------------------------------------' static erTcoTableStruct tcoTableOPUS1[] = { /*{TCO,Temp,} */ {1,-99}, {4,-45}, {5,-40}, {8,-35}, {11,-30}, {16,-25}, {22,-20}, {29,-15}, {37,-10}, {48,-5}, {61,0}, {78,5}, {99,10}, {124,15}, {153,20}, {188,25}, {227,30}, {270,35}, {315,40}, {365,45}, {420,50}, {481,55}, {549,60}, {625,65}, {710,70}, {805,75}, {910,80}, {1010,85}, {1060,88} }; /* ============================================ ** ** NON-Module Specific (Externs) ** ** ============================================ */ #else // not included by "UART.C" but referenced... // NOTE: "erTooTableStruct" is structured in "Structures.H" extern erTooTableStruct tooTableOPUS1[ MAX_ROWS ][ MAX_COL ]; #endif
Using this format, I can control what includes get included with each module, and when. In addition, I don't have to worry if each module has the proper 'global' header files, and avoids a mismatch or redundant inclusions.
--Cpt. Vince Foster 2nd Cannon Place Fort Marcy Park, VA
P.S. You are welcome: I rarely give out such information.
But why would you ever want to do that?
This could make some sense if each copy of the table had to be modified individually. The other possibility is that the author of the code didn't want to make the table 'global', so only the files that include the header can access it. Some kind of phobia of global variables. Either way, it looks strange.
Total Control
All data allocation on a module basis should be kept in a header file.
Why would you want to have distinct but identically-named and identically-typed objects in multiple files??
Again,
#ifdef UART_Module .-----------------------------------------------. ; TCO count to temperature conversion table. ; '-----------------------------------------------' static erTcoTableStruct tcoTableOPUS1[] = { /*{TCO,Temp,} */ {1,-99}, {4,-45}, {5,-40}, {8,-35}, {11,-30}, {16,-25}, {22,-20}, {29,-15}, {37,-10}, {48,-5}, {61,0}, {78,5}, {99,10}, {124,15}, {153,20}, {188,25}, {227,30}, {270,35}, {315,40}, {365,45}, {420,50}, {481,55}, {549,60}, {625,65}, {710,70}, {805,75}, {910,80}, {1010,85}, {1060,88} }; /* ============================================ ** ** NON-Module Specific (Externs) ** ** ============================================ */ #else // not included by "UART.C" but referenced... // NOTE: "erTooTableStruct" is structured in "Structures.H" extern erTooTableStruct tooTableOPUS1[ MAX_ROWS ][ MAX_COL ]; #endif
That won't work - you can't have an extern reference to something that was declared static!
Although the use of static CAN be circumvented, as shown, it is still not conforming with the C spec:
(1) An identifier declared in different scopes or in the same scope more than once can be made to refer to the same object or function by a process called linkage. There are three kinds of linkage: external, internal, and none.
(2) In the set of translation units and libraries that constitutes an entire program, each declaration of a particular identifier with external linkage denotes the same object or function. Within one translation unit, each declaration of an identifier with internal linkage denotes the same object or function. Each declaration of an identifier with no linkage denotes a unique entity.
3 If the declaration of a file scope identifier for an object or a function contains the storage class specifier static, the identifier has internal linkage.
Therefore, declaring static - by definition above - forces/restricts the identifier to be internal.
Sorry about being nit-picky.
I think you can, but I might be rusty on my "C." I only say that because you (ANDY) are an excellent embedded guy, and therefore I *might* be wrong on the extern to a static.
Dear Captain,
I think that doing what you propose really harms the chance of a less-informed "code-monkey" to correctly maintain C code. I mean, I have encountered too many software spaghettization instances so far, due to: * Incompetence * Sloth * A bad character * Job security * Arrogance
I don't want to add "control freak" to this list :-) :-) :-)
Tamir,
Although it might look 'complicated' it really isn't. All too many times people will re-include files that don't need to be included, and will sometimes include the wrong file (esp the SFR files).
Using my method, the code is very structured and all include files are in total control via the "Includes.H" file.
This also adds to the reliability of the code, not to mention repeatability during the debug phase.
Any code-monkey who cannot handle this structure should be taken out back and, well you get the idea.
I think once you have tried it, you'll stick with it.
I agree: having identically-named and identically-typed but distinct objects sounds very dodgy to me!
I think MISRA frowns upon it - even for automatic variables?
Yes, there is merit in having a single header that provides (by conditional compilation) both the definitions and the extern declarations of shared symbols - the practice is quite common.
Just to note that the code in question (in my original post) is not doing any of this definition-here-declaration-there trickery; It's just a straightforward 'static' definition that is included verbatim in any & every file that #includes that header.
Andy,
I think that 'static' does not mean 'private' unless you are declaring a function. Static, from what I remember, means that the data allocation remains in memory and is not optimized out. Thus an 'extern' to a static should be allowed.
I don't always agree with MISRA.
But since you & Tamir bring up the 'same name' issue, you must realize that the compiler shall complain if it doesn't appear in the 'extern' area in addition to the data-allocation area. So such maintenance shall be caught by the compiler.
Actually, they will be caught by the Linker!
And nothing can catch the error where you thought your were accessing the 'x' in this.c but you're actually accessing the 'x' in that.c...
Can you imagine to total nightmare one code-monkey might end up in, when such statics are passed as parameters, and the wrong/inappropriate configuration is used, but it does not affect the function that is called so the code-monkey can't immediately tell what's happening...? In other words: Sorry captain, I usually agree with just about anything you write, but not this time...
The key dis-advantage that I see to headers of the form
#if MAKE_DEFINITION unsigned int x; // This is a definition #else extern signed int x; // This should be the corresponding declaration #endif
is that the compiler only ever sees either one or the other; so it can never verify that the two match - or, conversely, report that they don't!
That's why I prefer: file1.h
extern signed int x; // declaration
file1.c
#include "file1.h" // The declaration is visible unsigned int x; // definition: // The compiler can now see both declaration & definition // and, therefore, can report any discrepancy
file2.c
#include "file1.h" // The declaration is visible; // this file just references the external object
Not quite.
static applied to an automatic variable, as you say, makes its storage permanent.
But static applied to a file-scope variable does make it "private" - or, in spec-talk, gives it "internal linkage".
Andy, I understand your concern. I used copy-n-paste for the externs required.
You are right in that people with lesser skills shall screw things up really fast.
All I can suggest is that you try 'my' method for a small project, and you'll get the hang of it.
The reason I do it this way, is that I realized that the #include process is generally uncontrolled, and I wanted a way of controlling which files get included and when... and to ensure that all common files are indeed common to all modules.
Came accross this 'hiring' test for embedded programmers that may amuse and challenge some. Regarding use of the keyword static:
6. What are the uses of the keyword static?
This simple question is rarely answered completely. Static has three distinct uses in C:
(1) A variable declared static within the body of a function maintains its value between function invocations.
(2) A variable declared static within a module, (but outside the body of a function) is accessible by all functions within that module. It is not accessible by functions within any other module. That is, it is a localized global.
(3) Functions declared static within a module may only be called by other functions within that module. That is, the scope of the function is localized to the module within which it is declared.
Most candidates get the first part correct. A reasonable number get the second part correct, while a pitiful number understand the third answer. This is a serious weakness in a candidate, since he obviously doesn't understand the importance and benefits of localizing the scope of both data and code.
...BRUTAL...
The complete test, if your interested, is here: www.embedded.com/.../0005feat2.htm
Indeed - so, again, why would one put static definitions in a header file?!
You're declaring them static to localise their scope - so why try to extend it by putting them in a header?!
Unfortunately, this seems to be a 'loophole' within the standard ,as no real information is defined for header files. This should, at the very least, generate a warning/error message.
According to spec: "If, within a translation unit, the same identifier appears with both internal and external linkage, the behavior is undefined."
(A translation unit being a source file and all headers referenced in it.)
Furthermore, the only requirement of the 'translation enviornment' (ie: compiler/linker) is:
"A conforming implementation shall produce at least one diagnostic message (identified in an mplementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined."
But since each source file is it's own 'translation unit' there are no requirements to flag this usage.
Moral: You can really do some damage without knowing it. I hope none of this code is used within heavy machinery.
I hope none of this code is used within heavy machinery.
Don't worry, the code is in the very capable hands of the good old caption. Oh, by the way, he is the guy that protects us from the ultimate bad guys - his stuff explodes. Heavy enough for you ? ;-)
As long as the vector is outgoing.
View all questions in Keil forum