I was working with pointers and was trying different things.
So here I made a general print function
void print(const int &value)
{
cout << "value = " << value << " & address = " << &value << endl;
}
Now Here's a code that works somehow:
int j = 9090;
int* p;
*p = j; //Please read further
print(*p);
I know *p = j is something illogical and I should have done p = &j or p = new int etc.... but why did this work?
The output was
value = 9090 & address = 0x28ff48
Moreover if I print j, the address is 0x28ff44. Which means it allocated new memory automatically for p (as its address is ..48)
Now, if I only add another line before j's declaration:
int i = 80;
int j = 9090;
int* p;
*p = j; //Please read further
print(*p);
^The program crashes.
However if I add this i's line after declaration of p the program runs.
int j = 9090;
int* p;
int i = 80;
*p = j;
print(*p);
Am I doing something wrong? I was using GCC compiler in Dev C++ IDE 4.9.9.2. What's going on?
The problem is that dereferencing an uninitialised pointer is undefined.
This means that anything can happen - sometimes a program will crash, sometimes just give strange results, and sometimes (if you're unlucky) it will just seem to work.
Since your pointer is uninitialised, you will access memory at whatever random location it happens to represent.
Sometimes, this is a valid address, and the program will keep running.
Sometimes, it's an invalid address, and the program will crash.
int* p;
*p = j;
This is correct syntax of C++. It means that in field which address is in p, you put value of j. The problem with crash originate from the fact that you don't provide any memory for p. When you say int * p it means that you have pointer on int variable. However you didn't provide any real address in memory to store int value. There is some rubbish in p but this rubbish is considered to be valid address by program and it tries to write value of j there. If you are lucky and this address was not used the program will work further. Otherwise it will crash. To avoid such undefined behaviour you need to allocate memory for p:
int* p = new int;
*p = j;
I know *p = j is something illogical
In this case it is undefined behavior. You are dereferencing a pointer that wasn't initialized, so it effectively points to an unknown (random) location.
Which means it allocated new memory automatically for p
No, it didn't. The address you see is just the random, undefined value which ended up in your uninitialized pointer.
why did this work?
That's the thing with undefined behavior: It might work by coincidence, for example if the random value in the pointer happened to point into some valid, writable memory region, your code would work fine for that specific run. Another time, it might just crash. Yet another time, it might corrupt your heap, or some unrelated data, and cause a seemingly unrelated crash or bug some time later. Which is just screaming for long nights in front of your debugger ;)
Bottom line: Don't do it - avoid UB at all cost.
However if I add this i's line after declaration of p the program runs.
I think the behavior you observe (crash vs. no crash) is not directly related to the code changes, but coincidental. In fact, if you'd run the "working" version multiple times, I'm sure you'd still get a crash after some time.
Related
This is a small snippet of code I was writing to test my knowledge of pointer :
int *grades;
*(grades+0) = 100;
*(grades+1) = 87;
*(grades+2) = 99;
*(grades+3) = 92;
cout<<*(grades+0)<<endl;
cout<<*(grades+1)<<endl;
cout<<*(grades+2)<<endl;
cout<<*(grades+3)<<endl;
I know the flaw in assigning pointers like this. However the above snippet of code correctly printed out the value of each of the grades. The issue is if I use a for loop to automate the display of the content of the pointers.
int *grades;
*(grades+0) = 100;
*(grades+1) = 87;
*(grades+2) = 99;
*(grades+3) = 92;
for(int i=0;i<4;++i)cout<<*(grades+i)<<endl;
Why does addition of a for loop causing segmentation fault. I know the reason of the segmentation fault, but isn't both snippets of code basically the same? I was using https://www.onlinegdb.com/ for writing the code.
Both scenarios (with or without the for loop) suffer from the same undefined behavior, and the nature of UB is that anything can happen. That is why your two snippets that are "basically the same", differ in behavior.
You code exhibits undefined behavior. grades points to invalid memory as it's uninitialized. Attempting to read or write through the pointer results in UB. It might look like it works but that's just one possible side effect of UB.
If you need a dynamic array use std::vector<int>, otherwise stick to std::array<int, 5>.
To make your current snippet work:
std::array<int, 4> a;
int* grades = a.data();
*(grades+0) = 100;
*(grades+1) = 87;
*(grades+2) = 99;
*(grades+3) = 92;
Your dynamic array is unitialized. So now it is pointed to a random address.
Try to do this:
int* grades = new int[4];
// do things
delete [] grades;
So, OS will assign some space for your 4 integers and not a random place where maybe there's another important value in it.
#include<iostream>
using namespace std;
int main( )
{
int *p;
double *q;
cout << p << " " << q << endl;
p++;
q++;
cout << p << " " << q << endl;
//*p = 5; // should be wrong!
}
This function prints
0x7ffe6c0591a0 0
0x7ffe6c0591a4 0x8
Why does p point to some randm address and q to zero? Also, when I uncomment the line *p=5, shouln't it throw an error? It still works fine:
code with line uncommented output
0x7ffc909a2f70 0
0x7ffc909a2f74 0x8
What can explain this weird behaviour?
When local (auto) variables of basic type (int, double, pointers to them, etc) are uninitialised, any operation that accesses their value yields undefined behaviour.
Printing a variable accesses its value, so both the statements with cout << ... give undefined behaviour. Incrementing a variable also accesses its value (it is not possible to give the result of incrementing without accessing the previous value) so both the increment operators present undefined behaviour. Derefererencing an unitialised pointer (as in *p) gives undefined behaviour, as does assigning a value to the result *p = 5.
So every statement you have shown after the definitions of p and q gives undefined behaviour.
Undefined behaviour means there are no constraints on what is permitted to happen - or, more simply, that anything can happen. That allows any result from "appear to do nothing" to "crash" to "reformat your hard drive".
The particular output your are getting therefore doesn't really matter. You may get completely different behavior when the code is built with a different compiler, or even during a different phase of the moon.
In terms of a partial explanation of what you are seeing .... The variables p and q will probably receive values corresponding to whatever happens to be in memory at the location where they are created - and therefore to whatever some code (within an operating system driver, within your program, even within some other program) happened to write at that location previously. But that is only one of many possible explanations.
As you have not initialised the variables - the code resorts to undefined behaviour. So anything can happen including what you have experienced.
C++ gives you more than enough rope to hang yourself. Compile with all the warnings switched on to avoid some of the perils.
When you do not initiate a variable, it set to a random or specific value based on the compiler policy.
About ++ if you create a pointer to class A, and use ++ the pointer will be incremented by sizeof(A).
And about your last question, *p=5 is a good instance of undefined behavior when you did not allocate a memory for p;
My question:
int* x = new int;
cout << x<<"\n";
int* p;
cout << p <<"\n";
p = x;
delete p;
cout << p <<"\n";
I wrote this purely by myself to understand the pointer and to understand (also get lost in) the dynamic new and delete.
My XCode can compile the program and return the following results:
0x100104250
0x0
0x100104250
I know I can only call delete on dynamically allocated memory.
However, I called delete on p in the above program and it compiles.
Could anyone explain this to me?
Why could I delete p?
Moreover, I found if the program changes to the following:
int* x = new int;
int* p;
cout << p <<"\n";
delete p;
cout << p <<"\n";
Then my Xcode again compiles and returns me:
0x0
0x0
Program ended with exit code: 0
and now, I got completely lost:(.
Could anyone please explain me this ?
Why I could delete p since it has nothing to with x?
Since Xcode compiles successfully, I assume the above two programs are correct for computer.
However, I think it is again the statement of "only call delete on dynamic allocated memory".
Or probably, I didn't fully understand what is pointer and what is dynamic allocated memory.
I found this post when I searched online.
But I don't think it is like my case.
Please help me out.
I would like to ask one more question. The code is here about binary search tree.
From line 28 to 32, it deals with deleting a node with one child.
I put this part of code here, in case the weblink does not work.
else if(root->left == NULL) {
struct Node *temp = root;
root = root->right;
delete temp;
}
It is these codes leading me ask the above the question regarding pointer.
Following the answer given by this post.
Is it correct to understand the code in the following way?
I cannot firstly link the parent node of root to right child of root.
and then delete the root node, as the subtree beneath the root node will also be deleted.
So I must create a temp pointer, pointing to the memory slot, which is pointed to by root.
Then I link the parent node of root to right child of root.
and now, I can safely delete the memory slot pointed by "root", (i.e. temp as they both point to the same memory).
In this way, I release the memory and also keep the link between parent and children.
In addition, the temp is still there and still points to "that" memory slot.
Should I set it to NULL after deletion?
Thank you all again in advance.
Yaofeng
Yes, you can only call delete on memory which was allocated via new. Notice that it's the address of the memory (the value of the pointer) which matters, and not the variable storing the pointer. So, your first code:
int* x = new int; //(A)
cout << x<<"\n";
int* p;
cout << p <<"\n";
p = x; //(B)
delete p; //(C)
cout << p <<"\n"; //(D)
Line (A) allocates memory dynamically at some address (0x100104250 in your example output) and stores this address in variable x. The memory is allocated via new, which means that delete must eventually be called on address 0x100104250.
Line (B) assigns the address 0x100104250 (value of pointer x) into the pointer p. Line (C) then calls delete p, which means "deallocate the memory pointed to by p." This means it calls delete on address 0x100104250, and all is well. The memory at address 0x100104250 was allocated via new, and so is not correctly allocated via delete. The fact that you used a different variable for storing the value plays no role.
Line (D) just prints out the value of the pointer. delete acts on the memory to which a pointer points, not on the pointer itself. The value of the pointer stays the same - it still points to the same memory, that memory is just no longer allocated.
The second example is different - you're calling delete p when p was not initialised to anything. It points to a random piece of memory, so calling delete on it is of course illegal (technically, it has "Undefined Behaviour", and will most likely crash).
It seems that in your particular example, you're running a debug build and your compiler is "helpfully" initialising local variables to 0 if you don't initialise them yourself. So it actually causes your second example to not crash, since calling delete on a null pointer is valid (and does nothing). But the program actually has a bug, since local variables are normally not initialised implicitly.
p = x;
This would make p contain same value as x ( p would point to same object as x). So basically when you say
delete p;
Its doing delete on the address referred to by p which is same as that of x. Hence this is perfectly valid as object referred to by that address is allocated using new.
Second case :-
Co-incidently your pointer p is set by compiler as a NULL pointer( You should not depend on this). So deleting that pointer is safe. Had that not been a NULL pointer you would have possibly seen a crash.
Ok, let's take a look at the documentation for the "delete" operator. According to http://en.cppreference.com/w/cpp/memory/new/operator_delete:
Called by delete-expressions to deallocate storage previously allocated for a single object. The behavior of the standard library implementation of this function is undefined unless ptr is a null pointer or is a pointer previously obtained from the standard library implementation of operator new(size_t) or operator new(size_t, std::nothrow_t).
So what happens is the following: you are calling the new operator, which allocates sizeof(int) bytes for an int variable in memory. That part in memory is referenced by your pointer x. Then you create another pointer, p, which points to the same memory address. When you call delete, the memory is released. Both p and x still point to the same memory address, except that the value at that location is now garbage. To make this easier to understand, I modified your code as follows:
#include <iostream>
using namespace std;
int main() {
int * x = new int; // Allocate sizeof(int) bytes, which x references to
*x = 8; // Store an 8 at the newly created storage
cout << x << " " << *x << "\n"; // Shows the memory address x points to and "8". (e.g. 0x21f6010 8)
int * p; // Create a new pointer
p = x; // p points to the same memory location as x
cout << p << " " << *p << "\n"; // Shows the memory address p points to (the same as x) and "8".
delete p; // Release the allocated memory
cout << x << " " << p << " "
<< *x << " " << *p << "\n"; // p now points to the same memory address as before, except that it now contains garbage (e.g. 0x21f6010 0)
return 0;
}
After running, I got the following results:
0x215c010 8
0x215c010 8
0x215c010 0x215c010 0 0
So remember, by using delete you release the memory, but the pointer still points to the same address. That's why it's usually a safe practice to also set the pointer to NULL afterwards. Hope this makes a bit more sense now :-)
Before the answers, you need to understand the following points.
once you delete a pointer, it need not be assigned with 0. It
can be anything.
You can delete a 0 or NULL without any harm.
Undefined behavior is one in which anything could happen. The
program might crash, it might work properly as if nothing happened,
it might produce some random results, etc.,
However, I called delete on p in the above program and it compiles.
Could anyone explain this to me? Why could I delete p?
That is because you assign the address of memory allocated by new via x. ( p = x;) x ( or p) is a valid memory location and can be deleted.
Now x is called a Dangling pointer. Because it is pointing to a memory that no longer valid. Accessing x after deleting is undefined behavior.
Could anyone please explain me this ? Why I could delete p since it has nothing to with x?
This is because your p is assigned with 0. Hence you are getting away with undefined behavior. However it is not guaranteed that your uninitialized pointer will have a value of 0 or NULL. At this point it seems to work fine, but you are wading through undefined behavior here.
delete/free keyword is used to empty the stored value from the memory location. if we dont use pointers we have the reassign the value to NULL or just let them go out of scope.
Be careful using pointers, if we go out of scope using pointers without deleting the value. It will create memory leak. because that portion of the memory block is not usable anymore. And we lost the address because we are in different scope.
By referring to http://www.cplusplus.com/doc/tutorial/dynamic/, it is said that the new operator returns a pointer to the beginning of the new block of memory allocated. Let's assume again that any pointer has the feature of pointer arithmetic, which can make it true that p + 1 points to the next position in memory if p is a pointer.
Based on the above assumptions, I have tried the following block,
#include <iostream>
using namespace std;
int main(){
int * ptr = (new int [4]) + 1;
ptr[3] = 2;
cout << ptr[3] << endl;
/*Before delete the dynamic allocated memory,
it should be necessary to let ptr point to the
beginning of it, add one line to do that:
--ptr;*/
delete [] ptr;
}
I expected that ptr[3] should goes beyond the range of newly allocated memory. However, the result still give 2. Why the pointer arithmetic become useless? Or is it really new Type returns an object of *Type?
Thanks for any help.
I expected that ptr[3] should goes beyond the range of newly allocated memory.
It does indeed. You need to be careful not to do that, since (as you've noticed) that's an error that often can't be detected. Dynamic analysis tools like valgrind can help diagnose these problems, but not perfectly.
However, the result still give 2.
That means that there happened to be accessible memory beyond the end of the array, so the invalid access couldn't be detected. Instead, it overwrote memory that didn't belong to the array - perhaps it was unused, or perhaps you've overwritten some other data, which will cause baffling errors later.
Why the pointer arithmetic become useless?
It's as useful as any pointer arithmetic. Pointer arithmetic can't detect the end of an array, so you get undefined behaviour if it takes you out of range.
Or is it really new Type returns an object of *Type?
new Type[] returns a Type* pointer to the allocated array. That behaves just like any other pointer, so adding one gives you a pointer to the second element.
delete [] ptr also gives undefined behaviour, since ptr no longer points to the start of an allocated array.
Going beyond boundary is undefined behavior.
You may get different results when compiled by differet compiler, different compilation flags, even running at different time. You computer may blow off and your monitor may burn.
Return a correct value is one of undefined behaviors too.
C/C++ allows you to assign/read values to/from out of bounds memory, as this is undefined behavior.
For instance, this is valid code:
int *array = new int[10];
array[10] = 5;
std::cout<<array[10];
However, doing this you could damage other data structures, if they happen to have memory allocated adjacent to your array.
int * ptr = new int [4];
will point to ptr[0], so
int * ptr = (new int [4]) + 1;
will pont to ptr[1] and you will not be able to free ptr unless you do something like delete (ptr--);
I have the following program:
//simple array memory test.
#include <iostream>
using namespace std;
void someFunc(float*, int, int);
int main() {
int convert = 2;
float *arr = new float[17];
for(int i = 0; i < 17; i++) {
arr[i] = 1.0;
}
someFunc(arr, 17, convert);
for(int i = 0; i < 17; i++) {
cout << arr[i] << endl;
}
return 0;
}
void someFunc(float *arr, int num, int flag) {
if(flag) {
delete []arr;
}
}
When I put the following into gdb and insert a break point at float *arr ..., I step through the program and observe the following:
Printing the array arr after it has been initialized gives me 1 17 times.
Inside someFunc too, I print arr before delete to get the same print as above.
Upon going back into main, when I print arr, I get the first digit as 0 followed by 16 1.0s.
My questions:
1. Once the array has been deleted in someFunc, how am I still able to access arr without a segfault in someFunc or main?
2. The code snippet above is a test version of another piece of code that runs in a bigger program. I observe the same behaviour in both places (first digit is 0 but all others are the same. If this is some unexplained memory error, how am I observing the same thing in different areas?
3. Some explanations to fill the gaps in my understanding are most welcome.
A segfault occurs when you access a memory address that isn't mapped into the process. Calling delete [] releases memory back to the memory allocator, but usually not to the OS.
The contents of the memory after calling delete [] are an implementation detail that varies across compilers, libraries, OSes and especially debug-vs-release builds. Debug memory allocators, for instance, will often fill the memory with some tell-tale signature like 0xdeadbeef.
Dereferencing a pointer after it has been deleteed is undefined behavior, which means that anything can happen.
Once the array has been deleted, any access to it is undefined behavior.
There's no guarantee that you'll get a segment violation; in fact,
typically you won't. But there's no guarantee of what you will get; in
larger programs, modifying the contents of the array could easily result
in memory corruption elsewhere.
delete gives the memory back to the OS memory manager, but does not necessarily clears the contents in the memory(it should not, as it causes overhead for nothing). So the values are retained in the memory. And in your case, you are accessing the same memory -- so it will print what is in the memory -- it is not necessarily an undefined behaviour(depends on memory manager)
Re 1: You can't. If you want to access arr later, don't delete it.
C++ doesn't check for array boundaries. Only if you access a memory which you are not allowed to you will get segfault