Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How, exactly, to use file-wide static variables in C?

I'm going round and round in circles trying to work out how to implement variables that need to be accessed by multiple functions within a [.c] file.

I have been ploughing through thread after thread on Stack Exchange and other Google searches where the general [but certainly not unanimous] consensus seems to be that file-wide static variables are fine, yet, you should pass variables (or at the very least pointers to variables) into functions and not just have any old function access the static file-wide variable (i.e. one that is declared outside of any function). Some people have said file-wide statics are essentially as bad as globals, but give no indication of how to avoid globals if not with file-wide statics!

However, at some point, even if you pass pointers to the file-wide static variable from function to function, some function has to originally access that file-wide static variable. Also, I cannot see a way where just one function within the .c file can be the sole function that accesses that static variable, because not all functions that will need the static variable would go through one single function.

It seems to me that you could have a function that does nothing but holds a static variable and returns a pointer to that static variable. Any function that needs to access that variable calls that function, gets the pointer to the variable and does what it needs to do with the variable. This kind of thing:

struct PacketStruct* GetPacketStructPtr(void)
{
    static struct PacketStruct Packet;

    return &Packet;
}

I've seen some people here say, yep, that's how a singleton factory is built (whatever that is) and it's completely valid, yet others say it's dangerous (but without really explaining why it's dangerous), others have said it's poor practice (I think they said it was inefficient, but I've read so much today I could be wrong).

So, what I am trying to ascertain is this:

  1. Are file wide variables OK?

  2. If so, given that it seems so wrong just to have all functions access that file-wide static variable and not pass pointers to it [the static file-wide variable] - as much as anything to make function re-use with different variables possible - can you just decide the first function that needs to access the file-wide static does so and then passes pointers all the way down to other functions? I really hate the look of code that just access the file-wide static variable, even though it also seems a little daft passing a pointer to something that the function can access anyway.

  3. If file-wide static variables are not valid, given that this is not multi-threaded and just a run-to-complete program on an embedded micro, can/should I use that way of passing a pointer to the function-wide static variable to any other function that needs access to the variable?

  4. If none of the above, how on earth do you avoid the dreaded global variables? This question of not using globals seems to have been tackled a zillion times here but without any concrete examples of how to do it. There is an awful lot of contradictory advice knocking about here, let alone on the rest of the web!

I stress this is single thread, not re-entrant and all relatively simple.

Hopefully, this gives some more idea about what I'm trying to do:

#include "work_order.h    
        // This is work_order.c

    // Nothing outside of this file needs to access the WorkOrder struct
        static struct WorkOrderStruct WorkOrder;     

        // Package up a work order - *data is a pointer to a complete serial package
        int16_t CableConnectOrder(uint8_t *Data)
        {

            if (UnpackagePortInformation(&WorkOrder.PortA,&Data) == CARD_UID_NOT_FOUND)
                return CARD_UID_NOT_FOUND;
            if (UnpackagePortInformation(&WorkOrder.PortB,&Data) == CARD_UID_NOT_FOUND)
                return CARD_UID_NOT_FOUND;

            AddNewKeysToWorkOrder(&WorkOrder,Data); 

            WorkOrder.WorkOrderType = CONNECT_CABLE_REQUEST;
            WorkOrder.Flags.SingleEndedConnection = FALSE_BIT;
            WorkOrder.Flags.PortACableRemoveRequest = FALSE;
            WorkOrder.Flags.PortBCableRemoveRequest = FALSE;

            return ConstructCableOrderRequest(&WorkOrder);

        }


        int16_t ConstructCableOrderRequest(struct WorkOrderStruct *WorkOrder)   
        {

        // This function is accessed by other Work Order requests and does the rest of the        // packaging of the work order
        // It can also pass the work order information further down
        DoOtherStuff(WorkOrder); // Kind of further passing that might happen


        }

        int16_t CheckAdditionalInfoAgainstWorkOrder(struct WorkOrderPortUpdateStruct *Update)
        {

            // Compare connection information against the previously set-up work order
            // Needs to access the static WorkOrder structure as well. Also, can call other
            // functions that needs to access the static function
        WorkOrder.Foo = Update->bar;
        DoYetMoreOtherStuff(&WorkOrder); // This is not real code, but the general kind of idea

        }
like image 883
DiBosco Avatar asked Nov 11 '22 21:11

DiBosco


1 Answers

More information on what you're doing would be helpful. I often do embedded system programming where globals/file-wide statics are an absolute must due to interrupts. If that is what you're working on - go for it.

Re: A single function that creates the variable and passes a pointer to all other functions... Your "single function" would be main. I'll often create code like so...

struct PacketStruct {
    char name[128];
    uint8_t age;
    float bac;
}

void setup (PacketStruct *packetStruct, ...);
void foo (PacketStruct *parameter);
void bar (PacketStruct *parameter);

int main (void) {
    PacketStruct ps;

    // Initialize all variables"
    setup(&ps);

    // Run program
    foo(&ps);
    bar(&ps);

    return 0;
}

void setup (PacketStruct *packetStruct, ...) {
    strcpy(packetStruct->name, "Squidward");
    packetStruct->age = 16;
    packetStruct->bac = 0.11;
}

I like this because ps is not a global variable, you do not have to dynamically allocate memory (though you could just as easily do so), and it becomes accessible in all functions.

Again, if you post your full code (or a snippet showing how it's used) we might be able to give you some applications specific advice.

-Edit- Since you're mentioning file-wide, I'm guessing that means you're not using this variable in the same file as main. In that case, my sub-files will have functions like filename_init(...)...

/* File:   FooBar.c
 */

#include "FileWithPacketStructAndOtherCoolThings.h"

// "g_" sits in front of all global variables
// "FooBar_" sits in front of all file-wide statics
static PacketStruct g_FooBar_ps;

FooBar_init(void) {
    strcpy(g_ps->name, "Squidward");
    g_ps->age = 16;
    g_ps->bac = 0.11;
}
like image 135
DavidZemon Avatar answered Nov 14 '22 21:11

DavidZemon