embedded software boot camp

Efficient C Tips #5 – Make ‘local’ functions ‘static’

Saturday, December 13th, 2008 by Nigel Jones

In my humble opinion, one of the biggest mistakes the designers of the ‘C’ language made, was to make the scope of all functions global by default. In other words, whenever you write a function in ‘C’, by default any other function in the entire application may call it. To prevent this from happening, you can declare a function as static, thus limiting its scope to typically the module it resides in. Thus a typical declaration looks like this:

static void function_foo(int a)
{
}

Now I’d like to think that the benefits of doing this to code stability are so obvious that everyone would do it as a matter of course. Alas, my experience is that those of us that do this are in a minority. Thus in an effort to persuade more of you to do this, I’d like to give you another reason – it can lead to much more efficient code. To illustrate how this comes about, let’s consider a module called adc.c This module contains a number of public functions (i.e. functions designed to be called by the outside world), together with a number of functions that are intended to be called only by functions within adc.c. Our module might look something like this:

void adc_Process(void)
{
 ...
 fna();
 ...
 fnb(3);
}
...
void fna(void)
{
  ...
}

void fnb(uint8_t foo)
{
 ...
}

At compile time, the compiler will treat fna() and fnb() like any other function. Furthermore, the linker may link them ‘miles’ away from adc_Process(). However, if you declare fna() and fnb() as ‘static’, then something magical happens. The code would now look like this:

static void fna(void);
static void fnb(uint8_t foo);

void adc_Process(void)
{
 ...
 fna();
 ...
 fnb(3);
}

...

static void fna(void)
{
 ...
}

static void fnb(uint8_t foo)
{
 ...
}

In this case, the compiler will know all the possible callers of fna() and fnb(). With this information to hand, the compiler / linker will potentially do all of the following:

  • Inline the functions, thus avoiding the overhead of a function call.
  • Locate the static functions close to the callers such that a ‘short’ call or jump may be performed rather than a ‘long’ call or jump.
  • Look at registers used by the local functions and thus only stack the required scratch registers rather than stacking all of the registers required by the compiler’s calling convention

Together these can add up to a significant reduction in code size and a commensurate increase in execution speed.

Thus making all non public functions not only makes for better code quality, it also leads to more compact and faster code. A true win-win situation! Thus if you are not already doing this religiously, I suggest you go through your code and do it now. I guarantee you’ll be very pleased with the results.

Next Tip
Previous Tip

A Request …

If I’m to believe the statistics for this blog, it appears that I’m gradually building a decent sized readership. Furthermore many of you choose to come back and read the latest postings which tells me that I’m doing something of value. Anyway, if this describes you, I’d be obliged if you’d encourage your colleagues to read the blog and also to post comments / questions. Why do I ask this? Well, an increased readership has several benefits, for both me and you the readers.

  • I believe quite passionately about improving the quality of embedded systems. Those of us that are working in this field collectively have an enormous impact on the world. Thus anything that helps improve the quality of embedded systems in turn helps improve the world. (I appreciate that this is a little melodramatic. It is, however, true).
  • Writing about something is the best way to I know to find out if I truly understand it. Thus, the very act of publishing a blog causes me to improve my skills and knowledge.
  • Some of the (too few) comments I get are quite profound and often instructive. Thus I also learn in this way.
  • The bigger the readership I have, the more inclined I am to publish. If I’m publishing things of value, then presumably the readers benefit.

Anyway, if you concur, then please encourage your colleagues. If you don’t, then that’s OK as well.

Thanks for reading.

Home

Tags:

42 Responses to “Efficient C Tips #5 – Make ‘local’ functions ‘static’”

  1. victorh says:

    I believe that using the same keyword ‘static’ for two different concepts (namely: storage class specifier and function scope) is the main reason for some many people ignoring the benefits of making functions local.Personaly, in my projects I use to#define LOCAL static, so make clear with LOCAL void foo (void); that I want foo be a local function for its module.Moreover, this helps when I need to find some static variable: when I fire the search window in my editor, it does not stop in “false positives”, i.e., static-defined functions.(Nigel, please delete my previous comment)

    • That is a useful workaround. I trapped myself in the same way in the past. And I openly admit that I simply never knew that static functions in C are module local until recently.

      By that little define one adds quite a bit of readability to the code, yet complies to the standard.

      Something valuable learned here!

      Matthias

      • Gauthier says:

        Actually, I like to think of static as always having the same meaning: “make that scope-limited, but with lifespan as long as the whole program”.
        This definition applies to all the three common uses of static (local var in a function, global var in a module, function in a module).

        I understand though, that functions already have all max lifespan, and that local vars are already scope-limited. But I think that this definition helps understand why the same keyword is used in all cases.

        I have very little insight in compilers, but I suspect that the parser does just that when encountering static?

  2. Bruno Santiago says:

    Wonderful tip victorhI will use it. :DAnd NigelI´m already encouraging people to read your blog and your “C Test”.I´m about to graduate on Eletrical Engineering and I don´t have any formal study on programming. My knowledge is mostly self-taught and I learn a lot with your blog.

  3. Ignacio Piqueras says:

    Well, I am software engineer and I have some formal study on programming, but I assure you that embedded programming is still unknown in many Faculties.I work with MCUs only for two years (8051 and ARM) and I find it amazingly interesting. I try to learn about “good coding” and your articles -and this blog- are really useful. I have a lot to learn.So I wanted to thank your job! Keep posting!

  4. Uhmmmm says:

    @victorhI see both those uses of static as two cases of the same concept. For static local variables, the varaible is really a global variable – it exists for the entire life of the program, just like any other global variable. The only difference is that it’s only accessible (by name anyways) within the function it’s declared in.Similarly for static functions: they are only accessible (by name) within the module they’re defined.In the case of static functions, you can pass pointers to the functions to other parts of the program to user use it from other compilation units. I presume you can with static local variables as well, but I’ve never had the need to.

  5. Nigel Jones says:

    I’ve never tried to pass the address of a static function to a function outside the module. I suspect that you’d have to engage in some casting in order to do it. Notwithstanding this, doing so would IMHO be incredibly dangerous and foolhardy. However, I take your point that static in of itself does not provide rock solid protection.

    • David Brown says:

      “static” functions and data (whether the data is file-scope static or function-scope static) are the same as any other global functions or data, but with limited visibility. Their lifetime scope is global – they exist from just before main() starts and live until just after main() ends. But their name scope is limited to the enclosing scope (compilation unit or function). This simply means that you can’t access them by name outside that scope.

      But since they still exist, you are free to take their address and pass it around the rest of the program. Passing the address of a function-local static variable, for example, is perfectly valid and perfectly safe.

      Of course, the optimisation advantages of statics are mostly lost if you take its address and pass it outside the current compilation unit. For example, the compiler will generate the full code for a static function rather than inlining it if you’ve taken its address. And a “static const int” might be optimised away entirely by the compiler if you don’t take its address.

  6. Uhmmmm says:

    I’ve seen it used to do object-oriented C code. You have some way to create an object with some standard, known interface. The object contains a vtable – just a structure containing pointers to functions with known signatures. The function that created the object fills in the vtable (or simply points it to a static copy somewhere, or similar). The actual functions pointed to are often made static, to avoid cluttering the namespace.

  7. todd says:

    @ Uhmmmm.Actually static local variables are quite useful. Because they maintain state between calls, they can be used to create state variables in state machines for example. Or maintain timer counts, etc.But they are local, so they use the smallest applicable scope, which is good practice, for both optimization as well as just good style.The only negative I've seen is with debugging. Some compilers put them in a different segment and then forget to make that segment available to the debugger. But that was a few years ago, so probably not an issue anymore.

  8. Rick Mann says:

    Note that this is dependent on the compiler. LLVM, for example, is capable of whole-program optimization, and can inline method calls across translation units.

  9. Nigel Jones says:

    Rick – are you advocating not doing this if your compiler happens to perform global program optimization?

  10. MarkK says:

    At my current job we pass static function pointers around extensively – to other modules as well. This is particularly useful for timer expiry routines, and other callback functions which should not have a public interface.As for the local call/long call, I haven't even thought of the potential problems with them. However it should not be an issue unless there is a different assembler return instructions for each.

  11. ashleigh says:

    Keeping things static (local to a module) is just plain good practice.It makes the job of the linker easier (there is basically a whole bunch of stuff it never even has to go near). It avoids namespace pollution (which is REALLY important in compilers that dont support namespaces in their own right). [NOTE here that an exported naming convention helps enourmously. More below.]It avoids name clashes (I once spent WEEKS tracking down a weird defect when some of my code was linked with some vendor code – we both used the same name – so the linker made them the same piece of storage. More important for module-local variables).Once you do some embedded Ada programming and realise that some important principles are forced on you, its easy then to use those in C as well:- Stuff used in a unit should be local only to that unit (and not visible by accident or design ANYWHERE ELSE). [This does not apply to function pointers.]- Stuff exported by a unit should always be explicitly exported, by declaration in a header file, and by explicit action in the body.- Header files are only for exports. If you have a variable, define, constant, enum, typdef used only in a units "C" file, then put in in the C file, never in the header.And… use a naming convention. The file "fred.h" should prefix EVERY exported thing from the fred unit with "fred_". (This is similar to the idea of namespaces, object references, and in Ada, eliminating the "with" statement so that you must refernce things from another unit using the dotted notation.)This approach makes it REALLY easy to see where an identifier came from, at a glance, without needing some clever GUI/IDE to track identifer names and their originating units.Another dirty trick I use is to define:#define exported extern#define exportThen in header files, I write:exported UINT8 fred_do_calc(stuff);Here – exported is like a declaration: "this is a thing that is being exported" – so its visble in the outside world.And in the C file that corresponds, the function would appear as:export UINT8 fred_do_calc(stuff){more stuff}And here – "export" is like a directive: "EXPORT THIS FUNCTION!"I find this greatly enhances the readability and understandability of the code.I also lump ALL module local stuff together with a big comment block "LOCAL FUNCTIONS", and all exported stuff together under a comment block "EXPORTED FUNCTIONS".Imposing this discipline into the source code tends to force a more logical way of thinking about whats exposed and whats not. Using a standard empty header and body file template with all these blocks in makes it a no brainer when writing a new code unit.

  12. Ten-Seng Guh says:

    @todd"But that was a few years ago, so probably not an issue anymore."Yep, still an issue with Code Composer 3.3 at least.

  13. Greg Nelson says:

    Ashleigh writes: "The file "fred.h" should prefix EVERY exported thing from the fred unit with "fred_"."That's an interesting approach. Our style conventions do the opposite. Everything function for variable that is, can be, or should be "static" is prefixed with a module-specific string (e.g. CPU_write_date()), while the exposed user interface is all done with mixed case and no prefixes (e.g. SetDate()).The mixed case helps by ensuring that the exposed functions aren't overlapping with library functions. Leaving off the prefixes makes the calling code (at least to those of us brought up this way) more readable, because it isn't so long-winded. The prefixes group the functions together in various debugging tools so that the relevant pieces are easier to find.In a way, though, the ADA/C debate always reminds me of the VHDL/Verilog debate. Our company chose Verilog after reading about a challenge where about 20% of the Verilog coders got a working program, while 100% of the VHDL coders hadn't finished writing when their time ran out!

  14. GroovyD says:

    in c++ how about the difference between using static variables within a module and declaring the variables as private within the class, which is preferred? i can see how static to a function would be preferred over private to a class. often times i find myself moving static module level variables into the class or structure and declaring them as private. i guess the real difference is by declaring private within the class you get an instance for each instance of the class instead of one instance over all instances of the class. perhaps this is something to be aware of, that a static variable within a module could be used across more then one instance of that object.

  15. Bernhard Weller says:

    So I was wondering, is static treated the same in C++ as in C?
    I happened to write a new software for a new product, and as I read things here, I tried to write a whole lot of better code than before.
    So I tried out defining all my module-specific functions as static, and only the few ones for global interaction as not static. It didn’t reduce my code size by any byte, but the functions can not be accessed outside the module they are used in, which is quite nice.

    I also noticed that this effectively prevents you from declaring local functions in a header file, giving you a compiler warning, that the function was referenced but not defined.

    Coming from object oriented programming, the use of static in a functional approach didn’t make any sense (for functions that is). Well maybe even more so, coming from Java, you can assign a function of a class as static which implies a lot of things, like it doesn’t change the actual object of the class, and you don’t even need an object to call that method. If you come from this context why would you declare a function as static?

    So thank you for pointing this out.

    • Nigel Jones says:

      My recollection is that C++ does differ from C in places in its use of static. However, I haven’t programmed in C++ for a few years, so I will let more knowledgeable folks comment on this.
      Where declaring local functions as static will normally reduce your code size is if you also declare them to be inline. Typically in a C program, the compiler will not inline a non static function since it has to keep a copy around in case it is called from outside the module.
      You are dead right that static prevents you from declaring local functions in a header file. This is a good thing. I prototype my local functions in the module that they appear.

      • David Brown says:

        C and C++ use “static” in the same way, at least unless you are doing something really obscure as a challenge to the language-lawyers.

        If a function is declared “static”, and you don’t take its address, then the compiler knows it cannot be called outside the module. It is therefore free to inline it, remove it, simplify it, change its calling conventions, or otherwise optimise it as it sees fit. Whether this will reduce code size or not will depend on the compiler and your optimisation settings, but you are giving the compiler the best chance to do a better job.

        With modern compilers, you should not have to explicitly declare a static function as “inline” unless you believe the compiler will do a poor job on its own (compiler heuristics are normally good, but not perfect). In those cases, you may need something additional such as __attribute__((always_inline)) to force the behaviour you want. Of course, sometimes adding “inline” makes your code clearer, in which case it’s a good thing. But otherwise let the compiler make the decision.

        There are occasions when you want to use “static” in header files. But remember that the data or functions are then independent for each module that includes the header. Thus the main uses for “static” in header is for a modern and type-safe (at least, as type-safe as it gets in C/C++) replacements for pre-processor defines. For example, you can replace:

        #define magicNumber 100
        #define magicFormula(x) ((x) * (x) + magicNumber)

        with

        static const int magicNumber = 100;
        static inline int magicFormula(int x) { return x * x * magicNumber; }

        • MarkM says:

          C++ retains the C semantics of ‘static’ and adds an additional use for it in support of object-oriented programming: Within a class/struct, a static member of the class/struct has a single global instance, can be referred to without dereferencing an object of the class/struct’s type, and cannot implicitly access members of any such object. A static class/struct method (function) does not have a ‘this’ pointer, as it cannot be invoked on an object of the class/struct.

          I think OO programming is not widely used within embedded programming, so I’ll clarify: Within C++, a ‘class’ is basically the same thing as a ‘struct’. The ONLY difference, and I truly mean ONLY, is that in a struct, members are by default public (anyone who has a pointer to an instance of a struct has free access to its members), while in a class, members are by default private and must be declared public in order for outsiders to access them. In C++, a class/struct can “contain” functions, which in OO parlance are usually called ‘methods’, though it means the same thing. Instead of having bits & pieces of code scattered throughout various functions all over your codebase that operate on data in a class/struct, in OOP, you group the data and the bits & pieces of code that operate on that data together inside the class/struct. And generally, if a you don’t put any functions in the class/struct, you call it a struct, otherwise you call it a class. Now when you create objects of these classes, they don’t get extra copies of the code for the functions inside the class, there’s really only one copy of the functions, but conceptually you can just go ahead and think of it as if they all have their own copies. But ‘static’ members are different: conceptually, you should (because it’s correct) think of a static member as having only a single instance regardless of how many objects of that type are created. So, for example, you could have a single static bool flag to flip all objects of that type between debug & normal modes. When you set the static bool debug flag true, it’s true for all objects of that type. In this sense, it’s sort of like a global variable, but it’s grouped within the class definition because OO programmers tend to shudder at the thought of anything other than main() being global. In addition to static class variables, a class can have static methods. Static methods (functions) can be called directly; in a sense, they’re sort of like global functions, but, ah, no, forget I said that. If you have non-static class methods, these can only be “called on” an object of that type that already exists. So, for example, if you have a class that contains a set of numbers, you might want to sort them. So you would have a member method to tell an object of that type to sort itself. If you have an object of your class numberList that you’ve named “myPhoneList”, you could call myPhoneList->sort(), and it would sort itself. But sometimes the purpose of a method doesn’t exactly apply to a specific object of that type. For example, you might want to know how many numberLists objects are in existence. For that purpose, you don’t need any specific object of that type to find this out; your numberList class probably has a static member variable named instanceCount, but like all good OO programmers you made it private so nobody can see it. But you made a method so folks can find out its value without accessing it directly, and of course it’s a static method because it doesn’t deal with any data related to any specific numberList. Now anybody who knows that the numberList class exists can find out how many numberLists are out there floating around in memory without actually knowing where any of them are.

      • Ajit says:

        I have a disagreement/query with this line in above reply
        >>Where declaring local functions as static will normally reduce your code size is if you also declare them to be inline.
        If the function is made inline, then size of code would increase, because everywhere that function is called, there would be a code replacement, rather than a call. So can u elaborate how inline actually reduces code size.

        • Nigel Jones says:

          If the overhead of calling a function is greater than the size of the function then inlining gives a size reduction. If the overhead is less than the size of the function then inlining may still give a reduction in code size as a lot of optimizations are performed across a function. Thus the optimizer can now see the entire picture with an inlined function and can optimize accordingly. Obviously if the inlined function is large and called more than once then inlining will not save code space.

  16. […] Originally Posted by millgates As I said, if you define structure locally, you can only use it in the function in which it is defined, so unless you are defining the function inside the function where the struct is defined (which in C is not possible), you can not use that struct as an argument or return type of any function. If you want to do that, declare the struct globally. About nested functions in C: link. To my knowledge, defining local structures and/or local (nested) functions is not the preferred way of programming in C (as opposed to, say, scheme or ruby). You can make a function local to a compilation unit by defining is as static. Such a function is not visible outside of the file it is defined in. Static functions has some other advantages. […]

  17. Hari says:

    I have a question regarding usage of static functions in C language

    In embedded system where memory is expensive; does declaration of too many static functions can take up more memory space?

  18. Dhaval says:

    Your post was really helpful. Keep posting.
    Though I saw the post today only, via google search, i’ll follow it henceforth.

    Thanks.

  19. Mohamed Muslim says:

    thank you very much, very simple powerful topic

  20. mfonseca says:

    I do my best to make locally scoped functions static whenever I can (remember!). That being said, with the advent of LTO(link time optimizations) in compilers, It seems the usefulness (in terms of performance anyways) of static functions become greatly diminished (as the compiler can figure it out anyway)…? From a code inspection standpoint, it does still make the code more succinct for knowing if a function is used elsewhere. I’m just starting to fool around with lto optimizations so I’m interested what others have to say?

  21. Erik Sweden says:

    What do you think of using static local variables for saving stack space?

    I’m using a RTOS for my embedded system and it is really important to know that the stack frame does not overflow. If a task makes a function call and the call depth is large the stack could overflow (and the application will halt).

    One way to minimize this risk would be to make all local variables static. Then they will not end up on the stack.

    What do you think of that strategy? Any pitfalls?

  22. Prashant says:

    As you said specifying static as scope specifier for functions can cause compiler to inline the functions. Is this the most probable approach taken by compilers, or compiler does this only under some optimization options.

    If this is the most probable option then it can increase the size of the code. In this case can we tell compiler to avoid inlining of static functions.

    Thanks in advance.

  23. Hi Jones,

    I had just got the message from compiler “Multiple function definition…” so I found here the solution. I had the same function name in different modules, but I’m not sure if this occured because I did architecture wrong.

    Thank you!

  24. Ramakrishna says:

    Excellent tip, thanks for explaining the intricacies of the tip

  25. Bill says:

    Where’s the before & after performance metrics to back this claim?

  26. Yuri Gribov says:

    To identify functions which could be made static in large existing codebase one could use a tool like https://github.com/yugr/Localizer

  27. jim says:

    Such a fantastic blog! Just discovered today (4/22). I see the most recent archives are for 7/17. I hope and pray you are well and still blogging.
    sincerely, jim

Leave a Reply

You must be logged in to post a comment.