Why is this example on pointers "bad!"? [duplicate] - c++

This question already has answers here:
Deleting a heap then dereferencing a pointer to that memory
(4 answers)
Closed 6 years ago.
I am reading Learning a New Programming Language: C++ for Java Programmers here, and there is an example on pointers that reads:
Never dereference a dangling pointer (a pointer to a location that was pointed to by another pointer that has been deleted):
int *p, *q;
p = new int;
q = p; // p and q point to the same location
delete q; // now p is a dangling pointer
*p = 3; // bad!
However, if I copy this code into a main function and add the following cout:
cout << p << " " << *p << endl;
I get the output:
0000022DC3DD0EF0 3
Which seems valid to me, I get the pointer and then the deref'd value.
Is this a typo in the webpage, or is the above code bad practice?

This is undefined behavior. You cannot access memory through a deleted pointer. That is a coincidence.

When you delete object it's memory is marked as free and can be used by other objects or even returned to OS. But nobody wastes CPU for erasing the object from memory. So p is still there but it is not yours anymore. You can't be sure what is stored in that place or even that you still have right to read the memory.
Your example is very simple so it's behaviour is predictable. When there is much work done between removal of the object and accessing it's memory, things go weird. And what's worse, such bugs are floating, sometimes code works correctly and sometimes not. So they are hard to debug.

Which seems valid to me, I get the pointer and then the deref'd value.
Among the behaviors allowed by the term "undefined behavior" is to produce results that sucker you into thinking everything is fine. (and then stop working in a more complex program giving you lots of grief because you believe that this can't possibly be the problem)

Related

state of pointer if memory is deallocated

Say I have a pointer char* ptr allocated memory and another pointer char* arr = ptr
What happens to arr after ptr memory is deallocated.
Let the function be:
char* foo()
{
char* ptr = new char [100];
char* arr = ptr;
delete [] ptr;
return arr;
}
Can I use this returned value?
Will it cause any compile-time/Run-time error?
Or any thing else.
Or what would happen if the function was
char* foo()
{
char* ptr = new char [100];
char* arr = ptr;
delete [] arr;
return ptr;
}
I guess there would be no change from previous output but would there be any change??
What would happen If I have a class
class Pointer
{
public:
char* ptr;
Pointer()
{
ptr= new char [100];
}
~Pointer()
{
delete [] ptr;
}
};
and function
Pointer foo()
{
Pointer ptr;
ptr.ptr[0]='l';
return ptr;
}
Wont destructor be called at the end of the function and create a dangling pointer Pointer::ptr ??
Can I use this returned value??
You can "use" it, but you can't dereference it. You could, for example, print the pointer value out (but not the pointed-to data!):
std::cout << (intptr_t)foo() << std::endl; // OK
std::cout << foo(); // WRONG: this dereferences the pointer!
So while the value can be "used", it's not really useful as a pointer to char anymore.
Or what would happen if the function was [...]
Both functions have the same meaning. On any decent compiler, you should expect both to yield identical machine code when compiled.
What happens to arr after ptr memory is deallocated?
Nothing happens to it: its value remains unchanged. But because the pointed-to object has been deallocated, it is now a dangling pointer. So you can't use it for anything: if you do, you'll get undefined behavior. Undefined behavior means that anything can happen. This includes: nothing happening (things "appear" to "work OK"), or getting your hard drive formatted.
The situation is the same is if you built a house on a lot. You give your friend Arr the GPS coordinates. But in the meantime you decided to move out. Yet your friend Arr still has the old coordinates. Now Arr decides to use them. There are several possible outcomes - and you have no control over which one happens. I'll list just a few:
You moved out an hour ago. Everything is still the same. Arr stopped by, took a picture of your old home, and left.
This corresponds to a case where due to a coincidence, the pointed-to memory still contains usable contents. You still have a bug, but coincidence hides it.
You moved out, but the next day the city decided to raze the lot and build a big condo building on it and adjacent lots. Your friend comes in expecting a small house, sees a big condo high-rise and ends up completely stumped.
This corresponds to a case where the memory gets reused followed by the dangling pointer dereference. Whether this leads to CPU raising an exception or not depends on what kind of an object lived there before.
You moved out, but there was an earthquake and there's now a lake there. Your friend falls in and drowns.
This corresponds to a case where a now-redundant chunk of virtual memory that used to be a part of the free store has been unmapped. You get a page fault.
The memory manager runtime can deallocate the page that used to back the address space pointed to by your pointer. Recall that often a C++ program runs on top of a virtual memory machine. The address space seen by the program is the virtual address space. When there's no physical memory page backing a given virtual address space page, and no file or other backing for that page, any accesses to it will cause a page fault that propagates to userland and terminates the process if unhandled (as it is by default).
From your code the arr would point to not allocated memory so if you tried to work with it it would contain absolutely random data.
I once worked on an embedded system, and, as luck sometimes happens, I had the debugger connected near the correct place at the correct time (with respect to the delete) when the system crashed. The code was C++, the debugger was gdb, vxWorks is an embedded system tool suite.
The code I inspected was in some ways similar to your question, essentially a dereference of the pointer occurred after the delete.
...
char* ptr = new char [100];
delete [] ptr;
// more stuff that did not affect what ptr pointed to
// about 5 to 9 lines later
char retVal = ptr[x]; // <<< invalid access crash
...
The crash indicated that ptr[x] is invalid.
That embedded system (vxWorks) had techniques to confirm that where ptr pointed was indeed no longer in context, unmapped from that task.
I would guess it unusual (because of performance), but the delete not only released the block from dynamic memory, it also released the block of memory from the thread memory space, making the address invalid, causing a bus error.
I do not know how to confirm the similar information in Linux.
What happens to arr after ptr memory is deallocated?
The auto variables are unaffected by the delete.
Can I use this returned value?
Behavior is undefined (i.e. UB), so you should want to avoid doing so.
Will it cause any compile-time/Run-time error? Or any thing else.
UB means any thing else can happen.

Is pointer to variable the same as a pointer to an array of one element, or of zero elements?

Array with size 0 Has good explanations of zero-length arrays and is certainly worthwhile and pertinent. I am not seeing it compare zero-length with single-element arrays and with pointer-to-variable.
When I asked before (Is c++ delete equivalent to delete[1]?) I did not express myself well. My question seemed to be the same or included in more general answers about new, new[], delete, and delete[]. Some understood that I was asking only about a single element. All answers in comments seemed correct and consistent.
There is a question that looks like the same as this question. But the body is about using C++ and Java together. Here, we are talking only about C++.
Checking my understanding
I will present pairs of proposed equivalent statements. The statements are declarations or instantiations of a pointer to a single variable followed by a pointer to an array of one element. Then I will state why I would think they are equivalent or not.
Are these pairs equivalent?
int one = 1;
// Sample 1. Same in the sense of pointing to an address somewhere
// whose contents equal one. Also same in the sense of not being able to
// change to point to a different address:
int * const p_int = &one;
int a_int[1] = {1};
// Sample 2.
int * p_int1 = new int;
int * a_int1 = new int[1];
// Sample 3.
delete p_int1;
delete[] a_int1;
// Sample 4. If Sample 3 is an equivalent pair, then (given Sample 2)
// we can write
delete[] p_int1;
delete a_int1;
Granted, Sample 4 is bad practice.
I am thinking: "delete" will call the destructor of the object. delete[] will call the destructor for each element of the array, and then call the destructor for the array. new in Sample 2 would malloc (so to speak) the variable. new[] would malloc an array of one element, then malloc the one element. And then that one element would be set equal to 1. So, I'm thinking THAT'S why I need to call delete[] and not delete when I have an array of even one element. Am I understanding?
And if I am understanding, then calling delete instead of delete[] to free an array of one element, then I will certainly have a memory leak. A memory leak is the specific "bad thing" that will happen.
However, what about this:
int * a_int0 = new int[0];
delete a_int0;
Would THAT result in a memory leak?
I invite corrections of my misuse of terminology and anything else.
Sample 1:
int const * p_int = &one;
int a_int[1] = {1};
NO, these are not equivalent. A pointer is not the same thing as an array. They are not equivalent for the same reason that 1 is not the same as std::vector<int>{1}: a range of one element is not the same thing as one element.
Sample 2:
int * p_int1 = new int;
int * a_int1 = new int[1];
These are sort of equivalent. You have to delete them differently, but otherwise the way you would use p_int1 and a_int1 is the same. You could treat either as a range (ending at p_int1+1 and a_int1+1, respectively).
Sample 3:
delete p_int1;
delete[] a_int1;
These are I suppose equivalent in the sense that both correctly deallocate the respective memory of the two variables.
Sample 4:
delete[] p_int1;
delete a_int1;
These are I suppose equivalent in the sense that both incorrectly deallocate the respective memory of the two variables.
int const * p_int = &one;
int a_int[1] = {1};
They are not equivalent, the first is a pointer to another variable, the other a mere array of size one, initialized straight away with the value one.
Understand this: pointers are entities in themselves, distinct from what they point to. Which is to say, the memory address of your p_int there, is entirely different from the memory address of your variable one. What's now stored in your p_int however, is the memory address of your variable one.
// Sample 2.
int * p_int1 = new int;
int * a_int1 = new int[1];
Here though, they are effectively the same thing in terms of memory allocation. In both cases you create a single int on the heap(new means heap space), and both pointers are immediately assigned the address of those heap allocated ints. The latter shouldn't be used in practice though, even though it's technically not doing anything outright wrong, it's confusing to read for humans as it conveys the notion that there is an array of objects, when there is in reality just one object, ie no arrayment has taken place.
// Sample 3.
delete p_int1;
delete[] a_int1;
Personally, I've never used the "array" delete, but yeah, what you're saying is the right idea: delete[] essentially means "call delete on every element in the array", while regular delete means "delete that one object the pointer points to".
// Sample 4. If Sample 3 is an equivalent pair, then (given Sample 2)
// we can write
delete[] p_int1;
delete a_int1;
Delete on array is undefined according to the standard, which means we don't really know what'll happen. Some compilers may be smart enough to see what you mean is a regular delete and vice versa, or they may not, in any case this is risky behavior, and as you say bad practice.
Edit: I missed your last point. In short, I don't know.
new int[0];
Basically means "allocate space for 0 objects of type int". That of course means 0 * 4, ie 0 bytes. As for memory leaks, what seems intuitive is no, as no memory has been allocated, so if the pointer goes out of scope, there is nothing on the heap anyway. I can't give a more in dept answer than that. My guess is that this is an example of undefined behavior, that doesn't have any consequences at all.
However, memory leaks happen when you do this:
void foo()
{
int* ptr = new int;
}
Notice how no delete is called as the function returns. The pointer ptr itself gets deallocated automatically, since it's located on the stack(IE automatic memory), while the int itself is not. Since heap memory isn't automatically deallocated, that tiny int will no longer be addressable, since you got no pointer to it anymore in your stack. Basically, those 4 bytes of heap memory will be marked as "in use" by the operating system, for as long as the process runs, so it won't be allocated a second time.
Edit2: I need to improve my reading comprehension, didn't notice you did delete the variable. Doesn't change much though: You never have memory leaks when you remember to delete, memory leaks arise when you forget to call delete on heap allocated objects(ie new) prior to return from the scope the pointer to heap memory was located. My example is the simplest one I could think of.
This is a syntax-only answer. I checked this in a debugger with the following code:
// Equivalence Test 1.
*p_int = 2;
p_int[0] = 3;
*a_int = 2;
a_int[0] = 3;
Because I can access and manipulate the declarations as an array or as a pointer to variable, I think the declarations are syntactically equivalent, at least approximately.
I have to apologize
(1) I did not think to define my terms clearly. And it is very hard to talk about anything without my defining my terms. I should have realized and stated that I was thinking syntax and what a typical compiler would probably do. (2) I should have thought of checking in a debugger much earlier.
I think the previous answers are correct semantically. And of course good programming practice would declare an array when an array is the meaning, etc. for a pointer to variable.
I appreciate the attention that has been given to my question. And I hope you can be accepting of my slowness to figure out what I am trying to say and ask.
I figure that the other declarations can be checked out similarly in a debugger to see whether they are syntactically equivalent.
A Compiler's generating the same assembly would show syntactic equivalence. But if the assembly generated differs, then the assembly needs to be studied to see whether each does the same thing or not.

dangling pointers with string and vector [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 9 years ago.
I know that a dangling handle is a pointer or reference to invalid data. And I experiment with string and vector, below is my code with string:
char *p = NULL;
{
string s;
s.push_back('a');
s.push_back('b');
p = &s[0];
}
cout << *p << endl;
and the result is 'a'. It surprised me, shouldn't p be a dangling pointer? And I think that the object s should have been destructed, why can p still point to the valid content?
And I do another experiment with vector by just replacing the "string" with "vector" in the code above, and this time print nothing. So what does this mean? Is there any difference that string and vector organize their members?
You are invoking undefined behaviour, so anything can happen. In your case it just happens that the memory hasn’t been overwritten yet. It could also just segfault, wipe your harddrive or awaken the nasal demons.
Traffic laws say that if you wait for a green light, you are guaranteed to be able to cross the road safely.
You are complaining that not everyone who crosses at a red light is instantly crushed by a truck.

Is it necessary to use IF statement when releasing memory? [duplicate]

This question already has answers here:
Is it safe to delete a NULL pointer?
(8 answers)
Closed 9 years ago.
I am trying to understand memory part in C++. I am trying to release memory after I generate the output by using the code below.
Question:
Is it necessary to release memory by using if-statement?
Code:
int main(){
char *pc;
int *pi;
pc = new char('a');
pi = new int(8);
cout << *pc << endl;
cout << *pi << endl;
//What's the purpose for doing if(pc) and if (pi) below?
if(pc){
delete pc;
}
if(pi){
delete pi;
}
return 0;
}
Could I be able to do in this way?
int main(){
char *pc;
int *pi;
pc = new char('a');
pi = new int(8);
cout << *pc << endl;
cout << *pi << endl;
delete pc;
delete pi;
return 0;
}
Is it necessary to use IF statement when releasing memory?
No, it is not (as long as you haven't overridden the global operator delete). This is perfectly fine and will do nothing:
int* p = nullptr;
delete p;
Per paragraph 3.7.4/2 of the C++11 Standard:
[...] The value of the
first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation
function is one supplied in the standard library, the call has no effect. [...]
As suggested by chris in the comments, however, consider using smart pointers rather than performing manual memory management through raw pointers, new, and delete (or their array counterpart).
In your particular code, the null checks are not needed.
In general, it's implementation specific whether delete (T*)0; will call the deallocation function for type T. If your implementation does pass null pointers to the deallocation function, and either your type has overridden the deallocation function by providing member operator delete, or you're provided a replacement global ::operator delete, and that custom deallocation function doesn't handle null pointer values well, you could have trouble.
The Standard does NOT require that a custom operator delete do nothing when passed a null pointer. It shouldn't fail, but it might write nasty log messages or tell your boss that someone isn't following the coding standard, for example.
No it's not but it's considered a good practice to do so. In real life scenario your code changes frequently due to business requirements, bugs, etc so it's always best to use this kind of defensive programming strategy.
The last thing you want is having a hard-to-detect error due to freeing already freed pointer because your colleague changed the upper portion of the code

Pointer to deallocated location Is it a Undefined Behavior?

Pointer to deallocated location Is it a Undefined Behavior?
http://ideone.com/Qp3uY
int *p = new int;
*p = 10;
delete p;
*p = 10;
cout << *p << endl;
There mere existence of a pointer to a deallocated location is not undefined behavior in itself. Attempting to dereference that pointer does produce undefined behavior though.
Dereferencing a deleted pointer is an undefined operation. Don't do it.
This is undefined behavior:
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value, the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined. - C++ '03 3.7.3.2
When you allocate memory to make a new pointer, as you do in the first line
int *p = new int;
You're asking the operating system to produce some memory for you to use, for as long as you like. You can then put something in that spot, as you then do
*p = 10;
This memory is available for you to use as long as you want, and then you can tell the operating system you're done with it, by calling delete, as you do on the next line.
delete p;
The operating system now has the memory available to it, but it may or may not do something with that memory. If you allocate a bunch of other memory, it is possible that the new memory range includes this memory. The operating system may give away this memory to something else, or it may not - it's not going to tell you, that's why it is said to be undefined behavior to still use that place in memory.
*p = 10;
You then reuse this place of memory to set it to 10 again. Nothing else has happened in the meantime and this is a rather trivial program, so the operating system hasn't done anything else with that block of memory yet, so setting it in this case does not have any greater effect.
cout << *p << endl;
Again, the operating system owns the memory right now, but it isn't likely doing anything with it at this point; it's like staying in a hotel room after your stay is officially over. You may or may not be able to stay there, as you don't know whether the room is being used by another person afterward or if it is remaining empty. You could be thrown out, or you could be safe.