4

Is this small code UB?

void Test()
{
  int bar;
  printf("%p", &bar);  
}

IMO it's not UB, but I'd like some other opinions.

It simply prints the address of bar, even if bar has never been initialized.

alk
  • 69,737
  • 10
  • 105
  • 255
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • What about `printf("%d", bar);`? I remember some discussion differentiating between undefined and indeterminate... – Eugene Sh. Oct 07 '16 at 13:34
  • 4
    `printf("%p", &bar);` -> `printf("%p", (void*)&bar); `. This is probably not central to your question but C standard requires it. – P.P Oct 07 '16 at 13:34
  • 1
    @EugeneSh. `printf("%d", bar);` is definitly UB, it will print whatever is in memory at the address of `bar`. – Jabberwocky Oct 07 '16 at 13:35
  • @MichaelWalz But what you are saying is not UB, but just printing indeterminate value. UB is technically something allowed to summon nasal demons. – Eugene Sh. Oct 07 '16 at 13:37
  • @EugeneSh. I understand your point of view, but UB is a very large concept, anyway for me printing a non predictable value is also "undefined behaviour". – Jabberwocky Oct 07 '16 at 13:39
  • @MichaelWalz: It is not unpredictable, but indeterminate. Unless it is a trap representation, it is not UB. And trap representations on integers are pretty rare nowadays. – too honest for this site Oct 07 '16 at 13:40
  • @Olaf, Annex J.2 cites "The value of an object with automatic storage duration is used while it is indeterminate" as one of the sources of undefined behavior. On the other hand, that annex is not normative, and I'm having trouble finding normative provisions supporting it in this regard. – John Bollinger Oct 07 '16 at 13:52
  • @JohnBollinger: Hmm, I had something in mind about only trap representations invoke UB. But then, I never use such nonsense, so I'm not 100% sure. After all: who cares? It is just useless. – too honest for this site Oct 07 '16 at 13:54
  • @Olaf `{ int bar; printf("%d", bar); }` is be UB. From 6.3.2.1: 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** ... the **behavior is undefined**. – Riley Oct 07 '16 at 14:30
  • @Riley even if `bar` was declared with the (now obsolete) `register` storage class, the compiler should be smart enough not to put the variable into a register if it's address is used somewhere in the program. – Jabberwocky Oct 07 '16 at 14:38
  • @Riley: 6.3.2.1 is about conversion. Not sure how that is relevant here. AFAIS, the cited text is not from that section. Also an actual function parameter is not required to be an lvalue. And it is certainly not necessary to initialise an automatic object, but to have it determinate (resp. not indeterminate). Anyway, I don't care enough to continue; as I wrote above, be it UB or not, it is useless and wrong code anyway. – too honest for this site Oct 07 '16 at 14:40
  • @MichaelWalz: `register` is not obsolete, it just has typically not the same intention when used it had decades ago. (as long the user is aware of the changed relevance). – too honest for this site Oct 07 '16 at 14:41
  • @MichaelWalz Even if the compiler is smart enough to do something useful, UB is still UB. – Riley Oct 07 '16 at 14:44
  • @MichaelWalz: Unfortunately, unless something has changed, the Standard mandate that taking the address of a `register` variable is a constraint violation (thus requiring that even compilers where it would serve no useful purpose must record the qualifier's existence). Were that not the case, the Standard could attach useful meaning to the qualifier by saying that a compiler may assume a register variable will not be accessed via pointer anywhere that is sequenced before or after evaluation of a complete expression taking its address, and will be accessed *only* via pointer... – supercat Oct 07 '16 at 15:22
  • ...during the evaluation of such an expression. This, given `int register r;`, something like `get_int(&r)` would be equivalent to `int temp123 = r; get_int(&temp123); r=temp123;`, but far less verbose. – supercat Oct 07 '16 at 15:24

3 Answers3

8

TL:DR No, your code does not invoke UB by using anything uninitialized, as you might have thought.


The address of a(ny) variable (automatic, in this case) has a defined value, so irrespective of whether the variable itself is initialized or not, the address of the variable is a defined value. You can make use of that value. ( if you're not dealing with pointers and doing double-dereference. :) )

That said, strictly speaking, you should write

 printf("%p", (void *)&bar);

as %p expects an argument of type pointer to void and printf() being a variadic function, no promotion (conversion) is performed. Otherwise, this is a well-defined behavior.

C11, chapter §7.21.6.1

p The argument shall be a pointer to void. [.....]

T.C.
  • 133,968
  • 17
  • 288
  • 421
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
4

Is this small code UB?

Yes, it's UB because the conversion specifier p requires a void-pointer.

On the other hand the code below does not invoke UB

void Test(void)
{
  int bar;
  printf("%p", (void*) &bar);  
}

as the address of bar is well defined independently whether bar itself got initialised.

alk
  • 69,737
  • 10
  • 105
  • 255
2

This behavior is well defined.

The address of the variable is known. The fact that it hasn't been explicitly initialized doesn't matter.

dbush
  • 205,898
  • 23
  • 218
  • 273