28

A simple program:

int main()
{
    long i = i;

    return 0;
}

Compiling as C gives no errors and no warnings.

$ gcc -Wall -Wextra -pedantic 1.c

Compiling as C++ gives a warning:

$ c++ -Wall -Wextra -pedantic 1.c
1.c: In function ‘int main()’:
1.c:3:7: warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
  long i = i;

In both cases variable i seems to be 0, although in c++ it could be uninitialized. I actually made such a typo in one of my functions and it was quite hard to find it. What can I do to avoid this? I'd expect at least a warning. Moreover, Clang doesn't give any warning in either case (c or c++). Is there a specific part of the standard that says anything about this behavior?

Edit: Having tried something similar:

$ cat 1.c
int main(void)
{
    int k = k + 0;
    int i = i + 1;
    return 0;
}

The warning (in C) is generated only for "i".

$ gcc -Wall -Wextra 1.c
1.c: In function ‘main’:
1.c:4:6: warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
  int i = i + 1;
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 2
    C is generally OK with trash values, but this is kind-of surprising, especially after having worked with Python for so long (I'm expecting a NameError :)). – Mad Physicist Sep 25 '18 at 13:05
  • 2
    Warnings are a "quality of implementation" issue. The standard tells nothing about them. – Basile Starynkevitch Sep 25 '18 at 13:07
  • I think the best the standard could say is that uninitialized reads (for anything other than unsigned byte types) are undefined in C++, while they might be unspecified in C? I'm not sure about the latter – KABoissonneault Sep 25 '18 at 13:10
  • 4
    @KABoissonneault it's still undefined behaviour in C. Since uninitialized variables in C have garbage data, it's unpredictable as to what will result will be in doing such an operation, or even when said variable is used in the RHS of a statement. – absoluteAquarian Sep 25 '18 at 13:13
  • @Tau I'm not sure it is UB in C, it's `main()` not `main(void)` thus it's technically AFAIK still legal to pass a value to that function (although that should be prototyped). But I'd have to leave that to language lawyers. – Mgetz Sep 25 '18 at 13:14
  • 1
    It compiles because C & C++'s rules are simply not strict enough or good enough. Non-ancient languages do not allow such constructs. – Boann Sep 25 '18 at 13:14
  • 2
    "unpredictable data" is a form of unspecified behavior. Undefined behavior means there's absolutely no restrictions on what the implementation can do – KABoissonneault Sep 25 '18 at 13:15
  • The compiler is not required to post diagnostic messages if the program invokes undefined behavior. As for why gcc behaves inconsistent here, I have no idea. `long i = i++;` gives a warning "‘i’ is used uninitialized in this function" but `long i = i;` does not. This is mighty strange. Smells like some kind of optimization, given that `i = i` should be optimized to `i`. Adding a volatile qualifier doesn't change anything though, as it ought to if optimization was the culprit, so I'm leaning towards a compiler bug. – Lundin Sep 25 '18 at 13:22
  • Notably, if we ignore the value being indeterminate, I think `i = i;` is well-defined behavior as far as sequencing goes, in C++17 and beyond. Perhaps that's why? – Lundin Sep 25 '18 at 13:25
  • 1
    @Lundin the assignment `i = i` is a no-op, but `int i = i;` is not an assignment, it means something completely different. It's very intentional that `int i = i;` doesn't warn, see the GCC docs for `-Winit-self`. Self-initialization is a traditional C idiom for avoiding uninitialized warnings, so GCC doesn't warn by default. – Jonathan Wakely Sep 25 '18 at 16:17
  • 1
    @Mgetz what has `main()` vs `main(void)` got to do with this question? In any case, and empty parameter list on a function definition means it takes no arguments. It only means it can take any number of arguments when `()` is used in a prototype, not in a definition. – Jonathan Wakely Sep 25 '18 at 16:28
  • @JonathanWakely recall your K&R in C without the `void` the number of parameters is _unspecified_ not none. It's not a useful feature... but it is a legacy "Feature" of C. – Mgetz Sep 25 '18 at 17:14
  • @Mgetz it would be undefined to pass a value to `main()` http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_317.htm – Jonathan Wakely Sep 25 '18 at 17:24
  • @JonathanWakely I'm aware of the standard there is a reason I said K&R. Which most compilers still subtly support even in the newest modes... you of course are empowered to fix that ;) – Mgetz Sep 25 '18 at 17:29
  • 1
    @JonathanWakely That's mostly just a formality, since C doesn't have any constructors. Rather, it is _exactly_ the same thing as assignment, as we can read in the C language standard 6.7.9 §11 "the same type constraints and conversions as for simple assignment apply". As for self-initialization, I would hardly call it a "traditional idiom", but bug-prone obfuscation. As we can see from this very question; the reason the OP asked is because gcc caused a bug for not warning about trashy code. And none of it explains why g++ does warn. – Lundin Sep 25 '18 at 20:11
  • 1
    @Mgetz An empty parenthesis is an obsolete feature in C and should not be used. Since C99: "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature." In addition, `int main()` is not one of the forms of a conforming hosted implementation needs to accept, but an implementation-defined form. In C++ however, `()` is fine and often stylistically preferred over `(void)`. – Lundin Sep 25 '18 at 20:19
  • 1
    i don't see this clearly mentioned yet, but `int i = i;` is legal in C and the same as `int i;`, but causes undefined behaviour in C++ (use of indeterminate value) – M.M Sep 25 '18 at 21:45

3 Answers3

18

For GCC compiling C programs, you need to add the compiler flag -Winit-self. (You also need -Wall or -Wuninitialized, see below.) For GCC compiling C++ programs, this flag is implied by -Wall but for C it needs to specified explicitly; it is not part of -Wextra either.

For Clang, the situation is slightly more interesting. In the snippet in the OP, Clang does not produce any diagnostic. However, with the slightly different snippet supplied in the GCC manual below, a diagnostic is provided:

int f() {
  int i = i;
  return i;
}

The difference is that in the above snippet, the (uninitialized) value of i is actually used. Apparently, in the original code Clang detected that the variable was useless and eliminated it as dead code before applying the diagnostic.

In Clang, the diagnostic is triggered by -Wuninitialized, which is enabled by -Wall as in GCC.


Here's an excerpt from the GCC manual:

-Winit-self (C, C++, Objective-C and Objective-C++ only)

Warn about uninitialized variables that are initialized with themselves. Note this option can only be used with the -Wuninitialized option.

For example, GCC warns about i being uninitialized in the following snippet only when -Winit-self has been specified:

        int f()
          {
            int i = i;
            return i;
          }

This warning is enabled by -Wall in C++.

As the excerpt indicates, -Wuninitialized is also required. In both C and C++, -Wall implies -Wuninitialized. However, note that many uninitialized uses will not be detected unless some optimization level is also requested. (That doesn't apply to -Winit-self, as far as I know. It can be detected without optimization.)


Irritatingly, when you unmark a question as a duplicate, the previously-marked duplicates disappear. I unmarked it because none of the duplicates actually answered the question in the body; I also edited the title.

For reference, here are the original duplicates, which may be of interest:

rici
  • 234,347
  • 28
  • 237
  • 341
  • What is the sense of a warning that you have to discover yourself prior to using a compiler option? The boys of gcc sometimes do things somewhat tricky!!! :) – Luis Colorado Sep 27 '18 at 08:51
4

It is basically:

int i;
i = i;

in which i is an uninitialized value.

Boann
  • 48,794
  • 16
  • 117
  • 146
samini
  • 195
  • 11
  • This seems quite probable. However if you do "int i = i + 0;" there's no warning, while gcc warn of uninitialized value when you do "int i = i + 1". Is there a way to circumvent such issues? – notnonuninvisible Sep 25 '18 at 13:18
  • @notnonuninvisible - What issue? Other than this most trivial of cases GCC warns you about using the value of an uninitialized variable. Avoiding the trivial case is easy: people should read what they type. – StoryTeller - Unslander Monica Sep 25 '18 at 13:19
  • @StoryTeller - I get your point, however there's a point we have syntax rules and compilers. Typing is not the only way to get into such a problem - think of e.g. merging code. – notnonuninvisible Sep 25 '18 at 13:23
  • 4
    @notnonuninvisible - C and C++ are not, and never were "safe languages". There is little point in contorting a compiler to protect against every conceivable human error. That's the whole reason UB is a thing in those languages. – StoryTeller - Unslander Monica Sep 25 '18 at 13:25
  • Just a nitpick: initialization and assignment have different rules - they may not always be the same - this case is a simplification. – Sourav Ghosh Sep 25 '18 at 13:25
  • @StoryTeller I also understand what you're saying now. I would like to understand and discuss the underlying reason. Just saying "C is bad" and marking it a duplicate doesnt' help me understand or discuss it. – notnonuninvisible Sep 25 '18 at 13:29
  • 3
    how does this answer the question? There is a warning along the line of "variable i may be used uninitialized" and the compiler could show this warning – 463035818_is_not_an_ai Sep 25 '18 at 13:30
  • 3
    @notnonuninvisible - I didn't say "bad". I said "not safe". It's not one and the same. Lack of safety is also power, because nothing is encumbering you with safety nets you don't need. And I'm sorry, but discussing this philosophical point is off-topic.The only on-topic point of is it allowed is answered in the dupe, which I believe I aptly chose. – StoryTeller - Unslander Monica Sep 25 '18 at 13:33
  • @StoryTeller The dupe drags in c# which I don't care about. I extended my question to include case where it's uninitialized plus a defined value, which then should follow some defined order of evaluation (values and types). So in the end the question is rather: "why the existing warning is not emitted in an obvious case", not "is C safe/bad", etc. – notnonuninvisible Sep 25 '18 at 13:37
  • @notnonuninvisible - "*some defined order of evaluation*" is again an assumption on your part about the nature of C and C++. For some constructs, it doesn't exist. And again, that's beside the point. Making your question broader (again, making it more off-topic) is not helping it. But we digress. If you manage to convince a gold-badge holder in the C or C++ tags to re-open, they will. I'm out. – StoryTeller - Unslander Monica Sep 25 '18 at 13:40
  • @StoryTeller Ok, I get it. It's not the place to discuss programming matters, but policies and policeman. However I still do not know why "for some constructs it doesn't exist" if such a warning was designed for similar cases. I still don't know why I shouldn't assume that warning to be emitted. You only pointed me into thinking that asking questions and discussing compiler warnings is off-topic and I shouldn't ask. That's sad. – notnonuninvisible Sep 25 '18 at 13:48
  • @notnonuninvisible didnt follow all your discussion, though you shouldnt assume any warning to be emitted. If they were that reliable they probably were errors not warnings. Warnings is like the compiler holding your hand to avoid some pitfalls, but you should not rely on it. The standard does not mandate anything about warnings afaik, so if you really want to know on the compiler level why there is no warning in this case you would have to study gcc internals (not the language specifications) – 463035818_is_not_an_ai Sep 25 '18 at 13:52
  • 2
    This discussion is way off topic. It should be continued in a [chat room](https://chat.stackoverflow.com/rooms/180743/notnonuninvisible-discussion). – François Andrieux Sep 25 '18 at 13:54
  • 1
    This answer doesn't appear to answer the question, which is, "How can I get gcc to warn me about 'int i=i;'" – Jeremy Friesner Sep 25 '18 at 14:59
  • @JeremyFriesner: That's partly my fault. I edited the question's title, which was misleading. In the original post, the question "How can I get a warning" was the actual question asked, but it was not at all obvious without reading. See the note at the end of my answer. – rici Sep 25 '18 at 15:04
4

The combination of -Wall -Winit-self seems to add this diagnostic:

$ gcc -Wall      -Winit-self t.c
t.c: In function ‘main’:
t.c:3:10: warning: ‘i’ is used uninitialized in this function [-Wuninitialized]
     long i = i;
          ^
nos
  • 223,662
  • 58
  • 417
  • 506