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!