0

I have this code that I'm using for something else, but, boiled it down to the root problem I think. If I enter 5 for the scanf variable when I run it, the printf out is 0,16. I don't understand why this is giving me 16 for *pScores?

#include <stdio.h>

int main(void) {
  int a=0;
  int sum=0;
  scanf("%d",&a);
  int scores[a];
  int *pScores = &scores[0];
  printf("%d, %d\n",scores[0],*pScores);
}
Joseph
  • 301
  • 3
  • 13
  • Well, you haven't stored any data in `scores[0]` yet. I would expect your code to print the same uninitialized value twice, but it's a weird and not-too-useful situation. What happens if you actually fill in some data? – Steve Summit Dec 08 '22 at 21:01
  • 1
    There's no need to use `sizeof` to calculate `size`. It will be the same as `a`. – Barmar Dec 08 '22 at 21:01
  • Are you intending for this to be an exercise in "Variable-Length Arrays"? – Ben Voigt Dec 08 '22 at 21:01
  • Yea I meant to delete the size line of code, it's for something else. I'm just trying to understand why I keep getting 16 for *pScores – Joseph Dec 08 '22 at 21:03
  • 1
    @SteveSummit Even if nothing is entered, shouldn't `scores[0]` and `*pScores` produce the same garbage? – Barmar Dec 08 '22 at 21:03
  • @Barmar: No! The Schroedinger wavefunction does not collapse! – Ben Voigt Dec 08 '22 at 21:03
  • 1
    @Barmar I would certainly think so, yes. – Steve Summit Dec 08 '22 at 21:03
  • 2
    I'm only able to reproduce this when I enable optimization. The optimizer detects the uninitialized array and compiles the array indexing differently. – Barmar Dec 08 '22 at 21:17

2 Answers2

2

You are declaring an array

int scores[a];

and then printing out the value of scores[0] in two different ways. However, you have not stored anything into any of the elements of the scores array, so the values there are indeterminate.

Whether use of uninitialized (and therefore indeterminate) values in this way actually rises to the level of Undefined Behavior is a surprisingly deep and actually somewhat contentious question. (See the comment thread raging at the other answer.) Nevertheless, printing an uninitialized value like this isn't terribly useful. If you fill in a well-defined value to at least scores[0], I believe you'll find that both scores[0] and *pScores will print the same — that same — value.

Now, you might expect that the uninitialized value — whatever it is — would at least be consistent no matter how you print it (and I might agree with you), but when it comes to gray areas like this, and especially when a modern compiler starts leveraging every nuance of the rules in performing aggressive optimizations, the end results can be pretty surprising. When I tried your program, I got the same number printed twice (that is, I couldn't initially reproduce your result), but as suggested by Barmar in a comment, when I turned on optimization (with -O3), I started seeing conflicting results, also.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
1

You have undefined behavior, caused by reading a variable with automatic storage duration whose value is indeterminate.

In 6.2.4 Storage durations of objects one finds the following rule

For such an object that does have a variable length array type, its lifetime extends from the declaration of the object until execution of the program leaves the scope of the declaration. If the scope is entered recursively, a new instance of the object is created each time. The initial value of the object is indeterminate.

Then in J.2 Undefined behavior:

The behavior is undefined in the following circumstances

...

  • The value of an object with automatic storage duration is used while it is indeterminate.

...

Among permitted very weird outcomes when dealing with indeterminate values is that they have a different value each time you read them. The Schroedinger wavefunction does not collapse!

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • While this is certainly allowed by the language spec, it's still surprising. Why would it generate different code for the two expressions? But maybe the compiler notices that it's not initialized, so doesn't actually generate any code to read it, and random stack garbage is being passed to printf. – Barmar Dec 08 '22 at 21:05
  • 2
    Looking at the generated code would be instuctive. OTOH, explaining why UB produces any specific result is just out of curiosity. – Barmar Dec 08 '22 at 21:06
  • 2
    Indeterminate, yes, but it's not clear the posted code is actually undefined. See [this question](https://stackoverflow.com/questions/11962457) and in particular its [accepted answer](https://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior/11965368#11965368). – Steve Summit Dec 08 '22 at 21:07
  • @SteveSummit: It's absolutely UB, standard citations added to answer. – Ben Voigt Dec 08 '22 at 21:10
  • 1
    @BenVoigt: Steve Summit is correct, the behavior is not defined. J.2 is non-normative; it is not an authoritative part of the standard, and it is in error on this point. The behavior is not undefined; it is defined to behave as if `scores[0]` has an indeterminate value. This is because, as you note, C 2018 6.2.4 6 says the value becomes indeterminate when the declaration is reached, and 3.19.2 says an indeterminate value is an unspecified value or a trap representation… – Eric Postpischil Dec 08 '22 at 21:17
  • @BenVoigt I see you've commented at the answer I linked to. We may be seeing some aspect of the "big mess" that Jens Gustedt was referring to, with a "gap between what is intended behavior and what is actually written up". – Steve Summit Dec 08 '22 at 21:18
  • @EricPostpischil: An unspecified value is *also* allowed to behave weirdly. – Ben Voigt Dec 08 '22 at 21:19
  • 1
    … There could be undefined behavior if a trap representation arose, but most modern C implementations do not have trap representations for the `int` type, so an indeterminate value cannot be a trap representation in such a C implementation. Aside from that, nothing else in the authoritative part of the C standard says the behavior would be undefined. 6.3.2.1 2 does say uses of uninitialized automatic objects have undefined behavior, but that does not apply because `scores` is an array and so could not have been declared `register` as needed for 6.3.2.1 2. – Eric Postpischil Dec 08 '22 at 21:19
  • @BenVoigt Don't know where my head was on that one... – dbush Dec 08 '22 at 21:19
  • @BenVoigt: “Behave weirdly” is not undefined behavior. An indeterminate value may behave as if it has a different value each time it is used, but it **must** as if it does have some value, possibly including a trap representation in C implementations that have them. In other words, if the C implementation does not have a trap representation for `int`, then `printf("%d\n", scores[0]);` **must** print some representable `int` value; it cannot do anything else, e.g., terminate the program, as would be allowed if the behavior were undefined. – Eric Postpischil Dec 08 '22 at 21:20
  • @EricPostpischil: J.2 is good enough for me. It expressly says this case is undefined behavior. – Ben Voigt Dec 08 '22 at 21:21
  • The table of contents says “Annex J (informative) Portability issues”. It is not a normative part of the standard. All of the normative annexes are marked “normative”, and they only define optional features. The C standard that specifies what implementations are to do in order to conform to the standard is chapters 1 to 7. – Eric Postpischil Dec 08 '22 at 21:28
  • @EricPostpischil: Appendix J is still a good way to understand the intent of the authors. If there's a conflict between that and the normative text, there's a very strong chance that the defect will be resolved by changing the normative text. – Ben Voigt Dec 08 '22 at 21:31
  • @BenVoigt: Your opinion about how the standard will be changed is very nice but entirely irrelevant. The normative part of the standard says that `scope[0]` has an indeterminate value, not that using it has undefined behavior. – Eric Postpischil Dec 08 '22 at 21:32