I have the following C++ code, and I'm running g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0:
#include <iostream>
const int& getConst() {
int x = 10;
printf("x ptr: %p\n", &x);
const int &y = 10;
printf("y ptr: %p\n", &y);
return y;
}
int main() {
int start = 0;
printf("start ptr: %p\n", &start);
const int &t = getConst();
printf("t: %d\n", t);
printf("t ptr: %p\n", &t);
int end = 0;
printf("end ptr: %p\n", &end);
return 0;
}
And the output of this code is as follows:
root#78600f6683dd:/home/test/question# ./a.out
start ptr: 0x7ffdcd2381f8
x ptr: 0x7ffdcd2381c8
y ptr: 0x7ffdcd2381cc
t: 10
t ptr: 0x7ffdcd2381cc
end ptr: 0x7ffdcd2381fc
There are two things I'm confused about the result:
The memory location of start and end within the function main() are 0x7ffdcd2381f8 and 0x7ffdcd2381fc respectively. The memory locations of variables of main function are ascending. The main function calls getConst() function, but the location of variables within the function getConst() are 0x7ffdcd2381c8 and 0x7ffdcd2381cc, which are both descending comparing to variables within the main() function. Since main functions calls getConst() function, shouldn't location of getConst() be on top of the stack to main()?
Within getConst() function, y is a const reference to 10. As far as I understand it, the procedure is that, a temporary int variable is created with a value of 10, and y references to it. As seen in the output of the program, both y and t point to the same memory location. However the temporary variable is a variable defined in stack, shouldn't it be cleaned up after getConst() function returns? If so, how can t still get the correct value?
Your code has undefined behaviour as it is returning a reference to a temporary variable so anything could happen.
However what is actually happening is probably that the returned reference is basically a pointer, the memory pointed to is no longer valid but the pointer itself is just a number so it is unsurprising that you are able to print its value. Printing the value of the reference will probably work as the runtime doesn't "clean up" deallocated stack memory, that would be a waste of time, when the memory is re-used it will be re-initialised. If you call a new function containing uninitialised variables it wouldn't be surprising if they also have the same values as set in getConst. This of course is all undefined behaviour.
Traditionally heap memory grew up from the bottom of the memory and stack grew down from the top, when the two met your program was out of memory. With modern virtual memory schemes this isn't literally the case any more but the stack is still generally a fixed size block of memory which is used from the end back to the front so it is not unusual for new stack allocations to have lower addresses than old ones. This is what makes overflowing stack variables so dangerous, you aren't overwriting unused memory you are actually overwriting variables earlier in the stack.
Related
So, I have the following code where I have a class x on the heap and I return its value. Let's say that if res would be declared on the stack, it would cause a stack overflow. Then, in the code below, since I am returning the value stored on the heap, will it cause a stack overflow? Do I have to return a pointer to x?
x func ()
{
x* res = new x;
// code
return *res;
}
int main ()
{
x* s = new x;
*s = func();
}
Also, I know I didn't delete the string.
EDIT: Changed from std::string to class x.
Can a value returned by a function cause a stack overflow?
Return value can be stored on the stack. Anything that is stored on the stack can cause a stack overflow if it is larger than fits on the stack.
In an example call convention, the caller allocates the stack space for the return value, and parameters are stored after it, in which case if the return value overflows the stack, then that happens before the function is even called.
... will it cause a stack overflow?
Potentially, yes. Here is a demo: http://coliru.stacked-crooked.com/a/daca31b6551d1be5
Stack overflow is not guaranteed if there is optimisation involved which avoid creation of the temporary object. Same example with optimisation enabled: http://coliru.stacked-crooked.com/a/11fd38960fd62155
Do I have to return a pointer to x?
If sizeof of the type is very large, then you should avoid returning it by value just as much as you should avoid declaring variables with automatic storage, or creating temporary objects, or passing as value arguments.
In this example a std::unique_ptr would probably be a good choice.
To elaborate a bit on my comment. Normally this should not cause stack overflow if sizeof(X), where X is your type, is not extremely large - which it should not be in the well designed application.
However, you could play a bit with your example and check what will happen in case of large data structure:
// very bad 1MB structure
struct X
{
char data[1024*1024];
};
int main() {
std::cout << sizeof(X) << std::endl;
auto x = foo();
// data[0] is undefined, printing to prevent compiler from optimizing it out
std::cout << x.data[0] << std::endl;
return 0;
}
Now run it with valgrind and set the stacksize to less than 1MB:
valgrind --main-stacksize=1000000 --leak-check=full --log-file=vg.valgrind ./xxx
You might get the output similar to this:
Process terminating with default action of signal 11 (SIGSEGV)
Access not within mapped region at address 0x1FFEEFFB60
Stack overflow in thread #1: can't grow stack to 0x1ffeeff000
Note: the overflow is caused by the function call itself, not by storing the returned value, this example would be enough:
int main()
{
foo();
return 0;
}
however, gcc with -O1 or higher will optimize it out if foo does not have any side effects. You can compile with -O0 to see overflow in simplified example.
Simple improvement to struct X to avoid allocating big amount of data on the stack:
struct X
{
X(): data(1024*1024, 'x') {}
std::string data;
};
int* areaofsquare(int len,int width){
int value = len*width;
return &value;
}
int main() {
printf("area=%i\n",*areaofsquare(1,2));
return 0;
}
Why I get memory error from this code? As per my understanding I did not get any error in this code block so why does it not run properly?
Help me out.
Thank you.
You can't just return pointer to stack variable, as it's getting invalidated as the function returns. You can use dynamic memory allocation instead:
int* area(int leangth,int width){
int * area = malloc(sizeof(int));
*area = leangth*width;
return area;
}
int main() {
printf("area=%i\n",*area(5,6));
}
Please note that you create memory leak and it would be better to handle it some way, but in this tiny example it doesn't matter.
You are returning the address of a local function variable in your function area. When the function returns, its stack space is no longer valid, so attempting to access an address to a local variable from that function is undefined behavior.
I don't see why you need to make area return a pointer to an int. Just return area's value and have area's return type just be int instead.
If for some reason you are required to return a pointer to an int (i.e. assignment or deliverable specifications), you have some options:
Use heap memory. Allocate area with malloc, and free it in main after you've printed its value.
Use static to put area into the process' data section rather than in stack memory, so that it persists beyond its scope and an address-of operation on it will continue to work after the function returns.
Here and Here I found that variables in block are created when execution reaches that block,
To prove that I tried this:
int main()
{
{
char a;
printf("Address of a %d \n",&a);
}
char b;
printf("Address of b %d \n",&b);
}
As expected b was created first (because outer block is executed sooner than inner), and when the execution reached inner block a was created. Output of the above code:
Address of a 2686766
Address of b 2686767
(Tested on x86 (stack grows downwards, so variable with greater address was created first)).
But what about this one?:
int main()
{
{
char a;
printf("Address of a %d \n",&a);
} // I expected that variable a should be destroyed here
{
char b;
printf("Address of b %d \n",&b);
}
}
Output:
Address of a 2686767
Address of b 2686766
I expected that a was destroyed at closing brace of the first block statement, so address where a was located is now top of stack, and b should be created here, thus in the Output above both addresses should be equal, but they aren't? Are variables destroyed at the end of block? If not, why?
There are no rules for how the compiler places variables in memory. It could very well reserve space for both of them at the same time, if that is "easier" in some way.
It is allowed to reuse the space for variables in different scopes, but not required. Saving a single byte might not be worth the trouble of trying to "optimize" the allocation.
Destroying a variable in C++ means 'calling it's destructor'. Nothing more, nothing else. Since built-in types like chars have no destructors, destroying them is not visible for the observer.
int main()
{
int *aligned;
// aligned value 0xcccccccc{???}
aligned = (int*)_aligned_malloc(sizeof(int) * 1000, 16);
// aligned value 0x001d9490{-842150451}
_aligned_free(aligned);
//// aligned value 0x001d9490{-17891602} address did not change
*aligned == 100;
int *y;
// y value 0xcccccccc{???}
y = new int();
// y value 0x001d9480{0}
delete(y);
// y value 0x00008123{???} address has changed
*y = 100; // Gives exception
}
My question:
Why code at line 4 does not give exception? Does it mean memory is not properly freed by _aligned_free? If yes, then how should we free memory allocated by _aligned_malloc.
The code at line 4 isn't doing anything - you are using == (test for equality) not = (assignment) on that line. Well, actually, it is doing something - it will attempt to read from an address in a memory block you just freed (and compare that value to 100). This might not necessarily cause an exception - many heap managers will carve chunks out of larger blocks allocated in your process address space, so freeing one might very well just mark the block as available for the heap manager to reuse. Reading/writing to this space after freeing might not cause an exception every time (because it might still be part of the valid heap space for the process), but it is definitely an error.
Also, after calling _aligned_free the pointer value (aligned) shouldn't change - that doesn't mean the memory was not freed.
int *aligned;
// aligned value 0xcccccccc{???}
You did not initialize the pointer, so it's got whatever garbage value was there before main started. Do int *aligned = NULL; if you want it to start off null.
aligned = (int*)_aligned_malloc(sizeof(int) * 1000, 16);
// aligned value 0x001d9490{-842150451}
Looks OK
_aligned_free(aligned);
//// aligned value 0x001d9490{-17891602} address did not change
None of the free calls changes the pointer passed in. Ever.
*aligned == 100;
As frasnian pointed out, this does a comparison and throws the result away. But even if you changed that to an assignment, you're dereferencing a freed pointer, which is undefined behavior. And that means anything could happen: it could work, it could crash, it could do something else unexpected.
int *y;
// y value 0xcccccccc{???}
Again, uninitialized.
y = new int();
// y value 0x001d9480{0}
Okay, you have a pointer to an int.
delete(y);
// y value 0x00008123{???} address has changed
That's unexpected. Might be a feature of your compiler. Might be an optimization reusing the register.
*y = 100; // Gives exception
Because you dereferenced a deleted pointer, and again you're in undefined behavior land. Apparently your platform does not like odd addresses for integers. Not all platforms throw exceptions here.
The rule is that a pointer that you have passed to free or delete is gone. Dead. An ex-pointer. Pushing up the bit-daisies. Pining for the fjords. It is no more.
There might be a value left over in the register, but it is no longer valid and should not be used. The compiler cannot be expected to catch the cases where you try to use a dead pointer.
Here is some C++ code.
#include <iostream>
using namespace std;
class test{
int a;
public:
test(int b){
a = b;
cout << "test constructed with data " << b << endl;
}
void print(){
cout << "printing test: " << a << endl;
}
};
test * foo(){
test x(5);
return &x;
}
int main()
{
test* y = foo();
y->print();
return 0;
}
Here is its output:
test constructed with data 5
printing test: 5
My question: Why does the pointer to x still "work" outside of the context of function foo? As far as I understand, the function foo creates an instance of test and returns the address of that object.
After the function exits, the variable x is out of scope. I know that C++ isn't garbage collected- what happens to a variable when it goes out of scope? Why does the address returned in foo() still point to what seems like a valid object?
If I create an object in some scope, and want to use it in another, should I allocate it in the heap and return the pointer? If so, when/where would I delete it
Thanks
x is a local variable. After foo returns there's no guarantee that the memory on the stack that x resided in is either corrupt or intact. That's the nature of undefined behavior. Run a function before reading x and you'll see the danger of referencing a "dead" variable:
void nonsense(void)
{
int arr[1000] = {0};
}
int main()
{
test* y = foo();
nonsense();
y->print();
return 0;
}
Output on my machine:
test constructed with data 5
printing test: 0
When a variable goes out of scope, the destructor is called (for non POD data) and the location occupied by that variable is now considered unallocated, but the memory isn't actually written, so the old value remains. This doesn't mean that you can still safely access this value because it resides in a location marked as 'free'. New variables can reside or allocation can occur in this memory space.
The reason why the memory isn't erased is because you can't actually erase memory, what you could do is write something to it like all zeros or all ones or random, which is not only pointless, but performance-degrading.
It has nothing to do with garbage-collection. A garbage collector doesn't "erase" memory, but marks it as being free. The reason why the behaviour you described exists in C and not in Java for instance is not the garbage-collector, but the fact that C lets you access via pointers any memory you want, allocated or not, valid or not, and Java doesn't (To be fair the garbage collector is a reason why Java can make so that you can't access any memory).
An analogy can be made with what happens on disk when you delete a file. The file contents remain (they are not overwritten), but instead pointers (handles) in the file system are modified so that that memory on the disk is considered free. That's why special tools can recover deleted files: the information is still there until something new writes over it, and if you can point to it you can obtain it. Is almost the same thing with pointers in C. Think what would it mean to actually write 4GB on disk every time you delete a 4GB file. There is no need to write in memory for each variable that goes out of scope the entire size of that variable. You just mark it's free.