-1

whenever I read dangling pointers article. I can not anticipate the potential risk here. What is the issue in below code if I do not initialize the pointer, or I do not assign ptr to NULL after deal locating malloc? Why it becomes dangling, what is the risk here?

int main()
{
    int *ptr;      //what is risk here?
    
    ptr = (int *)malloc(sizeof(int));

    // After below free call, ptr becomes a
    // dangling pointer
    free(ptr);
    
    
    //ptr = NULL;           //what is risk here if i do not assign NULL?
}
Ravi
  • 173
  • 1
  • 10
  • Does this answer your question? [What is a dangling pointer?](https://stackoverflow.com/questions/17997228/what-is-a-dangling-pointer) – Silidrone Jul 17 '21 at 09:24
  • The risk is if you use it, it doesn't point to valid data anymore. – Silidrone Jul 17 '21 at 09:25
  • Does this answer your question? [Setting variable to NULL after free](https://stackoverflow.com/questions/1025589/setting-variable-to-null-after-free) – bereal Jul 17 '21 at 09:31
  • What "_dangling pointers article_" are you referring to? If it does not make the issues clear, it is not a particularly good article. – Clifford Jul 17 '21 at 10:33
  • See [this answer](http://stackoverflow.com/questions/37087286). – Steve Summit Jul 17 '21 at 10:33

3 Answers3

2

There is only a dangling pointer risk if you use a pointer when the data it points to is no longer valid. That is not the case in your code snippet provided.

If you do use it while the data it points to is invalid (uninitialised before the malloc or dangling after the free), all bets are off, that's undefined behaviour (UB).

It's also UB if you use the NULL that may be returned if your malloc failed, you should always check for that. And, because of that, setting it to NULL after the memory it points to ceases to be valid will not help you at all. It's just as much UB to dereference NULL as it is to dereference memory that's been freed.

You can make your code a lot safer with something like:

int main(void) {
    int *ptr = malloc(sizeof(int));
    if (ptr == NULL) {
        puts("Could not allocate");
        return 1;
    }

    // safe to use pointer here.
    free(ptr);
    // but not here.
}
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
1

The risk is not in the code shown, the risk is in the code after maintenance, modification and reuse. Or in "cargo-cult" copying of the anti-pattern. If for example you added a great deal of code between the initialised ptr declaration and the malloc(), it may no longer be clear that the pointer is not yet valid.

Dereferencing a null pointer "by accident" will normally be trapped as a run-time error, so likely to be detected during testing and development and reported at the point of error. Dereferencing a pointer that is a valid address but say refers to a block returned to the heap for re-use may have no immediate observable impact, but may later cause unrelated code to fail, possibly after deployment and in unpredictable ways - those bugs are very difficult to track down.

Dereferencing a pointer that simply has not been initialised, will have undefined behaviour, it may appear to work, it may trigger an exception, it may cause the code to appear to fail at an unrelated location in the code - again such bugs are hard to track down unless you were lucky enough for it to fail immediately - if it happened to be NULL or an invalid address triggering an exception for example.

Besides that, there are no advantages to the "unsafe" code:

    int *ptr;      //what is risk here?
    
    ptr = (int *)malloc(sizeof(int));

over both safer and simpler code thus:

    int* ptr = malloc( sizeof(*ptr) ) ;

Note other improvements in this:

  • do not cast a void-pointer to the destination type,
  • use the sizeof the object pointed to (or a multiple thereof) rather than an explicit type.

Remember in modern C, is is no longer necessary to declare all variables at the start of a brace-block ({...}) - though you will still see that anti-pattern too - so there are few excuses for not initialising a pointer with a useful value on instantiation.

Clifford
  • 88,407
  • 13
  • 85
  • 165
1
int main()
{
    int *ptr;      //what is risk here?
    
    ptr = (int *)malloc(sizeof(int));

Here you have 2 risks. The first one is that in real project between these 2 lines of code it could be a lot of another code. And there is a chance that you (or somebody else who maintains this code) will try to dereference this pointer before it's initialised.

The second one is that the return value of malloc is not checked, so in case if malloc failed, you will not know that it failed.

free(ptr);


//ptr = NULL;           //what is risk here if i do not assign NULL?

When you make free() you don't really "free" the memory (you don't set all bits in allocated chunk of memory to '0') you just mark this memory as "it could be allocated again in future allocations). But your pointer is still points to this chunk of memory. So after some period of time this chunk of memory could be used for another allocation. Some new data will be stored to this chunk (data will be overwritten). And your pointer will point to the memory that stores not relevant data. And you (or worth - somebody else who maintains your project) could use this pointer without knowing that he points to non-relevant data.

Igor_M
  • 308
  • 2
  • 12
  • `ptr = (int *)malloc(sizeof(int));`? Why did you add the cast? You don't need it, and it introduces some bug possibilities. See [**Do I cast the result of malloc?**](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) – Andrew Henle Jul 17 '21 at 16:37
  • 1
    @AndrewHenle i didn't add cast - this was original code from the question. Thanks for the link, I'll read it. – Igor_M Jul 17 '21 at 16:45