0

The simplest example:

int a;
printf("%d\n", a); //Is this Undefined or Unspecified behavior?

N2346/6.3.2.1p2:

If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.

But N2346/6.7.9p10:

If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.

So we can conclude that the object is initialized to some indeterminate value. The indeterminate value is well defined at

N2346/3.19.2p1:

indeterminate value

either an unspecified value or a trap representation

Since the representation of int is never a trap and applying N2346/3.4.4p1

unspecified behavior

behavior, that results from the use of an unspecified value, or other behavior upon which this document provides two

we have that the program has unspecified behavior.

Where does this reasoning fail?

Some Name
  • 8,555
  • 5
  • 27
  • 77
  • 1
    Dup of [Reading an indeterminate value invokes UB?](https://stackoverflow.com/questions/40584969/reading-an-indeterminate-value-invokes-ub) and [(Why) is using an uninitialized variable undefined behavior?](https://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior) – Language Lawyer Aug 25 '21 at 06:42
  • 1
    @LanguageLawyer https://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior/40674888#40674888 probably answers the question. Thanks – Some Name Aug 25 '21 at 06:57
  • 1
    Mhm. Closing this as dupe then, if that answered the question. – Lundin Aug 25 '21 at 08:51
  • 1
    see https://stackoverflow.com/a/25074258/1505939 , Defect Report 451 addresses this – M.M Aug 25 '21 at 09:05
  • 1
    You seem to be reasoning that 6.7.9 10, 3.19.2 1, and 3.4.4 1 do not specify that accessing an uninitialized `int` can trap. As has been pointed out, that is not correct because an `int` can have trap representations. However, even in a C implementation where `int` does not have trap representations or where you access an uninitialized `char`, which cannot have trap representations, it is not correct to reason that because those paragraphs do not indicate that accessing the object can trap, that accessing the object does not have undefined behavior… – Eric Postpischil Aug 25 '21 at 11:58
  • 1
    … That is because 6.3.2.1 2 says accessing an uninitialized object of automatic storage duration whose address is not taken has undefined behavior. That is a rule, period. It is not a rule that depends on other rules about indeterminate behavior or trap representations; it is just a rule by itself. So 6.7.9 10, 3.19.2 1, and 3.4.4 1 are irrelevant. 6.3.2.1 2 **is** the rule that accessing an uninitialized object of automatic storage duration whose address is not taken has undefined behavior. – Eric Postpischil Aug 25 '21 at 12:00
  • 1
    @EricPostpischil: If some behavior would be unambiguously defined on platforms with particular characteristics in the absence of a rule characterizing it as Undefined Behavior, implementations are allowed to give priority to either the definition of the behavior or the characterization as Undefined, but there's no reason a quality implementation should give priority to the latter absent an obvious or documented reason for doing so. – supercat Aug 25 '21 at 15:08

1 Answers1

1

Where does this reasoning fail?

One failure is here

Since the representation of int is never a trap

int can have trap representations.

The only type that can't have trap representations is unsigned char

But there is also this part in the standard describing undefined behavior (from draft n1570):

J.2 Undefined behavior

...

An lvalue designating an object of automatic storage duration that could have been declared with the register storage class is used in a context that requires the value of the designated object, but the object is uninitialized. (6.3.2.1).

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • Agree, in general it could. So the main point of the UB here is the ability to have a trap representation. – Some Name Aug 25 '21 at 08:00
  • 1
    Depends on storage duration. Accessing an automatic storage duration variable which never had its address taken by the program is always UB. Otherwise, trap representations apply but only on highly exotic or fictional systems using 1's compl or signed magnitude. 2's compl `int` don't have trap representations. Though in theory an implementation could allocate 4 bytes for an `int` value and then some other overhead crap bytes from traps, though such systems don't exist in the real world either. – Lundin Aug 25 '21 at 08:53
  • 1
    Also, it's been proposed for the upcoming C standard that moronic/fictional signedness format support should be removed from the language and 2's complement become required. Better 40 years late than never. – Lundin Aug 25 '21 at 08:57
  • Even though there are no trap reprsentations, there can still be indeterminate value of unsigned char, and DR 451 covers that indeterminate values persist (they don't "collapse" to a stable unspecified value -- they behave like a new unspecified value for each subsequent read) – M.M Aug 25 '21 at 09:08
  • @Lundin: Useful optimizations for partially-initialized structures may have corner-case behaviors which would be observably inconsistent with sequential program execution, but that would only be relevant if odd behaviors are limited to the fields that aren't written. Unfortunately, the Standard interprets the "as-if" rule as implying that the only way to allow an optimization is to characterize as UB all situations where its effects might be inconsistent with sequential program execution. – supercat Aug 25 '21 at 15:20
  • @M.M Yes but reading an indeterminate value is merely unspecified behavior. It won't cause crashes or signals getting raised. The C standard explicitly says that reading an indeterminate value cannot lead to a trap. – Lundin Aug 25 '21 at 15:43
  • @supercat There's no corner cases as far as I know. Padding bytes of automatic storage variables have unspecified values - padding bytes of static storage variables are initialized to zero. Also "The value of a structure or union object is never a trap representation, even though the value of a member of the structure or union object may be a trap representation." And padding bytes aren't members, so I don't see how they could hold traps. – Lundin Aug 25 '21 at 15:49
  • @Lundin: If code writes some fields of an automatic object and then copies it to two other objects, performance could sometimes be improved while still meeting many applications' requirements if an implementation only copied the members that had been written even though such behavior would be inconsistent with having automatic-duration structures iniitalized to Unspecified bit patterns. – supercat Aug 25 '21 at 16:01
  • @Lundin passing the indeterminate value to a library function (e.g. `printf` as in the question) causes undefined behaviour though, this is in the resolution to DR 451 – M.M Aug 25 '21 at 22:14
  • @M.M: I don't think the Committee has ever articulated a clear consensus understanding as to whether the Standard is intended to give priority to (1) ensuring that it defines the behavior of all constructs programmers might need to efficiently accomplish what needs to be done, or (2) ensuring that it does not impose any requirements that it might be impractical for *all* implementations to meet in *all* circumstances. Many of its judgment calls would be reasonable if the Standard made clear that it gave priority to #2, but some compiler writers treat it as giving priority to #1. – supercat Aug 26 '21 at 21:15
  • @M.M: If an implementation targets an execution environment whose low-level I/O functions may sometimes behave oddly if given a pointer to an only-partially-initialized buffer of automatic duration, should the implementation be required to initialize automatic objects to ensure that never happens? Or should programmers *who need their code to work in such environments* be required to ensure that all bytes of any object given to `fwrite` will be initialized before an `fwrite` call? – supercat Aug 26 '21 at 21:44