0

Edit: my question was relating to the difference between compilers but I was just mislead to think there was different behavior when really both compilers were showing the expected "undefined behavior"

I know that using the static keyword here would be good for the building_num integer, but I don't understand why the value prints correctly when compiling with g++, I would think the integer value should be erased after the assign_building_num function terminates

With visual studio's compiler I see the behavior I assume, where it doesn't print out 400, it prints out some random number (in this case it is always 32759 for some reason)

#include <iostream>
#include <string>

using namespace std;

class building {
    int* ptr_building_num;

public:
    void set_building_num(int* num_ptr) {
        ptr_building_num = num_ptr;
    }

    void print_building() {
        cout << *ptr_building_num << flush;
    }
};

void assign_building_num(building* building_ptr) {
    //int* ptr_to_building_num = nullptr;
    int building_num = 400;
    int* ptr_to_building_num = &building_num;

    building_ptr->set_building_num(ptr_to_building_num);
}

int main() {
    building* ptr_to_some_building = nullptr;
    ptr_to_some_building = new building;

    assign_building_num(ptr_to_some_building);

    ptr_to_some_building->print_building();

    return 0;
}
Tyler
  • 3
  • 4
  • 2
    `ptr_to_building_num` points to the variable that immediately goes out of scope, so class instance ends up with a dangling pointer. When this pointer is dereferenced behavior is undefined. – user7860670 Mar 13 '22 at 21:35
  • @user7860670 so then why does ptr_to_building_num have the value of 400 still when I compile with g++? It is out of scope so that value should have been erased like what happens with visual studio? – Tyler Mar 13 '22 at 21:43
  • see [Undefined, unspecified and implementation-defined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – user7860670 Mar 13 '22 at 21:48
  • You can't *erase* a value, you can only overwrite it with something else. "Undefined behaviour" means that anything can happen and that you can't make any assumptions about what the program should or shouldn't do. – molbdnilo Mar 13 '22 at 21:48
  • @Tyler Why should the implementation go to the trouble of erasing the value when it's cheaper and easier not to bother? What benefit does it gain from that effort? – David Schwartz Mar 13 '22 at 21:53
  • @DavidSchwartz I thought memory was like volatile and only holds data for as long as it needs. So once memory ceases to have electricity ran through it is the only time where data is erased without it being overwritten? – Tyler Mar 13 '22 at 22:11
  • @Tyler That's a detail about how RAM is manufactured and implemented. It has no effect on the operation of the system. The system doesn't try to remove power or stop refreshing individual chunks of memory just because they're unused. And if you think about it, that's all but impossible. Think about how complex the mechanism would be to allow removing power or refresh from individual chunks of memory and how much effort the system would have to waste to control such a thing. – David Schwartz Mar 14 '22 at 23:01

2 Answers2

2

Variables declared in a scope (in this case, the scope for building_num is the body of the function assign_ building_num) cease to exist once the scope exits.

You are taking the address of an integer that only has a short lifetime, and retaining its address after the object has been destroyed.

Reading from a pointer to a destroyed object is Undefined Behavior, so both compilers are correct.

The integer whose address that your building object holds must live at least as long as the building object itself.

EDIT: Since a question Undefined Behavior came up, here's what cppreference says about it, for completeness:

undefined behavior - there are no restrictions on the behavior of the program. Examples of undefined behavior are data races, memory accesses outside of array bounds, signed integer overflow, null pointer dereference, more than one modifications of the same scalar in an expression without any intermediate sequence point (until C++11)that is unsequenced (since C++11), access to an object through a pointer of a different type, etc. 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.

Chris Uzdavinis
  • 6,022
  • 9
  • 16
  • oh so "undefined behavior" means it could end up having the correct behavior one compilation but the next it has incorrect behavior? – Tyler Mar 13 '22 at 21:45
  • 1
    @Tyler Yes, the meaning is quite literal in that the behavior of the code is undefined, which means any behavior is permitted, which includes the desired behavior. – Kyle Mar 13 '22 at 21:48
  • 2
    @Tyler No, "undefined behavior" means that behavior is not correct even if it matches your expectations. It is wrong to have expectations about "undefined behavior" on the first place. Also it is not possible to write any kind of check to figure out whether "undefined behavior" is actually "undefined". It is programmer's responsibility to ensure that program does not yield any "undefined behavior". It is one of the reasons why C++ is considered a difficult language. – user7860670 Mar 13 '22 at 21:49
  • @Tyler Getting a garbage answer that happens to correspond to the one you had hoped for is the worst possible outcome--anything is possible but an answer resembling what you expect can falsely give the impression that the code works when it is severely and (quite possibly) catastrophically flawed. A broken clock is right twice a day too, but only by accident. – Chris Uzdavinis Mar 13 '22 at 22:00
  • @user7860670 okay right, makes sense. Thanks – Tyler Mar 13 '22 at 22:06
  • @ChrisUzdavinis absolutely, that is what happened here and lead me to wrack my brain for a couple hours about it. Thanks for the replies – Tyler Mar 13 '22 at 22:08
  • @Tyler also worth reading: [Undefined behavior can result in time travel](https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633) – user7860670 Mar 13 '22 at 22:10
0

You have made some very , very convoluted code that ends up setting 'building.ptr_building_num' to point at

   void assign_building_num(building* building_ptr) {
    //int* ptr_to_building_num = nullptr;
       int building_num = 400;              <<<<===== this value here
    int* ptr_to_building_num = &building_num;

    building_ptr->set_building_num(ptr_to_building_num);
}

THats not valid. Because that variable will disappear once that function ends. Normally VS or GCC would warn you about this but you have done enough juggling that they cant see that you are doing that.

Why does it work sometimes and not others. becuase you have invoked Undefined Behavior, the result can be anything. On my VS system I got 400.

But if I change the code to

assign_building_num(ptr_to_some_building);

cout << "test test\n"; <<== add this

ptr_to_some_building->print_building();

then I get

test test
-858993460

Its not clear what you actually want to do. If you want building to point to an integer (why?) then you need to allocate that integer somewhere stable.

You hint at 'static' , this will certainly work

void assign_building_num(building* building_ptr) {
    //int* ptr_to_building_num = nullptr;
    static int building_num = 400; <<<=======================
    int* ptr_to_building_num = &building_num;

    building_ptr->set_building_num(ptr_to_building_num);
}

but is a very odd thing to do, all building instances will end up pointing to the same integer. Maybe thats what you want. Hard to say. Otherwise allocate on the heap. Or if it just one integer put it in the building object

pm100
  • 48,078
  • 23
  • 82
  • 145
  • Thank you, you answered my question. I didn't realize that both compilers were giving me undefined behavior. And yes I'm aware it's very convoluted crappy code, I ran into this behavior while doing a CS assignment and I recreated it here in the shortest code I could come up with quickly so I apologize that it's hard to read – Tyler Mar 13 '22 at 22:01
  • @Tyler if you get weird behavior when you run the program slightly differently thats a clear indication of UB. The other classics are a) debug build vs Release build b) a bigger data file . PS. the compilers dont 'give UB', your code 'gives UB' – pm100 Mar 13 '22 at 22:07