There are two totally different answers to this question.
One is that it's the C implementation's job to assign addresses to objects. It's not your job, and it's arguably not your concern. Within very broad limits, the C implementation is allowed to assign any address whatsoever to your objects, so you shouldn't be surprised at anything it assigns. A newly in-scope object happens to have the same address as a different object that just went out of scope? Well, that's "anything". Not impossible, not surprising, not your problem.
And along the same lines, the initial value of an uninitialized variable is indeterminate. You have no idea what it might be; it might be anything. It just happens to have the same value as a previous variable from a previous function? Again, that's "anything", and it's not impossible, not surprising, not your problem.
Now, I know, that's your not your question. You're imagining that it's more than a coincidence that the new variable just happens to have the exact same address, and the exact same value, as the previous variable. You're imagining that it must mean something.
So that's the second answer.
Many C implementations store a function's local variables on a stack. Each variable's address is typically defined as an offset within a function call's stack frame. When one function returns, its stack frame is popped off the stack, and when the next function is called, its stack frame will occupy the same, newly-released portion of the stack. So if the previous and the next function both had the same variable(s) of the same type(s), their offsets with respect to the stack frame will probably be the same. So if the previous and the next stack frame are at the same spot on the stack, the variables within those stack frames will be at the same addresses, too.
Furthermore, when a function returns, although its stack frame is popped from the stack, that does not mean that anything actually gets cleared. And when a newly-called function has its stack frame allocated, nothing gets cleared at that point, either. So if a function has a local variable that's not explicitly initialized, its actual initial value — the value that we said was "indeterminate" — will actually be whatever bit pattern was sitting leftover on the stack, left by the last function whose stack frame was there. So if the last function had the same variable at the same stack frame offset, you may find that, lo and behold, the next function's same-offset variable will start out containing the same value that the previous function's variable had.
But this is obviously all happenstance and chance, not anything you can ever depend on. What you heard about local variables not preserving their values (let alone another function's value) is perfectly true. If you don't want an uninitialized variable like y
in function testB
starting out containing tantalizingly surprising values, then initialize it to a suitably unsurprising value that means something to testB
.