0

In C is it valid to use a variable in the same statement in which it is declared?

In both gcc 4.9 and clang 3.5 the following program compiles and runs without error:

#include "stdio.h"

int main() {
  int x = x;
  printf("%d\n", x);
}

In gcc it outputs 0 and in clang 32767 (which is largest positive 2-byte integer value).

Why does this not cause a compilation error? Is this valid in any particular C specification? Is its behavior explicitly undefined?

5
  • 2
    In MSVC it outputs "warning C4700: uninitialized local variable 'x' used". Please enable compiler warnings before asking silly questions. Commented Oct 3, 2016 at 20:52
  • 1
    @WeatherVane: The fact that a compiler issues a warning doesn't tell you whether it's valid. Commented Oct 3, 2016 at 21:03
  • MSVC outputs at runtime -1373317485 Well, once, various values. Commented Oct 3, 2016 at 21:03
  • 1
    @KeithThompson it is a pretty strong indicator. As you well know, C does not protect you against yourself. Commented Oct 3, 2016 at 21:06
  • @WeatherVane: It's a pretty strong indicator that it's not something you should do, but that wasn't the question. [reposting comment to fix typo] Commented Oct 3, 2016 at 22:26

4 Answers 4

4
int x = x;

This is "valid" in the sense that it doesn't violate a constraint or syntax rule, so no compile-time diagnostic is required. The name x is visible within the initializer, and refers to the object being declared. The scope is defined in N1570 6.2.1 paragraph 7:

Any other identifier [other than a struct, union, or enum tag, or an enum constant] has scope that begins just after the completion of its declarator.

The declarator in this case is int x.

This allows for things like:

int x = 10, y = x + 1;

But the declaration has undefined behavior, because the initializer refers to an object that hasn't been initialized.

The explicit statement that the behavior is undefined is in N1570 6.3.2.1 paragraph 2, which describes the "conversion" of an lvalue (an expression that designates an object) to the value stored in that object.

Except when [list of cases that don't apply here], an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion.
[...]
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.

The object in question is x, referenced in the initializer. At that point, no value has been assigned to x, so the expression has undefined behavior.

In practice, you'll probably get a compile-time warning if you enable a high enough warning level. The actual behavior might be the same as if you had omitted the initializer:

int x;

but don't count on it.

Sign up to request clarification or add additional context in comments.

6 Comments

Would it make a difference if code had been unsigned char x = x;? [C11 6.2.6.2 1]
That's all correct, but it sort of sidesteps the issue. You could use self-referential initialization to construct circular lists, for example: struct node {int value; struct node *next, *prev;} x = (struct node){argc, &x, &x};. As far as I know that is legal and produces the expected result.
@chux: I don't think so. 6.3.2.1p2 still applies.
@rici: What issue is being sidestepped? Your example doesn't apply to the OP's declaration int x = x;. And it's not evaluating the value of the object, it's just taking its address, which is well defined whether it's been initialized or not.
@keith: precisely, but it is only legal because the variable is already in scope at that point, which would not be the case in many other languages. That is, to my mind, the issue raised by the question in the title.
|
2

According to the language specification

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

Further, it says

6.7.8.11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion).

Hence, the value of the initializer expression (x to the right of =) is indeterminate, so we are dealing with undefined behavior, because initializer reads from variable x that has indeterminate value.

Various compilers provide warning settings to catch such conditions.

3 Comments

You missed the part that makes it undefined behavior.
@DavidSchwartz - wouldn't its value is indeterminate. be equal to UB?
@KevinDTimm Not immediately. Having an indeterminate value around is not a UB, as long as you do not read it prior to an assignment. That's why I edited the answer to clarify David's point.
1
int x = x;

is cause for undefined behavior. Don't count on any predictable behavior.

5 Comments

Any reference to the standard that would show this? I agree with you that intuitively, it's undefined behavior. It would be nice to have an authoritative source, though.
It's the lvalue to rvalue conversion that's UB since the value is indeterminate.
@Sjlver, I couldn't find anything more than what dasblinkenlight quoted.
There's an explicit statement that an lvalue-to-rvalue conversion whose value is indeterminate is UB.
@DavidSchwartz: I cited it in my answer.
1

Clang does warn about this:

$ clang -c -Wall ub_or_not_ub.c
ub_or_not_ub.c:4:11: warning: variable 'x' is uninitialized when used within its own initialization [-Wuninitialized]
  int x = x;
      ~   ^

So I guess it's undefined behavior.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.