Rewriting code is the worst

Suppose we were writing a small 2D physics-based game. We might need to know the slope of a surface, to determine if a player can stand on that surface. We’ve written this code before:

float x1, y1, x2, y2;
 
//We would scan in values here!
 
float slope = (y2 - y1) / (x2 - x1);

OK, so the code above works… but what if we want to calculate the slope of a different line?

float x1, y1, x2, y2;
float x3, y3, x4, y4;
 
//We would scan in values here!
 
float slopeA = (y2 - y1) / (x2 - x1);
float slopeB = (y4 - y3) / (x4 - x3);

See how we’re duplicating code here? It may not seem like a big deal now, but having to remember and rewrite the slope formula all over the place is annoying! It’s also error-prone… what if we accidentally put a variable in the wrong place in the equation? OK, so… what do we do to solve this problem?

What if we could somehow refer to that chunk of code we’re using for slope, without having to rewrite it everywhere? Maybe it’d look something like the following:

float slopeA = SlopeOfLine(x1, y1, x2, y2);
float slopeB = SlopeOfLine(x3, y3, x4, y4);

We could use “SlopeOfLine” to refer to our chunk of code from earlier. Whenever we have need of it, we could use that name, and pass things TO it, to WORK on! (remember… instructions and data). Well… sure, it’d be neat if we could do something like this:

//When we use THIS NAME...
SlopeOfLine(x1, y1, x2, y2);
 
//We're actually referring to THIS CODE
{
	(y2 - y1) / (x2 - x1)
}

But to name a chunk of code and reuse it like that, we’d need to learn about functions! Let’s shelve our example for now, and come back to it in a bit.

Functions!

Functions are present in basically every programming language. They’re a way to wrap any number of code expressions together in a little package, so that they can be used repeatedly in different contexts.

Every program is actually just a bunch of functions! When our program starts, it ALWAYS starts in the main function:

int main()
{
	printf("Hello World!");
}

We’ve used a number of different functions already. Remember how we discussed the C standard library? So far we’ve mostly called the things below “utilities” or some other word… but they’re actually functions!

printf("Hello World!");
 
int x;
scanf("%d", &x);

Functions usually take some input and produce some output (though they aren’t required to do either).

To use a function (which is CALLING or INVOKING a function), we write out the name of the function, and then put parentheses, and a semicolon. If the function takes input, the input goes in the parentheses:

printf("This stuff is being passed to a function!");

So this is all well and good… but we haven’t yet talked about how to write a function in code. We’ve seen some functions we’ve used before, and we now know how to call a function if we ever made one… so how do we MAKE them?

Writing our own functions

If we wanted to write a function of our own, how would we do that? Let’s look at a simple example:

int TimesTwo(int myNumber)
{
	int dubNumber = myNumber * 2;
	return dubNumber;
}

The code above is something we haven’t seen before. It’s called a ==function declaration==. OK, so what’s actually going on here? There are a number of parts we need to look at:

Function Name: TimesTwo

The name of the function is “TimesTwo”. This is how we’d refer to this chunk of code, if we wanted to use it in some other part of our program.

Return type: int

When this function is done executing code, it gives something back to us! And that something is… an integer!

Argument/Parameter: int myNumber

This function needs a value to do its job (data for the instructions). That value is an integer, and inside the function we refer to it as myNumber. This function has a single argument, but we could write a function with two, three… or zero arguments! For a function definition, the argument must have a name and a type, just like a newly created variable.

You’ll notice I used two different words above: argument and parameters. There is a difference between what these two things are, but I don’t think this distinction is important for our purposes. For now, just use the word argument, but know that if you see the term parameter, they’re basically interchangeable.

Function Body

The body of the function is code that multiplies our number by 2, and then RETURNS that value. Return is a special C/C++ keyword that is used to end a function’s execution, and give a value back to the calling code. When we return dubNumber, we’re copying the value from dubNumber and passing it back to the portion of code that called the function originally. And notice that dubNumber is an integer, which matches the return type of the function.

Tying it all together

So how would we write out our slope function?

Write out the function definition for the function Slope

Void

You might have seen this keyword before… what is it, and what does it mean? No, it has nothing to do with the Shadow Realm, Risk of Rain, or “Insert other dumb media here”.

It is just a way of specifying that a function won’t return anything.

So, we could do something like:

void EatBoolean(bool goodFood)
{
	if(goodFood)
	{
		printf("The food was yummy!");
	}
	else
	{
		printf("The food was yucky!");
	}
}
 
int main()
{
	EatBoolean(true);
	EatBoolean(false);
 
	bool myFood = true;
	EatBoolean(myFood);
}

We’re calling the EatBoolean function, but it doesn’t return anything. It does stuff on its own, but it never gives us anything back. Additionally, our function doesn’t have a return statement… because it doesn’t need to return anything! Whether your functions should return something or not is a design decision you’ll have to make as you craft your programs.

Note that you can’t use void to refer to a variable’s type. It is only used for function return types.

int x; //Fine
 
float y; //Yup
 
void z; //THIS IS NOT LEGAL CODE

Yo Dawg, we heard you like functions…

Functions can actually call OTHER functions entirely! And of course, we’ve also been doing this all along! Looking back at the example above, main is a function, and EatBoolean is a function… and we’re calling EatBoolean from inside main. But we can go deeper with this:

	float SlopeOfLine(float x1, float y1, float x2, float y2)
	{
		return (y2 - y1) / (x2 - x1);
	}
 
	bool AreCollinear(float x1, float y1, float x2, float y2, float x3, float y3)
	{
		float slopeA = SlopeOfLine(x1, y1, x2, y2);
		float slopeB = SlopeOfLine(x1, y1, x3, y3);
	
		return (slopeA == slopeB);
	}
    
    int main()
    {
		float px1, py1, px2, py2, px3, py3;
		
		//Assume I scanned in the values from the user...
		
		bool answer = AreCollinear(px1, py1, px2, py2, px3, py3);
    }

We can follow the flow of code, through these function calls, to see what is happening.

  1. Our program starts with the main function, which creates some float variables, and then calls AreCollinear. AreCollinear uses the arguments to then call SlopeOfLine.

  2. SlopeOfLine calculates the slope of a line. The slope is returned from this function as a float.

  3. Back in the AreCollinear function, the SlopeOfLine function is called a second time.

  4. SlopeOfLine calculates the slope of another line. The slope is returned from this function as a float.

  5. Back in the AreCollinear function, we compare the slopes, and return a boolean value from AreCollinear, to tell us whether or not the three points are collinear.

  6. Finally, this boolean value is stored in the answer variable in our main function.

Examples

[! Question] Function Signature

Given the following function:

float Average(float a, float b)

Determine the following:

Name: ??? Return Type: ??? Arguments: ???

[! Question] Function Signature

Given the following function:

bool DoAThing(char characterCode, int number)

Determine the following:

Name: ??? Return Type: ??? Arguments: ???

[! Answer]-

Name: DoAThing Return Type: bool Arguments: characterCode(which is a char), number (which is an int)