Is the stack variable deallocated when it goes out of scope? [duplicate] - c++

This question already has answers here:
Is it OK to reference an out-of-scope local variable within the same function?
(4 answers)
Closed 4 years ago.
sample1.cpp
#include <iostream>
int main()
{
int* aPtr = nullptr;
{
int a = 3;
aPtr = &a;
}
std::cout << *aPtr << std::endl;
return 0;
}
Output
3
I am able to access a through aPtr.
Does that mean a is not deallocated even after it goes out of
scope.
Does that mean a is deallocated only after the function in
which it is defined unwinds.
Or is this an undefined behavior that currently outputs some value?
sampe2.cpp
#include <iostream>
struct Box
{
Box(int a_)
:a(a_)
{}
int getValue() const { return a;}
~Box()
{
std::cout << "Destructor called" << std::endl;
}
private:
int a;
};
int main()
{
Box* boxPtr = nullptr;
{
Box box = 23;
boxPtr = &box;
}
std::cout << boxPtr->getValue() << std::endl;
return 0;
}
Output
Destructor called
23
I am able to access box through boxPtr even after destructor of box is called.

As mentioned in the comments, in both cases you are facing undefined behavior (so anything is allowed to happen). The stack variables are destroyed as soon as they go out of scope, the memory occupied by them is freed, although usually it's not immediately overwritten (at least in simple cases like above), therefore the pointer pointing at such variable, might sometimes exhibit more or less "valid" properties (as in it looks like the object is still valid), despite it clearly dangling.

Related

Why does the pointer exist even after the unique_ptr to which the pointer is assigned goes out of scope? [duplicate]

This question already has answers here:
Dereferencing deleted pointers always result in an Access Violation?
(6 answers)
Why is the phrase: "undefined behavior means the compiler can do anything it wants" true?
(2 answers)
Pointers in c++ after delete
(3 answers)
Why does calling method through null pointer "work" in C++? [duplicate]
(4 answers)
Closed 6 months ago.
I recently started learning about smart pointers and move semantics in C++. But I can't figure out why this code works. I have such code:
#include <iostream>
#include <memory>
using namespace std;
class Test
{
public:
Test()
{
cout << "Object created" << endl;
}
void testMethod()
{
cout << "Object existing" << endl;
}
~Test()
{
cout << "Object destroyed" << endl;
}
};
int main(int argc, char *argv[])
{
Test* testPtr = new Test{};
{
unique_ptr<Test> testSmartPtr(testPtr);
}
testPtr->testMethod();
return 0;
}
My output is:
Object created
Object destroyed
Object existing
Why does row testPtr->testMethod() work? Doesn't unique_ptr delete the pointer assigned to it on destruction if the pointer is an lvalue?
Edit: I learned from the comments that this method doesn't check if the pointer exists. If so, is there a way to check if the pointer is valid?
Edit: I learned that I shouldn't do anything with invalid pointers. Thank you for all your answers and comments.
Edit: Even this code works:
#include <iostream>
#include <memory>
using namespace std;
class Test
{
public:
Test(int num) :
number{ num }
{
cout << "Object created" << endl;
}
void testMethod(int valueToAdd)
{
number += valueToAdd;
cout << "Object current value: " << number << endl;
}
~Test()
{
cout << "Object destroyed" << endl;
}
private:
int number;
};
int main(int argc, char *argv[])
{
Test* testPtr = new Test(42);
{
unique_ptr<Test> testSmartPtr(testPtr);
}
testPtr->testMethod(3);
return 0;
}
I think it's because the compiler optimized it. Anyway, I really shouldn't do anything with invalid pointers.
You do not need a std::unique_ptr to write code with the same issue
int main(int argc, char *argv[])
{
Test* testPtr = new Test{};
delete testPtr;
testPtr->testMethod(); // UNDEFINED !!!
return 0;
}
The output is the same as yours here https://godbolt.org/z/8bocKGj1M, but it could be something else entirely. The code has undefined behavior. You shall not dereference an invalid pointer.
If you had actually used members of the object in testMethod() some faulty output or a crash is more likely, but also not guaranteed. Looking ok is the worst incarnation of undefined behavior.
Your code demonstrates nicely why you should ban raw new completely. At least you should call new only as parameter to the smart pointers constructor, or even better, use std::make_unique. Its basically just a wrapper around a constructor call via new and its main purpose is to let you write code that is free of new:
int main(int argc, char *argv[])
{
auto testPtr = std::make_unique<Test>();
testPtr->testMethod();
return 0;
}
Even then you can access the raw pointer and do wrong stuff. Smart pointers help with ownership, but they are not fool-proof.

How can a pointer to local scoped object still invoke the object method?

In my understanding that might be wrong, declaring a pointer to local object is dangerous or probably has no purpose.
The following globally defined function Factory that returns a pointer to a local object has no purpose. Right after invoking Factory(), the returned address is pointing to a local object that has been destroyed.
Foo* Factory()
{
// It is wrong to return the address of a local object!
// It is only for illustration!
Foo local{ 100 };
return &local;
}
Invoking Print() produces a garbage value. This is understandable so far.
Now consider the second case with a local scope as follows.
int main()
{
Foo* p ;
{
Foo local{ 100 };
p = &local;
}
p->Print();// 100 is printed even though p is pointing to a local scoped object that has been destroyed.
return 0;
}
What I don't understand is why I can still invoke Print() on the pointed object that should have been destroyed. Any comments are welcome!
Complete Example
#include <iostream>
class Foo
{
private:
int _data;
public:
Foo(int data) :_data{ data }
{
std::cout << "Ctor: " << this << std::endl;
}
~Foo()
{
std::cout << "Dtor: " << this << std::endl;
}
void Print()
{
std::cout << "Data: " << _data << std::endl;
}
};
Foo* Factory()
{
// It is wrong to return the address of a local object!
// It is only for illustration!
Foo local{ 100 };
return &local;
}
int main_1()
{
Foo* p = Factory();
p->Print();// Garbage gets printed because p is pointing to a local object that has been destroyed.
return 0;
}
int main_2()
{
Foo* p ;
{
Foo local{ 100 };
p = &local;
}
p->Print();// 100 is printed even though p is pointing to a local scoped object that has been destroyed.
return 0;
}
Undefined behavior
First I want to give you a brief explanation of undefined behavior. If you want to you can read more here Undefined behavior.
Undefinded behavior renders the entire program meaningless if certain rules of the language are violated. The compiler can basicly decide what to do. But like mentioned on cppreference: Compilers are not required to diagnose undefined behavior (although many simple situations are diagnosed), and the compiled program is not required to do anything meaningful. So in short there are no restrictions on the behavior of the program.
Your case
Both examples invoke undefned behavior:
int main_2()
{
Foo* p ;
{
Foo local{ 100 };
p = &local;
}
p->Print();
return 0;
}
int main_1()
{
Foo* p = Factory();
p->Print();
return 0;
}
So like we learned undefinded behavior is undefinded. So anything can happen. In the second case your compiler was probably smart enough to notice the issue.Because the behavior is undefined your program could also crash or do other weird things.

Why doesn't this dangling std::weak_ptr cause SEGFAULT? [duplicate]

This question already has answers here:
When does invoking a member function on a null instance result in undefined behavior?
(2 answers)
Closed 4 years ago.
In the following code I create a shared_ptr in the scope and assign it to a weak_ptr. How come when running the code I don't get SEGFAULT, because wp should be invalid out of scope, right?
namespace {
struct Dummy {
int x;
void foo() {
std::cout << "dummy created\n";
}
~Dummy()
{
std::cout << "dummy destroyed\n";
}
};
}
TEST(experimental, ptr_test){
std::weak_ptr<Dummy> wp;
{
auto sp = std::make_shared<Dummy>();
wp = sp;
}
wp.lock()->foo();
};
You're not actually dereferencing anything there though. The lock method will still return a shared_ptr if the locked shared_ptr is null, but that shared_ptr will also be null. In this example, foo doesn't crash on my compiler since it never dereferences the null pointer but it's undefined behavior so you never know what will happen. However, bar will always crash since it needs to dereference the pointer in order to get to x.
The reason why this happens to work is that all member functions compile to normal functions that take a pointer to the object as their first parameter accessible from the function body as this. Calling this function on nullptr will probably work most of the time if nothing in the body of the function dereferences this. You shouldn't do it though, future compiler changes or a port to another architecture could cause this to crash.
#include <iostream>
#include <memory>
struct Dummy {
int x;
Dummy()
: x(10) {
std::cout << "Dummy created" << std::endl;
}
~Dummy() {
std::cout << "Dummy destroyed" << std::endl;
}
void foo() {
std::cout << "foo" << std::endl;
}
void bar() {
std::cout << x << std::endl;
}
};
int main() {
std::weak_ptr<Dummy> wp;
{
auto sp = std::make_shared<Dummy>();
wp = sp;
}
auto locked = wp.lock();
if(locked.get() == nullptr) {
std::cout << "Locked pointer is null" << std::endl;
}
locked->foo(); // Does not crash
((Dummy*)nullptr)->foo(); // Does not crash
locked->bar(); // Will crash
}
Generally, you won't get a segfault unless you actually do something with the invalid memory (and then it won't always segfault - it is up to the hardware to send a signal to the OS, and then up to the OS to actually crash the program). If you were to set x in foo, you may have a better chance of seeing a segfault - but as user2357112 pointed out, the c++ standard does not guarantee a segfault for invalid code.

Why there is still valid access to struct after leaving the scope? [duplicate]

This question already has answers here:
Can a local variable's memory be accessed outside its scope?
(20 answers)
Closed 9 years ago.
First of all I'd like to understand why I get lines (5) and (6) in the output and not only one of them.
Second, why mc1.print() prints valid values? Shouldn't _ms point to undefined place after mc1(&MyStruct(mc2)) because the place where MyStruct was created (in cast operator) was already unwind?
struct MyStruct
{
int w;
int h;
MyStruct()
{
cout << "MyStruct" << endl;
}
~MyStruct()
{
cout << "~MyStruct: w=" << w << "h=" << h << endl;;
}
};
class MyClass1
{
public:
MyClass1(MyStruct* ms)
:_ms(ms)
{
cout << "MyClass1" << endl;;
}
~MyClass1()
{
cout << "~MyClass1" << endl;
}
void print()
{
cout << "print: w=" << _ms->w << "h=" << _ms->h << endl;
}
MyStruct* _ms;
};
class MyClass2
{
public:
MyClass2()
{
cout << "MyClass2" << endl;
}
~MyClass2()
{
cout << "~MyClass2" << endl;
}
operator MyStruct()
{
MyStruct ms;
ms.h = 11;
ms.w = 22;;
return ms;
}
};
int main()
{
MyClass2 mc2;
MyClass1 mc1(&MyStruct(mc2));
mc1.print();
return 0;
}
the output is
1. MyClass2
2. MyStruct
3. ~MyStruct: w=22h=11
4. MyClass1
5. ~MyStruct: w=22h=11
6. ~MyStruct: w=22h=11
7. print: w=22h=11
Why there is still valid access to struct after leaving the scope?
There isn't. It isn't valid. You can try to read arbitrary memory addresses, but doing so is not valid". The C++ standard just doesn't say what should happen if you do something "invalid". So your code is allowed to do what you're observing.
First of all I'd like to understand why I get lines (5) and (6) in the output and not only one of them.
I've no idea. I don't, but since the program has undefined behaviour, anything could happen in principle.
Second, why mc1.print() prints valid values?
It doesn't. It gives undefined behaviour, by printing whatever happens to be in the memory that was once occupied by the dead temporary, if that memory is still accessible. In your case, it just happens that nothing has reused or otherwise invalidated the memory, so you happen to see the values you put there when the temporary was alive.
Shouldn't _ms point to undefined place after mc1(&MyStruct(mc2)) because the place where MyStruct was created (in cast operator) was already unwind?
It points to some memory location which no longer contains a valid object. Typically, the stack frame isn't released until the function returns; and even when it is, the memory typically remains accessible. So accessing a dead object gives you some kind of undefined behaviour, which may not be whatever undefined behaviour you thought it should give.

Can Access Returned LValue Reference after object that allocates it is destroyed

I'm having trouble understanding why this code works. I've been in the C# world for awhile and wanted to brush up on C/C++ before diving into the new stuff in C++11 like RValue Refs and move semantics.
I'm wondering why this code that I wrote works:
class InnerMember
{
private:
int _iValue;
public:
InnerMember(): _iValue(0) {};
InnerMember(int iValue) : _iValue (iValue) {};
int GetValue(void) { return _iValue; }
int SetValue(int iValue) { _iValue = iValue; }
};
class TestClass
{
private:
InnerMember _objValue;
public:
TestClass() : _objValue(1) {};
void SetValue(int iValue)
{
_objValue.SetValue(iValue);
}
InnerMember& GetRef(void)
{
return _objValue;
}
virtual ~TestClass() { std::cout << "I've been released!" << std::endl; }
};
int main (int argc, char *argv[])
{
TestClass* pobjTest = new TestClass();
std::cout << "Before:" << std::endl;
std::cout << pobjTest->GetRef().GetValue() << std::endl;
pobjTest->SetValue(5);
InnerMember& robjInner = pobjTest->GetRef();
delete pobjTest;
std::cout << "After:" << std::endl;
std::cout << robjInner.GetValue();
return 0;
}
The output is:
Before:
1
I've been released!
After:
5
Press any key to continue...
I thought that this would cause an error, since I access the referenced object InnerMember from TestClass after TestClass has been destroyed. Is there some sort of return value optimization going on? Or is it really returning a copy instead of passing back the reference?
I used GCC to with no optimizations (-O0) and it still ran without an issue.
I also used the -S switch to generate the assembly but my AMD64 knowledge is rusty and the name mangling didn't help.
That is undefined behaviour, which means even the "correct" behaviour could happen. When you delete something in C++, it is not erased from the memory, so accessing it before something else writes over it will sometimes maybe still work.
robjInner is still a reference to some deleted object in memory.
This would lead to undefined behaviour.
After deletion, the reference robjInner has been left dangling. You get back the previous value because no one else claimed that piece of memory yet.
Copied from here
A previously-valid reference only becomes invalid in two cases:
If it refers to an object with automatic allocation which goes out of scope,
If it refers to an object inside a block of dynamic memory which has been freed.
The first is easy to detect automatically if the reference has static scoping, but is still a problem if the reference is a member of a dynamically allocated object; the second is more difficult to assure. These are the only concern with references, and are suitably addressed by a reasonable allocation policy.
You can add a print statement inside InnerMember destructor to see what is going on. You will see InnerMember is destroyed after TestClass and the 5 you get is because no one write to that part of memory yet. But that reference is not valid anymore.