1

I met a question about the behaviour of returning local pointers in C. I have a code:

static void *uar(int c)
{

    char name[3]={"OK"};
    char *name2;
    name2 = name;
    if(c == 0)
        return name;
    else
    return name2;
}

int main(){
    char *res;
    char *res2;
    res = uar(0);
    res2 = uar(1);
    printf("The res is %s\n",res);
    printf("The res2 is %s\n",res2);
    return 0;
}

The pointer name and name2 both point to a local array. And both of them are local pointers. However, name2 can be successfully returned. I am so confused. Could anyone give me an explanation?

Jasond
  • 11
  • 3
  • 2
    Is this working as expected? I doubt that. Because the local array `name` is out of scope as soon as the function returns and `name2` pointing to `name` will then be pointing to nothing. – kiner_shah Oct 28 '19 at 07:03
  • 1
    _However, name2 can be successfully returned._ The compiler is not required to set with zeroes the local array inmediately when you exit the function, use `static char name[] = "OK";` – David Ranieri Oct 28 '19 at 07:07
  • 1
    `name` is not a pointer, but an `array`. – alk Oct 28 '19 at 07:09
  • That is exactly what I thought. The name is supposed to out of scope and the name2 should not work either. However, the result is that name2 can still be returned. – Jasond Oct 28 '19 at 07:11
  • "*... name2 can still be returned*" but it's pointing to invalid memory, the moment the function ended. Printing out to what it points invokes undefined behaviour. Anything can happen, from seemingly working to crash to ... whatever. – alk Oct 28 '19 at 07:13
  • It can be returned, the compilation won't fail because of it. It will run but when you use those returned pointers, no matter if it is `name` or `name2` the behaviour is *undefined*! Which means anything can happen, including the fact that you may get printed "OK". – Alex Lop. Oct 28 '19 at 07:16
  • 1
    It's called _undefined behaviour_ (google that). Undefined behavour includes _apparently working fine_ – Jabberwocky Oct 28 '19 at 07:19
  • Not really a duplicate. The OP observes *and asks about* (on his machine) a behaviour difference between what should be 2 dangling pointers. That CAN be explained, and the explanation will help understanding what a particular implementation may do with the code. Just telling someone it is UB is too limited. – GermanNerd Oct 28 '19 at 07:29
  • 1
    @Jasond As has been pointed out, it is UB. But I understand that you want to explore the strange difference in behavior. As a first step, try to print the address values of both the array and the char pointer first inside uar(), then outside of it. Do you get differing results? If so, what are the differences? What compiler flags do you use, especially optimizations? These are hints to find out what's happening. You can also try to look at the corresponding machine code using godbolt.org. I consider such exercises as helpful in understanding what CAN happen in UB, and why it sometimes 'works'. – GermanNerd Oct 28 '19 at 07:53
  • Looked a bit into it. Check this out: https://godbolt.org/z/4-cYE8. It seems in line 11 of the assembly, the compiler simply puts 0 into the return register, which matches the compiler's detection of the UB. Big question: Why does it NOT do the same with the UB of returning the separate pointer? Seems inconsistent. – GermanNerd Oct 28 '19 at 09:09
  • @GermanNerd: "*seems inconsistent*" no, it's just "undefined" ...;) – alk Oct 28 '19 at 10:53
  • @alk The two things are not in contradiction. Of course it is UB, but it is still interesting to see how a compiler reacts to UB, especially UB that is detectable at compile time. In this case (gcc), the compiler behaves inconsistently in 2 ways: 1: It warns about the UB in one case, but not the other. That is, IMHO, a very bad idea. 2: It also emits differing code, which is almost as bad. UB happens, and when the coder observes some unexpected results, AND knows the compiler, it can be extremely helpful in spotting the UB. Inconsistent behaviour makes live a lot harder there. – GermanNerd Oct 28 '19 at 12:47
  • @GermanNerd Thank you so much for you generous help. You really got what had confused me. I have done experiments following your instruction. It took me a while to get familiar with machine code because I am new to that. I find the difference behaviour is cause by compiler. For name, it merely return 0 and for name2 it still returns the address of local array. The data in that address was not wipped away immediately after return. Hence, it seems working well.But it may eventually lead to UB. Am I right? – Jasond Oct 29 '19 at 05:30
  • @alk Thanks for correcting. I have learned a lot. – Jasond Oct 29 '19 at 09:28
  • @Jasond No, both returns constitute UB. So, one could end the discussion here, as others have obviously decided to do. However, I think it is quite a nice example in *what ways* UB can manifest itself. The behaviour for name2 is something one easily may expect, knowing about typical stack-based implementations of function calling. But it still is UB, and in the case of 'name', the compiler actually did something you would not expect from the architecture alone; I presume it is linked to optimization efforts. "The compiler is allowed to do ANYTHING with UB" - well demonstrated here. – GermanNerd Oct 31 '19 at 13:15

0 Answers0