Go Down

Topic: uint32_t weirdness (Read 3990 times) previous topic - next topic

sterretje

Quote
What do we do from Here?
Based on my understanding of the thread (uninitialised variable a causing the issue), just initialise a. Or do I see that too simplistic?

Interesting problem though.

Note:
I loaded the code in VS2012 C++ and it throws an error on the uninitialised variable a.
If you understand an example, use it.
If you don't understand an example, don't use it.

Electronics engineer by trade, software engineer by profession. Trying to get back into electronics after 15 years absence.

el_supremo

Quote
Based on my understanding of the thread (uninitialised variable a causing the issue), just initialise a. Or do I see that too simplistic?
I agree.

As I said back in message #5
Quote
A bug is a bug. Fix it
The fact that the local variable 'a' just happens to be zero on entry to the function is simple good luck and good luck is not the basis of good programming.
I am more or less certain that all the other weirdness is caused in whole or in part by that uninitialized variable and the compiler's subsequent optimization which removed some code from the loop. Trying to figure out what is happening to something when you don't fix an obvious bug may be of academic interest (figuring out how and why the optimizer removed the code), but in terms of getting the program working it is a futile waste of time.

Pete
Don't send me technical questions via Private Message.

chucktodd

I agree.

As I said back in message #5 The fact that the local variable 'a' just happens to be zero on entry to the function is simple good luck and good luck is not the basis of good programming.
I am more or less certain that all the other weirdness is caused in whole or in part by that uninitialized variable and the compiler's subsequent optimization which removed some code from the loop. Trying to figure out what is happening to something when you don't fix an obvious bug may be of academic interest (figuring out how and why the optimizer removed the code), but in terms of getting the program working it is a futile waste of time.

Pete
I agree that the 'solution' to this explicit compilation error is to initialize 'a' to zero,  My question about where to go from here is about reporting the incorrect compiler result.

If this uninitialized variable creates a condition whereby the compiler then creates incorrect code, that is the problem.

I would accept incorrect operation of the function because of the uninitialized index variable.  If the function was generating random values because the index was pointing into the hinder lands that would be acceptable.

I agree I should have initialized the index variable, but the compiled code did not match the source in functionality. By definition that is what the compiler must do.  

Does this mean I must assume that all uninitialized variable will/can cause the compiler to generate incorrect code?

Should I always provide an initialization statement during any variable declaration?

In the following exampe:
Code: [Select]

uint16_t a=0;
uint16_t b;
uint32_t c=7;
while(b=c+a){}


Is there an expectation that the compiler will generate invalid code because I chose not to initialize b to an ignored value?

Or, is this case one that may/might cause the compiler to fail?
Code: [Select]

uint16_t a;
uint16_t b;
uint32_t c=7;
while(b=c+a){}

 
I am not really comfortable taking the inshallah mindset when it comes to a supposed deterministic process.

Chuck.
Currently built mega http server, Now converting it to ESP32.

KeithRB

This code results in undefined behavior:

Googling this found this on StackOverflow:
Quote
Yes this behavior is undefined but for different reasons than most people are aware of.

First, using an unitialized value is by itself not undefined behavior, but the value is simply indeterminate. Accessing this then is UB if the value happens to be a trap representation for the type. Unsigned types have rarely trap representations, so you would be relatively safe on that side.

What makes the behavior undefined is an additional property of your variable, namely that it "could have been declared with register" that is its address is never taken. Such variables are treated specially because there are architectures that have real CPU registers that have a sort of extra state that is "uninitialized" and that doesn't correspond to a value in the type domain.

Edit: The relevant phrase of the standard is 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.

And to make it clearer, the following code is legal under all circumstances:
unsigned char a, b;
memcpy(&a, &b, 1);
a -= a;

http://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior-in-c

jremington

#34
Sep 08, 2016, 05:58 pm Last Edit: Sep 08, 2016, 06:00 pm by jremington
Quote
a supposed deterministic process
Sorry, you've lost my sympathy.

There are many, many cases where the C language definitions are ambiguous and the behavior is undefined.
For example, the statement
Code: [Select]
i = i++;is one of those. Using an uninitialized value as an index is another.

The compiler is simply not smart enough to detect when you are doing one of those things, so you should avoid doing them, rather than blame the compiler for failure to produce valid code.

KeithRB

jremington:
Except in this case the behavior should probably be indeterminate, not undefined. taking the address of a would be enough to guarantee that it is just a bug, not undefined behavior.

mrburnette

#36
Sep 08, 2016, 06:04 pm Last Edit: Sep 08, 2016, 06:05 pm by mrburnette
This code results in undefined behavior:

Googling this found this on StackOverflow:
http://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior-in-c
+1  Good find...   ;)

Ray

jremington

Quote
Except in this case the behavior should probably be indeterminate, not undefined.
I fail to see the difference.

mrburnette

I fail to see the difference.
A bad analogy probably ....
If you have ever worked with Microsoft security rules, the rules are not True/False but are True/False/Never set. 

Ray

chucktodd

So,

 Based on the recommendation I have seen on this message stream, it is a known fact that uninitialized variables will cause the compiler to generate invalid code unrelated to the uninitialized variable.

That is good to know, so every variable must be initialized to a known value even if that value is never used.

I accept the recommendations on improving my coding practices.

Chuck.
Currently built mega http server, Now converting it to ESP32.

KeithRB

As chucktodd pointed out, using 'a' inited to garbage is a bug, but when the code is compiled and run the computer should do what you would expect if you run the code with pencil and paper.

In this case, because it is undefined, you got a change to 'b' which is not something you would see if you ran the code with pencil and paper. But at least monkeys did not come out his nose.

KeithRB

#41
Sep 08, 2016, 06:31 pm Last Edit: Sep 08, 2016, 06:36 pm by KeithRB
You don't have to initialize it, just use put something useful in it.

Code: [Select]


uint16_t a;
uint 16_t b=42;
uint_16_t c = 99;

// stuff

a = b+c;



i.e., you don't need to initilialize it if you are going to assign to it later. (assuming you don't use the unitialized value!)

el_supremo

Quote
If this uninitialized variable creates a condition whereby the compiler then creates incorrect code, that is the problem.
That is not a problem. You are still missing the whole point that the bug in your code caused (or perhaps a better word is allowed) the compiler to generate the code that it did. The fact that 'a' happened to have a value of zero when you tested it is purely accidental. Once you use that function in a large program it will fail unless 'a' is initialized to zero.
But the compiler did not create incorrect code. Your bug allowed it to optimize away some of the code in the loop because you hadn't initialized 'a'. In effect the compiler was saying "you've put garbage in, I can make this code produce garbage even faster by taking some of your code out".

If you had initialized 'a' to zero and you still got those weird results, then maybe it would indicate a bug in the compiler.

Pete
Don't send me technical questions via Private Message.

bperrybap

So,

 Based on the recommendation I have seen on this message stream, it is a known fact that uninitialized variables will cause the compiler to generate invalid code unrelated to the uninitialized variable.

That is good to know, so every variable must be initialized to a known value even if that value is never used.

I accept the recommendations on improving my coding practices.

Chuck.

You could go bring in up over on AVRfreaks where the avg-gcc guys hang out; however, what I have found over the past 25 years of using gcc and other C compilers is that compiler developers will often hide behind things such as "implementation dependent", "undefined", or "missed optimization" quite often to declare the existing compiler behavior or generated code as compliant.
And while they are technically correct, there are often cases where the compiler could definitely do something better and still be compliant.
I've run into several of these types of issues with avr-gcc over the years and never got much interest in fixing the code to be better.
In this case, the issue would have shown up if warnings were enabled.
I'd recommend turning them on. Depending on the version of IDE you may be able to turn them on in the IDE or you may have to go in and modify the actual platform.txt file as the arduino boys have completely disabled warnings in some versions of the IDE.
It is sad that the core code and bundled libraries from Arduino still have warnings in them. So if you turn on the warnings, you can expect to see some warnings.

Some of the worst issues are truly silent like when using certain for/while  loop variables and expecting a variable to roll over from say 0xff to 0 in an 8 bt value.
You cannot depend on this to work correctly with avr-gcc.
Depending on how you declare the variable, and your specific code, it may or may not work as the compiler ends up generating 16 values for loops and it will optimize out the check since according to the C standard, how to handle overflows is implementation dependent.
So the compiler developer will say you should not expect rollover to work.
What I've seen is that the compiler generates a 16 bit loop variable, then optimizes out some of it later since it was declared as a unit8_t. However, that can affect what happens when increments and a roll-over occurs and in some cases your loop code can fail to work as expected depending on how you do your increments and your tests on the incremented value.

The really sucky part of that is that depending on how you declare the variable the behavior is different.
My argument was the behavior is "implementation dependent" not "undefined" so therefore the implementation is free to define it any way it wants but it should do it the same way regardless of how the variable is declared.
They didn't agree, I lost, and nothing changed.

In your case if you go bring in up over AVRfreaks, I can predict that they will come back pretty hard and say you shouldn't expect it work when using an initialized variable, and it works when it is initialized, so "there is no compiler issue. It was a bug in your code."
To some extent there is some truth in that since optimization is an extremely complex process, and the optimizer assumes that the code doesn't have bugs in it or expect things that are not required.
This allows the optimizer to remove away as much code as possible, and sometimes, needed code that normally would be needed had the original code been correct, will be ripped away. But like in this case, the original code wasn't quite right so it will very hard to point to anything that the compiler actually did incorrectly.
Could it have done things differently and made it work, probably, but that might affect the optimizer in some negative way in some other situations.


--- bill


PaulMurrayCbr

If this uninitialized variable creates a condition whereby the compiler then creates incorrect code, that is the problem.
The code is not incorrect - it does exactly what you told it to do.

Quote
I agree I should have initialized the index variable, but the compiled code did not match the source in functionality. By definition that is what the compiler must do.
The generated code code *did* match the source in functionality. The value of uninitialised variables with auto storage class is undefined. This is well-known, well-understood behaviour.

Quote
Does this mean I must assume that all uninitialized variable will/can cause the compiler to generate incorrect code?
Again, the compiler did not generate incorrect code. In the C++ language, uninitialised auto variables with primitive type have undefined values. That's what the language definition says. If you don't like C++, use Java, or something else that explicitly keeps you from using unititialised variables.

Quote
Should I always provide an initialization statement during any variable declaration?
No, you should understand what you are doing rather than complaining that the compiler cannot read your mind. You may as well complain that your car permitted you to drive it into a tree.

Quote
Based on the recommendation I have seen on this message stream, it is a known fact that uninitialized variables will cause the compiler to generate invalid code unrelated to the uninitialized variable.
The generated code is not invalid - it simply doesn't do what you would like it to do or think it should do. These are not the same thing.

Quote
That is good to know, so every variable must be initialized to a known value even if that value is never used.
Wait - what? How on earth did you manage to get that idea? Why are you even defining variables that you never use, anyway?

Anyway: why on earth post long screeds complaining about the C++ compiler here? No-one is going to "fix" the C++ compiler because someone on the arduino boards doesn't get it. The reason the compiler works how it does is that initialising these variables can be unnecessary code, because a programmer may know things that a compiler cannot know. C errs on the side of generating small, fast code.

Consider this:

Code: [Select]

extern boolean foo();

void bar(char *p) {
  int baz;

  if(foo()) baz = 12;

  Serial.print(p[baz]);
}


If foo() returns false, then baz will contain garbage and p[baz] will reference some random part of memory, which is bad. But you the programmer may know something that the compiler does not - that for some reason or another foo(), when it is invoked in circumstances when bar() is being used, will never return false.

Of course, this is bad code, but that doesn't change the fact that the compiler would be generating unnecessary code if it cleared this variable.

Now, Java forbids you to write code like this: it analyses the flow of control and determines that baz might be uninitialised. Why does C++ not do this? Because it's impossible, and the thing that makes it impossible is the goto statement (which Java) does not have.
http://paulmurraycbr.github.io/ArduinoTheOOWay.html

Go Up