0

The following program:

#include <stdlib.h>
#include <stdio.h>

int twice(int a){
    return a * 2;
}

int main () {
    
    int b = twice(b);
    printf("%i\n", b);

    return 0;
}

produces the output 0 without errors.

Why does it work without variable b being initialized? In Python, for example, a similar program would not work, and the Python interpreter will complain b being not defined.

sat63k
  • 333
  • 1
  • 2
  • 13
  • Does this answer your question? [Undefined, unspecified and implementation-defined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – kaylum Feb 26 '21 at 04:25
  • 1
    1. It invokes *undefined behavior*. 2. Python isn't C. – WhozCraig Feb 26 '21 at 04:25
  • 4
    *the Python interpreter will complain*. Most C compilers *will* complain if you turn up the warnings. For example, with `-Wall` in `gcc` will get: `warning: ‘b’ is used uninitialized in this function [-Wuninitialized]` – kaylum Feb 26 '21 at 04:27
  • @kaylum, no, it does not. – sat63k Feb 26 '21 at 04:30
  • Using an unintialised variable is Undefined Behaviour in C. That post describes Undefined Behaviour. – kaylum Feb 26 '21 at 04:31
  • @WhozCraig 1. Please explain 2. I provided that example for comparison. You wouldn't expect me to think that C is Python, would you? – sat63k Feb 26 '21 at 04:32
  • 1
    *Yes it does* exhibit a proper warning (which should always be treated as an error, but you still have to configure as much). [See here](https://godbolt.org/z/rqhdnK). 1. Evaluation of an indeterminate variable id in C invokes *undefined behavior*. That's how the language works. 2. No, I wouldn't, but you seem confused as to why the two *different* languages appear to behave differently at compile-time, where one allows this and the other doesn't. They don't *necessarily* behave the same because, as different languages, they *are* necessarily *not* the same. – WhozCraig Feb 26 '21 at 04:36
  • See [What is undefined behavior and how does it work?](https://software.codidact.com/posts/277486) – Lundin Feb 26 '21 at 14:24

2 Answers2

2

Re:

produces the output 0 without errors

That may be true. It may also generate 42, or format your storage devices while playing maniacal_laughter.mp3 on your sound device :-)

The problem lies with:

int b = twice(b);

since it's functionally the same as:

int b;
b = twice(b);

Note there the use of "functionally", it's not strictly the same since one is an initialisation and one is an assignment, but they're effectively identical for the purposes of this question.

The first line simply "creates" b and sets it to some arbitrary(a) value, the second line uses that value to change b.

This is not an error in C, it's simply undefined behaviour. However, a good compiler should pick this up and give you a warning, though it may need some options to do this such as with gcc -Wall:

prog.c: In function ‘main’:
prog.c:10:13: warning: ‘b’ is used uninitialized in this function [-Wuninitialized]
   10 |     int b = twice(b);
      |             ^~~~~~~~

The reason this can happen in C but not Python is that the former allows you to create an uninitialised variable. In Python, a variable either doesn't exist at all (i.e., the name is not bound to a value) or it exists with some value.


(a) "Arbitrary", in this case, uses the mathematical definition of "undetermined, not assigned a specific value" rather than the definition making it a result of some specific whim by someone. The actual term used in the ISO standard is "indeterminate" for the language lawyers among us (of which I am usually one).

Keep in mind there's nothing requiring an implementation to have a specific value for an uninitialised variable. Though unlikely, it's perfectly plausible that the following code would generate two different numbers:

#include <stdio.h>
int main(void) {
    int x;
    printf("%d %d\n", x, x);
}
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • So the order in which `int b = twice(b)` executes would be (1) declaration, (2) expression to the right of assignment statement, (3) assignment statement. Right? – sat63k Feb 26 '21 at 04:34
  • 1
    @satbekmyrza: yes, though technically the single-line variant is an initialisation rather than an assignment. – paxdiablo Feb 26 '21 at 04:36
  • 1
    thanks! Have a great day! Could you also include that in your answer? – sat63k Feb 26 '21 at 04:37
  • 1
    `b` has indeterminate value, not arbitrary value – M.M Feb 26 '21 at 04:57
  • @M.M: that's what arbitrary *means,* at least in the mathematical sense: undetermined, not assigned a specific value. But I'll clarify it to mention the term used in the standard. – paxdiablo Feb 26 '21 at 08:16
  • @paxdiablo "arbitrary" implies to me that it has some valid value but we can't say what (i.e. simliar to the standard term "unspecified") – M.M Feb 27 '21 at 08:47
2

Assuming that you uss gcc, you should enable the warning flags, especially these ones:

-Werror Make all warnings into errors.

-Wall This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros.

-Wextra This enables some extra warning flags that are not enabled by -Wall.

And if you like ISO C (you should):

-Wpedantic -pedantic Issue all the warnings demanded by strict ISO C and ISO C++; reject all programs that use forbidden extensions, and some other programs that do not follow ISO C and ISO C++

gcc -Werror -Wall -Wextra -Wpedantic test.c

Now lets see if your program compile nicely... ;)

Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81