Functions - very basic help

Hi,

I've written a basic function which works. But I'm not 100% sure why it works, so I'd like to understand why it does.

Goal: Compute the volume of a cube (sides can vary)

/***** GLOBAL VARIABLES *****/

// cube dimensions
const int length = 2;  // length
const int width  = 3;  // width
const int height = 4;   // height

int volume; // cube volume

int i = 0;  // used in for loop for printing


/***** FUNCTIONS *****/

// volume of a cube
int volumeCube(int length, int width, int height)
  {
    int volume;
    volume = length * width * height;
    return volume;    
    
  }
    

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  // uses const variables, and passes data to volumeCube function
  volume = volumeCube(length,width,height);  


  // print cube volume
  for(i; i < 1; i++)
  {
    Serial.print("volume of cube = ");
    Serial.println(volume);
  }
}

Question 1: If remove the line from my global variables section

int volume; // cube volume

then I get the error message when trying to upload:
'volume' was not declared in this scope

That confuses me because it is defined in the function body, which is how I learned to use write it. Having volume declared in both locations seems redundant.

Question 2: If I do have to declare int volume; in my global variables, then I'd prefer to just write the function in a more concise manner, but is this technically correct?:

// volume of a cube
int volumeCube(int length, int width, int height)
  {
    return volume = length * width * height;
  }

That confuses me because it is defined in the function body,

So its scope is that function. ie outside of that function it has no meaning but you are trying to print it in loop

You could do this

void loop()
{
    Serial.print("volume of cube = ");
    Serial.println(volumeCube(length, width, height));
}

which will print the value returned by the function

If you remove the global volume variable that you try to print in loop(), of course the compiler is going to complain.

Having volume declared in both locations seems redundant.

You could assign them different names, if it makes you feel better. Or, you could remove the local variable. Of course, returning a global variable is silly, so you'd want the function to not return anything, but that feels wrong, too.

There is nothing wrong with having local variables with the same name as global variables, as long as you KNOW when the local variable is being used and when the global variable is being used. Which is why real programmers have as few global variables as possible.

but is this technically correct?:

Yes, it is syntactically correct. But if you were a student of mine, I'd lower your grade for doing that.

There is no reason for volume to be global.

There is nothing wrong with having local variables with the same name as global variables, as long as you KNOW when the local variable is being used and when the global variable is being used. Which is why real programmers have as few global variables as possible.

Nothing syntactically wrong.
Not only have as few global as possible, but avoid giving them the same names as local variables.
It’s the programming equivalent of calling both your children “Bob”. There’s no law against it, but it’s obviously going to result in a lot of confusion.

Ok, got it. But while declaring it locally in the function appears to do nothing, I may leave it in there as it appears to fully define the function. However, I'll also need to declare int volume again in the void loop since I want to see the data, which I guess is fine (using your feedback of minimizing global variables).

/***** GLOBAL VARIABLES *****/

// cube dimensions
const int length = 2;  // length
const int width  = 3;  // width
const int height = 4;   // height

int i = 0;  // used in for loop for printing


/***** FUNCTIONS *****/

// volume of a cube
int volumeCube(int length, int width, int height)
  {
    int volume;
    volume = length * width * height;
    return volume;      
  }
   

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  // uses const variables, and passes data to volumeCube function
  int volume = volumeCube(length,width,height); 


  // print cube volume
  for(i; i < 1; i++)
  {
    Serial.print("volume of cube = ");
    Serial.println(volume);
  }
}

So here's a question, why do some programmers use a function prototype at the start of their code, and then write the actual function below the void loop? To me, that just looks like more code than necessary, and possibly adds confusion. Does rewriting the above code serve any benefit as this:

/***** GLOBAL VARIABLES *****/
const int length = 2;  // length
const int width  = 3;  // width
const int height = 4;   // height

int i = 0;  // used in for loop for printing


void setup() 
{
  Serial.begin(9600);
}

// function prototype
int volumeCube(int, int, int);


void loop() 
{
  // uses const variables, and passes data to volumeCube function
  int volume = volumeCube(length,width,height);  


  // print cube volume
  for(i; i < 1; i++)
  {
    Serial.print("volume of cube = ");
    Serial.println(volume);
  }
}


/***** FUNCTIONS *****/
// volume of a cube
int volumeCube(int length, int width, int height)
  {
    int volume;
    volume = length * width * height;
    return volume;  
  }

which I guess is fine

You guess correctly.

why do some programmers use a function prototype at the start of their code, and then write the actual function below the void loop?

Because that is a requirement. The fact that the IDE adds function prototypes to your code, often incorrectly, is something that the Arduino team is proud of, but the rest of us feel is just plain wrong. It teaches poor programming practices.

Because that is a requirement.

But that's not true. The program ran without a prototype. I know that using a prototype is the preferred method (or as you say, required), but my question is WHY is this so, with the considerations I posed, e.g. more code? Essentially, why is more code "better"?

But that's not true.

It is. The fact that you didn't supply one does not mean that the compiler didn't see one.

Essentially, why is more code "better"?

More than what?

In order for the compiler to know that you are calling a function correctly (right number of arguments, right type of arguments, etc.), it MUST know what the signature of the function is. That can happen because the compiler has seen the function before the first call to it, or because there was a function prototype defined before the first call.

Use File + Preferences, and check the boxes for "Show verbose output...". You will then see the command used to invoke the compiler, which will include the path to the file to compile, which is not the same as the path to the sketch. Look at the file that was compiled. You'll be amazed at all the stuff the IDE adds for you, because it assumes that you are too dumb to do that stuff yourself.

One of the things about the IDE that continuously annoys me is that it tries to hide too much stuff. I fully expect, at some point, for the IDE to allow allow you to drag and drop pieces of pre-written code, that will be hidden by pretty pictures. That will be the time when I depart the scene.

Good points, I'll use the conventional style then. Thanks for all of your input in helping me.