0

I am surprised that I didn't find the question already posted. Here is one that is similar but not what I'm getting at because it only talks about string literals.

Anyway, coming from Java and learning C a bit better. I love the options that C brings but I know I've got to be careful with memory management.

I have created my own timer utility as an exercise to get familiar with how to do things in C and came across this question... Here's a code sample first (Windows console app):

HANDLE createHPTimer(char *timerName, DWORD duration, UINT16 repeat, bool persist, void *callbackFunc, void *callbackArgs) {

    //...
    //...  (here I ensure struct hptGlobals is properly initialized and that *timerName does not already belong to the collection)
    //...

    HANDLE hHPTimer;
    hHPTimer = malloc(sizeof(HPTimerObj));
    if (hHPTimer != INVALID_HANDLE_VALUE) {
        if (hptGlobals.timerCnt >= hptGlobals.allocCnt) {
            //reallocate more room to global collection to fit this new timer
            hptGlobals.hCollection = (HANDLE*)realloc(hptGlobals.hCollection, sizeof(HANDLE)*(hptGlobals.allocCnt + hpt_ALLOC_RES));
        }
        //add new timer handle to global collection
        hptGlobals.hCollection[hptGlobals.timerCnt] = hHPTimer;

        //here I assign function parameters into the structure for later use
        //timerName is a c string therefore I am only saving the pointer reference
        ((HPTimerObj*)hHPTimer)->timerName = timerName;
        ((HPTimerObj*)hHPTimer)->status = hpt_INIT_NOT_STARTED;     //timer is initialized in the not started state
        ((HPTimerObj*)hHPTimer)->duration = duration;
        ((HPTimerObj*)hHPTimer)->repeat = repeat;
        ((HPTimerObj*)hHPTimer)->runCnt = 0;
        ((HPTimerObj*)hHPTimer)->persist = persist;
        ((HPTimerObj*)hHPTimer)->callbackFunc = callbackFunc;
        ((HPTimerObj*)hHPTimer)->callbackArgs = callbackArgs;

        hptGlobals.timerCnt++;
        return hHPTimer;
    } else 
        return INVALID_HANDLE_VALUE;    //return invalid handle if HPTimer creation failed
}

My HPTimerObj struct looks like this:

typedef struct {
    char *timerName;
    BOOL status;    //uses the following flags: hpt_INIT_NOT_STARTED, hpt_INIT_STARTED, hpt_INIT_PAUSED
    UINT64 timeData;//if the timer is currently running, this is the timestamp of the next scheduled callback; if the timer is paused, this is the elapsed duration when paused
    DWORD duration; 
    UINT16 repeat;  //number of times to run timer before it frees itself (use persist flag to keep timer (pause it) after runCount has been reached)
    UINT16 runCnt;  //number of times timer has triggered
    bool persist;
    void *callbackFunc;
    void *callbackArgs;
} HPTimerObj;

So I have three pointers that were passed into the function and I need to reference their data later: timerName, callbackFunc, and callbackArgs

This code works (for my test case) as written but I'm not sure it is safe because I do not know how to determine how memory for the pointer timerName was allocated -- is it in the stack or on the heap? Is it static or a string literal (which I think it would always be valid and safe). If this function is called from code where timerName was allocated on the stack then there is a possibility that it could be overwritten later. Am I correct in this thinking?

If I could always know how the memory was allocated then I could copy it from stack to heap if needed but I don't think that is possible (see this SO question). I think that the other two pointer arguments are always safe because one is a function pointer (I would assume would be global/static) and the other is supposed to be a pointer to a struct which must be on the heap, is that right?

To ensure that timerName always has valid data I could create a copy on the heap as follows:

//instead, to ensure timerName data is on the heap, copy it there
size_t timerNameLen;
timerNameLen = strlen(timerName);
char *newTimerName = (char*)malloc(sizeof(char)*(timerNameLen + 1));    //Must use free on ((HPTimerObj*)hHPTimer)->timerName when "killing" the timer
strcpy_s(newTimerName, timerNameLen + 1, timerName);
((HPTimerObj*)hHPTimer)->timerName = newTimerName;

Should I make this a habit in other similar situations or was it okay the way I first wrote it? I'd like to avoid duplicating values which may take extra time and memory if I don't need to. Are there any better approaches for this? Thanks!

Galik
  • 47,303
  • 4
  • 80
  • 117
DatuPuti
  • 629
  • 7
  • 17
  • 1
    Making a copy of the string is certainly the safest approach. In general, these types of questions are either answered by documentation, or by design guidelines. In other words, if you write the function and the code that calls the function, then it's up to you to determine the rules that the function and the caller must follow. If you don't control both sides (caller and callee), then you need to check the documentation to see how the memory is managed. There are no hard and fast rules. Every project has its own rules. – user3386109 Mar 04 '18 at 01:53
  • 1
    Okay, just wanted to make sure I wasn't missing something. I guess with a low-level language like C there is a lot more relying on documentation rather than things that are built-in to the language. Coming from Java is a huge difference (and very gratifying to be able to use pointers) but I can see there may be some advantages to all the strictness. – DatuPuti Mar 04 '18 at 02:08

0 Answers0