0

I'm a newb with c++, so forgive me, I've tried to make this example as simple as possible. I'm running into a segmentation fault in trying to assign a pointer's target value to another pointer's target value. What I've found is, the fault can be fixed, but only if I declare an extra variable which I never use, and which has no use in the program. Can anyone explain what's going on here?

int main() {
  int x = 3;
  // bool y; //Although y is never used, uncommenting this removes the fault!
  int *b1, *b2;
  b1 = &x;
  *b2 = *b1;
  cout << "b1 @ " << b1 << " value: " << *b1 << endl; //b1 @ 0x16fa3f518 value: 3 [when y is declared]
  cout << "b2 @ " << b2 << " value " << *b2 << endl; //b2 @ 0x16fa3f520 value 3 [when y is declared]
  return (0);
}

The result is a segmentation fault unless I declare another variable - it doesn't matter what type and it doesn't matter if it's ever assigned. In this example, declaring bool y removes the fault.

I'm guessing this has something to do with the program not assigning enough memory to deal with a second pointer address, but why is that the case and how does one address it?

joshstrike
  • 1,753
  • 11
  • 15
  • 6
    `*b2 = *b1;` is undefined behavior. `b2` does not point at any valid memory – UnholySheep Jun 19 '23 at 08:08
  • It was declared just as *b1 was. Why would it suddenly point at valid memory simply because I declared some random other variable before it? – joshstrike Jun 19 '23 at 08:10
  • 1
    It doesn't. You've just managed to hide the bug - although you might still get wrong behavior and/or crashes when turning on/off optimizations, using a different compiler, etc. Undefined behavior means that the behavior of your code is not defined - anything can happen – UnholySheep Jun 19 '23 at 08:11
  • @UnholySheep so I understand this... the reason that it can have its memory value assigned after I declare an unused variable is that the compiler has reserved that memory space, and now *b2 is just reading something that's randomly available... which could be anything? – joshstrike Jun 19 '23 at 08:13
  • 2
    @joshstrike *Why would it suddenly point at valid memory simply because I declared some random other variable before it?* -- The program is broken. It makes very little sense to attempt to figure out why a broken program behaves in any defined way. In other words attempting to break a program on purpose, and then conclude what it should or should not do is like a dog chasing its own tail. – PaulMcKenzie Jun 19 '23 at 08:13
  • 1
    [always enable compiler warnings](https://stackoverflow.com/q/57842756/995714), and run [Asan](https://en.wikipedia.org/wiki/Code_sanitizer) and [Ubsan](https://developers.redhat.com/blog/2014/10/16/gcc-undefined-behavior-sanitizer-ubsan) if possible and you'll get why the bug happens. Demo: https://godbolt.org/z/1ss93aeWT – phuclv Jun 19 '23 at 08:14
  • 3
    "Why would it suddenly point at valid memory" It wouldn't and it doesn't. Do not confuse "the program didn't crash this time" with "the bug is fixed", these are two completely different things. – n. m. could be an AI Jun 19 '23 at 08:14
  • 1
    @joshstrike *What I've found is, the fault can be fixed, but only if I declare an extra variable which I never use* -- This is not a fix. All you did was move (or mask) the bug to another place in the program, since the executable image has changed. A true fix is one where you know *why* the problem occurs, and address the actual problem. If you were a C++ programmer in a professional enviroment, and you stated that you fixed a bug by declaring a variable, you will be fired, or at the very least, laughed at. – PaulMcKenzie Jun 19 '23 at 08:19
  • @PaulMcKenzie I'm learning the language, so I'm trying to ascertain the mechanics of what's going on when I assign a value to an uninitialized pointer... in particular, I'm confused as to why that works if some other variable was initialized. Understanding exactly what the compiler is doing would help me visualize the situation. – joshstrike Jun 19 '23 at 08:21
  • 1
    @joshstrike -- I'll be honest with you -- you're going to find out it will be a waste of time. You know that the code you have is broken -- that's all you need to know. Second, what if you change the compiler options, or compiler itself, and the program behaves yet again differently. Are you going to spend time trying to figure out why the program behaves differently again? Read about the [as-if rule](https://en.cppreference.com/w/cpp/language/as_if) and undefined behavior. – PaulMcKenzie Jun 19 '23 at 08:24
  • @PaulMcKenzie I didn't say I "fixed" it. I'm here to ask what's going on under the hood that would make it correctly assign under this set of circumstances. Again, I am new at C++. I'm learning it for fun. I've got 25 years of professional experience in scripting languages and SQL. There's no need to get personal or snippy about it. You seem like a person who just wants to take the piss out of people for no reason. You haven't explained the reason for the behavior I'm seeing, you're just telling me to quit. Thanks a lot. – joshstrike Jun 19 '23 at 08:26
  • 1
    I don't know what I've stated that makes you react this way. I have 30 years of experience in the C++ programming language itself. Unlike the languages you have used, C++ (and C) has something called *undefined behavior*. This is a brand new concept to programmers who are new to C++ and C. When your program has undefined behavior, you cannot predict what will happen, again, because you have various compilers, compiler options (such as optimizations), and the compiler itself may look at your code and say "that can never happen, so I'll remove this entire chunk of code". – PaulMcKenzie Jun 19 '23 at 08:30
  • @PaulMcKenzie Yes, the undefined behavior is what I'm trying to understand. You said "If you were a C++ programmer in a professional enviroment, and you stated that you fixed a bug by declaring a variable, you will be fired, or at the very least, laughed at." Obviously, that would be true in any professional environment, but it's not helpful (and it's insulting). I am trying to create a mental image of what the compiler is doing when *b2 = *b1 if *b2 is unassigned. Please either contribute to my understanding of that or refrain from criticizing my lack of understanding. – joshstrike Jun 19 '23 at 08:33
  • I already mentioned optimizations, compiler settings, the as-if rule, etc. Thanks for not mentioning that I mentioned these topics to you. – PaulMcKenzie Jun 19 '23 at 08:34
  • 1
    @joshstrike there are a lot of different C++ compilers, so if you want to know what one specific compiler (including its specific version) does for this particular piece of *undefined behavior* you'd need to specify all of that. Which is why it's a "waste of time" in most cases, as different compilers (or different compiler versions, optimizations, etc) can and will do different things here – UnholySheep Jun 19 '23 at 08:35
  • @UnholySheep I'm using clang++, c++11... the weird thing is, when I simply declare `y`, I get `&y` at a memory address 0x16bddf517, then x at 0x16bddf518, which is where b1 is also, and b2 at 0x16bddf520. There's reliably a blank spot at 519. If I avoid declaring `y` and instead initialize `b2=new int` prior to `*b2 = *b1;`, I get the same gap in memory at 519. How can it reserve 520 if I never initialize b2, but do initialize y? – joshstrike Jun 19 '23 at 08:40
  • @joshstrike -- Change compiler options, such as optimization settings. Don't be surprised if things turn out differently again. – PaulMcKenzie Jun 19 '23 at 08:42
  • @PaulMcKenzie I'm getting the sense that you don't know the answer and you're bullying someone who's trying to figure it out. Whether or not you laugh at junior employees in your office is your business, but your comments here are extremely unhelpful. I feel unlucky that I ended up with you in this thread, because I still don't understand how this memory is being allocated and you're continually derailing and trying to make this about me not knowing anything. If I knew the answer I wouldn't be asking here. – joshstrike Jun 19 '23 at 08:47
  • 2
    Again, [as-if rule](https://en.cppreference.com/w/cpp/language/as_if). Did you read the section that mentions *undefined behavior*? I am not sure what you are looking for, but that explains what to expect with a broken program. A C++ compiler is free to do anything, because of the rule breakage. Then you can also use [this](https://godbolt.org/z/n1r16hhMs) site to see what happens with different compilers. As you can see, no fault is generated when using a specific compiler. – PaulMcKenzie Jun 19 '23 at 08:50
  • Then using [-O1](https://godbolt.org/z/bGxr9dG7q) optimization level, same gcc compiler, the results are now different (the `std::cout` now does not show up). This is exactly what I was referring to -- same compiler, different optimization settings, differing results. – PaulMcKenzie Jun 19 '23 at 08:59
  • 1
    Read this: https://en.cppreference.com/w/cpp/language/ub including the linked material at the end. – Jesper Juhl Jun 19 '23 at 09:06
  • @PaulMcKenzie So in my original case, the optimization was giving `b2` a phantom handle on an address that was it never initialized, which just happens to be available if I declared some extra variable? – joshstrike Jun 19 '23 at 09:06
  • You can see the [generated assembly code](https://godbolt.org/z/PM6dTzPqq). If you want to see what a compiler on certain settings does with broken code, that's the only real way to figure out what "strategy" it took. But usually, going down the assembly code route is reserved when non-broken code acts broken, i.e. looking for actual compiler code-generation bugs. – PaulMcKenzie Jun 19 '23 at 09:12
  • @PaulMcKenzie Thank you. Looking at that makes sense. It actually looks like the compiler infers it should move `b2` back into an earlier reserved memory register [-32] instead of [-24] when `y` has been declared but never used, thus (I think?) making `b2` able to behave as if it was initialized. That goes a long way to clarifying this for me. Cheers. – joshstrike Jun 19 '23 at 09:26
  • 1
    Pro tip: enable the address sanitizer (and UB sanitizer). They catch most of those errors, so experimening with UB stops being a complete gamble. – HolyBlackCat Jun 19 '23 at 10:00

0 Answers0