Get a function to update a passed variable?

Hi everyone,

I've written a little function that accepts a few inputs. It works great...except, the variables I pass to the function don't have their value updated when the function runs.

This is a simplified version of the code:

//
// Application variables
//

unsigned int intWaterTemperature_Counter = 0;

//
// Application setup
//
void setup() {
  // Configure serial output
  Serial.begin(9600);

  // Start the Stream with a carriage return
  Serial.println();
}

// Application loop
void loop(){

  Serial.print(CounterTest(intWaterTemperature_Counter));
  Serial.print(",");
  Serial.println(intWaterTemperature_Counter);
  delay(500);
}

int CounterTest(unsigned int thisSampleCounter){
  thisSampleCounter++;
  return thisSampleCounter;
}

The output I receive is: 1,0 1,0 1,0 1,0

I would expect to receive something more like: 1,1 2,2 3,3 4,4

I know this is really simple, but I don't understand why it doesn't work. I'm from the VB Dot Net programming land!

Any help would be appreciated.

Cheers, Scott :o

What I assume is happening, is that the parameter for the function CounterTest, is being initialised as a new variable every execution. How do I make this function accept an existing variable as a parameter?

Well I'm new to both Arduino and C, but might declareing the variable as static do what you want?

Lefty

Hmm. Thanks for the response Lefty. An interesting idea, I’ll check it out now and post back. Thanks!!!

Scootabug--

The problem with your function is that it is using "call-by-value" parameter passing, which is the method always used in C/C++ unless you explicitly use another method. What that means is that when it is time to call the function CounterTest, the compiler creates a copy of intWaterTemperature_Counter and sends it to the function. CounterTest dutifully increments the copy, prints it out and then destroys it. The original value of intWaterTemperature_Counter is never touched.

You can use call-by-reference to get the results you desire simply by adding an & to the parameter like this:

int CounterTest(unsigned int &thisSampleCounter){
  thisSampleCounter++;
  return thisSampleCounter;
}

Good luck!

Mikal

Alrighty, had some success there. Awesome tip RetroLefty.

I declared some static variables at the beginning, then in the function, updated their value and after the function ends, I update my original variables with the value of the static variables. Works a treat!

Thanks for your help...

@Mikal: Just tried your suggestion, but when I "verify" I get this error:

In function 'void loop()': error: 'CounterTest' was not declared in this scope

Hmm...doesn't make any sense.

Just doing some research on this 'call by function' business, on on this page: http://www.geekinterview.com/question_details/31063 it suggests that you put the ampersand character in front of the variable when calling the function, not in the definition of the function.

So I tried this, but still got an error anyway: In function 'void loop()': error: invalid conversion from 'unsigned int*' to 'unsigned int'

sigh

I'll run with the method I concocted with RetroLefty, until I find a better way.

Sigh. Yes, I realized after my post that a bug in the Arduino "auto-prototyping" code makes my call-by-reference suggestion fail. But if you add your own prototype, it will work. I modified your code slightly, and this works:

//
// Application variables
//

unsigned int intWaterTemperature_Counter = 0;

//
// Application setup
//
void setup() {
  // Configure serial output
  Serial.begin(9600);

  // Start the Stream with a carriage return
  Serial.println();
}

[glow]int CounterTest(unsigned int &thisSampleCounter); // manually add prototype[/glow]

// Application loop
void loop(){

  Serial.print(CounterTest(intWaterTemperature_Counter));
  Serial.print(",");
  Serial.println(intWaterTemperature_Counter);
  delay(500);
}

int CounterTest(unsigned int [glow]&[/glow]thisSampleCounter){
  thisSampleCounter++;
  return thisSampleCounter;
}

(Putting the ampersand in front of the variable is something else entirely. That's creating a pointer value, which is semantically similar to call-by-reference, but uses a different syntax.)

Mikal

Thanks Mikal. Just to be sure I understand what is happening, from your sample code...

I define the variable initially, just as a variable - as opposed to a function. Then after the loop(), I can redefine the same variable, this time as a function. And that is just to get us around the issue with auto-prototyping bug?

At any rate, I've just given it a try and it works perfectly!

Thank you for your help, much appreciated. 8-) I hope JustLefty followed along and picked something up from this too.

Cheers, Scott

Here's a better explanation:

Part 1 You defined a variable. Check. In your original code you defined a function that manipulated a copy of that variable. That didn't do what you wanted, so I suggested modifying the function slightly, adding the "&" to make the function's parameter call-by-reference instead of call-by-value. Call-by-reference parameters are not copies; instead they refer directly to the original value.

Part 2 All C/C++ functions must be prototyped (declared) before they can be used. But Arduino provides an auto-prototyping service that makes it so programmers don't have to create their own prototypes. However, the Arduino service doesn't work correctly when the function uses call-by-reference. That's why we had to manually add our own prototype --

int CounterTest(unsigned int &thisSampleCounter); // manually add prototype

-- to bypass the Arduino problem. It would also have worked to move the whole CounterTest function above loop.

Then after the loop(), I can redefine the same variable, this time as a function.

A better way to say this is that the parameter to CounterTest now refers directly to the variable that was passed in (and is not just a copy of that variable).

A good rule of thumb is to use call-by-value for most parameters, but call-by-reference (&) when the function needs to be able to change the value of the variable being passed in.

Hope that clarifies things a bit!

Mikal

I noticed that the simple way to do what you want is to store the variable that you are returning. Since the function is returning you incremented counter, you need to store it to reflect the change.

int foo = 0;
foo = incfoo(foo);

If you do go the route of pass by reference the function should return void. Hope that helps

Thanks Scott. Great idea when you only need to update the value of one variable.

In my case, I had at least two which meant returning the values as an array (I’m not sure if you can do that or not, I assume you can) or doing it using the method that Mikal suggested.

Sorry for resurrecting an old thread, but mikalhart's comments about the autoprototyping bug were extremely helpful in solving a similar problem I'm having - I just wanted to say "thanks" :)

just as a footnote: if you code the example in this thread using the "explicit C way" of pointer handling as opposed to the "implicit C++ way" the prototyping thing isn't an issue.