Possible bug in function return

I'm implementing an analog button function to read several buttons with only one analog input(inspired from http://tronixstuff.wordpress.com/2011/01/11/tutorial-using-analog-input-for-multiple-buttons/ )
My changes are using 2.2K resistors and buttons from GND to last Resistor(so the values would be button 1 GND, button 2 2.2K button 3 4.4K) as in the picture in the article
So i built my code for 3 buttons and wanted to test a simple thing: "what happens if i press two buttons at the same time", and i found a very weird error, here's the drill:

If i press button 1 since it's GND it overrides any other button, the output is always b=1
if i press button 2 AND 3 together (essentially putting the two 2.2K resistors in parallel yielding a 1.1K resistance), instead of returning 0(since the variable is initialized to 0 according to C)
ir returns a value between 236 and 237, i've tested different values and it turns out it's returning the analogread.
here's the code:

void setup() {
 Serial.begin(9600);
 digitalWrite(A0, HIGH);  //pulllup for pin A0
}

void loop() {
  byte a=readButtons(0);
  Serial.println(a,DEC);
  delay(500);
}
byte readButtons(byte pin) 
{
  byte b;
  int c;
  c=analogRead(pin); // get the analog value
  if (c>1000)
  {
    b=0;
  } // buttons have not been pressed
  else
    if (c<20) // button 1 pressed
    {
      b=1; 
    } 
    else
      if (c>60 && c<80) // button 2 pressed
      {
        b=2;
      }
      else
        if (c>110 && c<130) // button 1 pressed
        {
          b=3;
        }
  return b;
}

what i did was add a little patch that shouldn't be needed, after the last if i add:

       else
       b=0;

and now for any value that's not in the IF tree it returns 0...

but why is this happening, the analogread is using variable C so why is it returning it's value in B when ANSI C specifies that any variable is initialized to 0 automatically and the IF tree is skipping completely thus not touching B?, i smell a bug....

You have not initialized "b" to anything, so if your decision tree doesn't go down a path that touches "b", it never gets set to anything. This leaves it containing stack "garbage" that may or not be the same each time you call readButtons(), but is by no means guarantied to be 0.

ANSI C does not say anything about automatic variables being set to zero.

yes i have initialized b:
byte b;

that inits B to 0
i can't find now the page/article/pdf that says that there's no need to initi a variable to 0 as it's done intrinsicially.

and even leaving b initi aside, the function should NOT return the analogread to it because it's in a totally different variable

Hi,

when I read the code (without the extra else) the var b is undefined when it does not hit an if( ).
Its common practice not to ´trust´ undefined variables. always define then either in the initialization or the else you had.

byte readButtons(byte pin)
{ 
  byte b;   int c;

  c = analogRead(pin);           // get the analog value
  if      (c>1000)         b=0;  // buttons have not been pressed
  else if (c<  20)         b=1;  // button 1 pressed
  else if (c>60 && c<80)   b=2;  // button 2 pressed
  else if (c>110 && c<130) b=3;  // button 1 pressed
 
  return b;
}

grtz.

Eliminateur:
yes i have initialized b:
byte b;

No, you haven't. Try:

byte b = 0;

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

You defined the variable(s). -- This creates a space at a give location in memory. The contents are whatever was there.

You did not initialize the variable(s). This gives the variable a known value.

Some languages do initialize the variables upon creation of the object (Delphi -- Object Pascal for example -- but not when the language was first implemented.)

Rather than debate the issue you could test. However you need to load some large programs and then some smaller programs. The smaller programs would likely then show what was there -- in the RAM space.

But it might take many trials to prove the point as you truly do not know what was there before.

first letme touch the init subject:
Local and global variables can be initialized to a value when they are declared. If no initialization value is given, the variable is initialized to zero.
http://www.newtonlabs.com/ic/ic_5.html
Also:
Unless they have an explicit initializer, all objects with static duration are given implicit initializers—the effect is as if the constant 0 had been assigned to their components. This is in fact widely used—it is an assumption made by most C programs that external objects and internal static objects start with the value zero.

so yes, implicit initialization creates and sets the value to 0 according to C, hence, why is it not respected on Arduino?(i did a quick test of the program with init to 0 and works as intended)

Let's continue on the undefined path and the unknown allocation, it should return "garbage" not the analogread....
the program skipping the IF tree is:

byte b;
int c;
c=analogread(pin)
return b;

and B is returning C.... wth?, i can deal with B returning weird garbage, but to skip to another variable? :fearful:

Look, I gave you a pointer to the standards document that defines what C does. It is not wrong.

Local and global variables

Do not trust that source. Jeez, it does not even distinguish scope from storage class. And it doesn't say anything about register storage class. It is simply wrong on this point.

all objects with static duration

Your variable is not static, it is automatic.

All those who have responded to you, the actual behaviour of your program, and the C standard all tell you that your "b" is uninitialized and has an undefined value.

As to why it is seems to contain the analog read value, this one function definition does not give enough information to guess what other functions might have written on that part of the stack before this one was called. The value is likely something left there by the Serial.println() or the delay(). And that could easily have logic somewhere that copies data amongst its own automatic variables, that winds up moving the old value of your "c" onto what will become the initialized "b". The AVR-GCC tends to put variables in registers a lot, and the value of 'c' could just be what is left in that register from some other time. These are just guesses, and without dredging through the assembler output, it's tough to say for sure.

All that I can say is that it is absolutely no surprise at all for this kind of thing to happen. Uninitialized automatics exhibit this type of behaviour, and it is a common source of bugs and debugging challenges.

good to know,
i'll take the precaution of explicitly initializing automatic vars inside functions when there's a chance of them passing "untouched"(or i could init all of them and be done with it...)

Incidentally,

Eliminateur:
http://www.newtonlabs.com/ic/ic_5.html

This is documentation for a C-like language with a number of extra features such as type safety and array bounds checking and stuff. This is NOT by any means what AVR-GCC gives you, and this documentation source is flat-out not going to put you on the right path.

or i could init all of them and be done with it...

A much better idea than relying on the compiler to do it for you. How much effort is it, really, to add " = 0" to all declarations? Do it, and you'd KNOW what value the variable has at all times.