I'm wrestling with the minutiae of variable lifetimes in C (of course)!
I've learnt that I shouldn't leave variables that were defined within functions floating around, expect when the memory for that variable has been allocated with malloc
. What I can't work out is how I know when a pointer is good to pass out of a function.
In the following example, there is a fundamental difference between fptr
and p_uniqueId
:
int initialize_db(ToyDatabase* p_tdb, const char* filePath) {
FILE* fptr;
if ((fptr = fopen(filePath, "wb")) == NULL) {
printf("Error! opening file");
return -1;
}
int uniqueId = 2;
int* p_uniqueId = &uniqueId;
p_tdb->pFile = fptr; //Presumably not a dangling pointer, how to tell?
p_tdb->pId = p_uniqueId; //Will become a dangling pointer, not Ok!
return 0;
}
int cleanup(ToyDatabase* p_tdb)
{
fclose(p_tdb->fptr);
//Do I need to do anything else with this fptr? Maybe p_tdb->fptr = NULL;????
return 0;
}
In the example, the address pointed to by p_tdb->pId
won't be valid outside the initialize_db
function. But presumably p_tdb->pFile
will be valid, presumably because fopen
allocated memory for fptr
.
Is there something in the documentation for fopen
that I'm looking for that delineates these two cases, or is it just assumed that any function returning a pointer has been malloc
d?
CodePudding user response:
First, p_uniqueId
unambiguously points to uniqueId
which has automatic storage class. If it were a global, malloc'd, static, etc then you'd be fine.
Is there something in the documentation for fopen that I'm looking for that delineates these two cases, or is it just assumed that any function returning a pointer has been mallocd?
The manpage doesn't say much. It just says that a FILE*
is returned, which you can safely use and pass around. It might not have been necessarily malloc'd, as it could have been allocated in a static buffer or other implementation-dependent storage. You thus can't assume that it can be safely free
d. At best you leave a dangling file descriptor because free
doesn't know anything about file streams.
The only remarks about cleanup that I found were in the fclose
manpage:
[after calling
fclose
]... any further access (including another call to fclose()) to the stream results in undefined behavior.
//Do I need to do anything else with this fptr? Maybe p_tdb->fptr = NULL;????
You don't need to do anything, per se. You can leave p_tdb->fptr
dangling with the understanding that using it is UB. You could null it. You could replace it with a different file pointer. By setting it to NULL, you can detect that it doesn't point anywhere meaningful using a null-check. If you don't check it, it's just as harmful to use (UB wise) after being nulled as if you used it while it was dangling.