2

I've been doing some tests with pointers and came across the two following scenario. Can anybody explain to me what's happening?

void t ();
void wrong_t ();

void t () {
        int i;
        for (i=0;i<1;i++) {
                int *p;
                int a = 54;
                p = &a;
                printf("%d\n", *p);
        }
}

void wrong_t() {
        int i;
        for (i=0;i<1;i++) {
                int *p;
                *p = 54;
                printf("%d\n", *p);
        }
}

Consider these two versions of main:

int main () {
        t();
        wrong_t();
}

prints: 54\n54\n, as expected

int main () {
        wrong_t();
}

yields: Segmentation fault: 11

I think that the issue arises from the fact that "int *p" in "wrong_t()" is a "bad pointer" as it's not correctly initialized (cfr.: cslibrary.stanford.edu/102/PointersAndMemory.pdf, page 8). But I don't understand why such problem arises just in some cases (e.g.: it does not happen if I call t() before wrong_t() or if I remove the for loop around the code in wrong_t()).

  • 3
    `*p = 54;` blows up because although you *declared* p to be a pointer to an int, you didn't actually *point* it at anything. So when you do `*p` you are pointing at random memory based on whatever was on the stack at that moment. In the other routine you first assigned a good value to p and *then* you dereferenced it. – Peter Rowell Feb 18 '13 at 23:57
  • Already explained here http://stackoverflow.com/questions/7240365/pointer-default-value – QuantumBlack Feb 18 '13 at 23:57
  • You say `"prints: 54\n54\n, as expected"`. That is not true. The expected behavior is undefined. The preferred behavior there (from a developer perspective) is a crash, not the output you suggest. – Bill Lynch Feb 18 '13 at 23:58

3 Answers3

4

Because dereferencing an uninitialised pointer (as you correctly guessed) invokes undefined behaviour. Anything could happen.

If you want to understand the precise behaviour you're observing, then the only way is to look at the assembler code that your compiler produced. But this is normally not very productive.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • This explains why the C standard permits this behavior to occur. It does not explain what is happening. The latter question was asked. The former question was not. – Eric Postpischil Feb 19 '13 at 00:03
  • @EricPostpischil: If that's the case, then the answer is "it doesn't matter". – Oliver Charlesworth Feb 19 '13 at 00:05
  • Any use of an uninitialized pointer invokes undefined behavior. For instance `printf("%p\n", (void *) unintialized_pointer);` – Kaz Feb 19 '13 at 00:07
  • @OliCharlesworth: Of course it matters. Not everybody can or wants to work only inside the C model. Some people need to know or want to know how the rest of the hardware and the software work. Even people who want to work only in C benefit from understanding the rest of the computer, because it helps them debug by helping them interpret the odd behaviors that are caused by bugs. – Eric Postpischil Feb 19 '13 at 00:11
  • @EricPostpischil: Agreed. But then the best way to understand the behaviour is to take a look at the underlying code, which is what my answer suggests ;) – Oliver Charlesworth Feb 19 '13 at 00:13
  • @OliCharlesworth: Certainly if a person knows assembly language for their target processor, looking at the generated assembly language can be informative. However, anybody who knows assembly language likely already knows the answer to this question. For somebody who does not know assembly language, asking why this behavior occurs is a very reasonable question. And it got them an answer that explains what occurred. – Eric Postpischil Feb 19 '13 at 00:15
  • @OliCharlesworth Taking the address clearly isn't use. (It depends on of what: `&foo->bar` uses the value of `foo`; `&foo` doesn't use foo). It wouldn't make sense to rule out `init_func(&uninited_var);`. – Kaz Feb 19 '13 at 02:53
3

What happens is almost certainly:

In both t and wrong_t, the definition int *p allocates space for p on the stack. When you call only wrong_t, this space contains data left over from previous activity (e.g., from the code that sets up the environment before main is called). It happens to be some value that is not valid as a pointer, so using it to access memory causes a segment fault.

When you call t, t initializes this space for p to contain a pointer to a. When you call wrong_t after this, wrong_t fails to initialize the space for p, but it already contains the pointer to a from when t executed, so using it to access memory results in accessing a.

This is obviously not behavior you may rely on. You may find that compiling with optimization turned on (e.g., -O3 with GCC) alters the behavior.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

In wrong_t function, this statement *p = 54; is interesting. You are trying to store a value into a pointer p for which you haven't yet allocated the memory and hence, the error.

Ganesh
  • 5,880
  • 2
  • 36
  • 54