Hello, and welcome back.
In this lecture, I want to talk about defining functions in c.
Defining a function.
We know the importance of functions.
We understand the advantages.
We will want to use functions in our programs.
So when you create a pro function in c, you have to specify the
function header as the first line of the definition of the function.
We're going to talk about defining a function, and we're also going to
talk about implementing a function.
Implementing a function is just problem solving,
it's just regular c code.
Defining a function is saying that the function exists.
So when you define a function, the first thing you do is you have that
function header and then you follow that with a curly brace that starts
the block code for the function.
So it's just like when you use a curly brace in an if statement,
just like when you use a curly brace in a for loop, except the curly
brace is required for functions.
There's no eliminating it.
And it always signifies the start of the code for that function.
Anything in between the starting and ending curly brace is
the code for the function.
It's where you're going to implement the task.
So in between the braces is called the function body and
the first line of the function is called the function header.
The function header defines the name of the function, the
parameters of the function.
We're going to talk about parameters and arguments in detail.
But right now, you just need to know that there's
something called parameters.
And the parameter is going to specify the number and the types of the values
how you can pass data, parameters have to do with passing data.
The type for the value that the function returns.
We mentioned that functions we can return data.
So in that function header, you're going to have the name
of the function, the data that you can take can be accepted
by the function and the data that's returned by the function.
We're not going to focus right now on the data that can be passed or the
data returned, we're just going to talk about creating a simple function.
The function body contains just regular code, and that
code gets executed when you call or invoke the function.
And the function body itself will have access to any values
that are passed as arguments.
So the data that you pass to it, you can utilize that data
inside the body of the function.
What it looks like is this, the function header.
Function header has the first word.
And function header is a keyword representing the type
of data that's going to be returned from the function.
The next keyword there is the name of the function.
And then you have a left parenthesis and then you have parameters separated
by commas, that will be the data that's passed to the function.
And then you have that right parentheses to
end the function header.
Then you have the curly brace that signifies the start of
the function implementation.
And you have any number of statements to do a task.
Like I said, you want to have your functions be small.
They want to be cohesive.
They don't want to be all over the place.
They want to have a single focus, do something specific.
Like I said if you have a function that gets input, it reads input.
It doesn't print out output.
It doesn't do any sorting or doesn't do anything else.
If you have a function named sort, it sorts the data.
It doesn't get input.
Functions need to be cohesive and small.
It's much easier to debug those functions, so make them cohesive.
And then you have that right curly brace, which ends the
function implementation.
That is what a function looks like in c.
Now let's focus on the function names, and we'll talk about the
parameters and return types later.
The first line of the function again definition tells the compiler in
order from left to right three things about the function, as I mentioned.
The type of the value returns, the name of the function
and then any data it takes.
This is just to summarize, that's important.
As far as function names, you want to choose meaningful function names.
It's just as important as choosing meaningful variable names.
It greatly affects the program's readability, so do
not name functions x or y.
Name the function based on what it's going to do.
If it's adding two numbers, name it add.
If it's subtracting two numbers, name it subtract.
In this example, here's our simple function.
Don't worry about the void, just worry about the name here.
We have a function named print message.
And then we see that curly brace which signifies the starting
of the function, and then we have some implementation.
And this implementation is doing nothing more than
displaying a string, a text.
And then we have that right curly brace.
So this is a very simple function it doesn't do much, it displays output.
In this example for the print message, the first line of the
print message tells the compiler that the function returns no value.
That's the keyword void.
Again, don't need to focus on that, but that's what that's doing.
The next is the name.
And then after the name is arguments, they're called parameters.
And we use the keyword void again if we're not passing any data.
The statement in the function body can be absent, but the
braces must always be present.
So you don't have to actually have any implementation, but you have to have
the braces for a function to be there.
If there are no statements in the body of a function, the
return type has to be void.
And again, we'll focus on this later and the function will not do anything
because you have no implementation.
If you define a function with an empty body, it can still
be useful even though it's not accomplishing anything.
You usually do that during the testing phase of a very large program.
And what this allows you to do is you can run the program with
only certain functions actually doing something, helps you test
better, helps you isolate errors.
You could even have implementation in the test in the function
body but just comment it out.
And then you can do this divide and conquer approach.
As you test more and more and more, you can then start putting
implementation in the function bodies.
It's a step-by-step process of working.
You are constantly working in small chunks, adding code, testing that
code and then moving on to the next step and then eventually the whole
implementation will be completed and all functions will be implemented.
So there is a use case for having a function body that
has no code or commented out.
Naming functions.
The name of a function can be any legal name.
Just like when you're naming a variable, you
cannot use reserved words.
So these are keywords in the language such as enter doubles
because they represent data types.
Sizeof is an operator.
You want to be able to use it for or if.
You could also not have two functions with the same name.
And this is also referred to as function overloading, kind of.
But you can't have two functions with the same name, overloading
is not supported in c.
Lastly, you can have the same name as a function that's in
the standard library function.
Technically, if you don't include that library include header file,
you might be able to have it.
But if you ever wanted to actually use that library function, the compiler
would complain and say you have two functions with the same name.
So don't name your functions the same as standard library functions.
A legal name is the same as a variable, a sequence of
letters and digits to start off.
The first character has to be a letter and the letter can be an underscore.
You can't start off functions with numbers or any kind
of weird characters.
The name that you choose should be meaningful and relevant
to what the function does.
I said this already, I'm repeating it for emphasis, meaningful
names are very important.
You will often define function names and variable names too that
consist of more than one word.
So now I want to talk a little bit about just coding style.
You've probably noticed when I've written my code that I use camel case.
There are three different ways you can write the names of
your variables and functions.
You can separate each of the words in a function name with an underlined
character, it's one approach.
You can capitalize the first letter of each word or you can capitalize
words after the first word, and you still have strum together.
Remember, you can't have spaces in between.
This is just an approach for naming because it's important
that you follow some convention so that it's easier to read.
Just having a long word with five different words.
So if you had a function that says check for valid input, you wouldn't
want to have those all lowercase.
It'd be really hard to read and identify where the words were.
So camel case is a way to identify where the words are in
many words and it's easy to see what that function is doing.
An underscore is another way to separate words.
And capitalizing the first letter of each word is another way to separate.
It's important to be able to separate the words in a function
to make it more readable.
I usually use camel case.
And when you use one of these conventions, you want to use it
for everything, you want to use it for naming variables, you want
to use it for your function names.
Usually, you just follow this common coding convention, it
makes things more readable.
Function prototypes.
We talked about defining a function.
What about declaring a function.
You may say why do you need to declare a function, aren't you
declaring it when you define it.
But there's also a concept of declaring a function.
And it's necessary if you want to use the function before it's
implemented, before it's defined.
So a function prototype is a statement that defines a function.
It tells the compiler essentially all the details of the function.
It defines its name, return type and the type of each of its parameters.
So it looks like a function header.
It provides all the external specifications of the function,
mainly you've also probably heard the term signature, function
signature that's what it is.
It's specifying the data returned, the data that it takes and
everything about the function itself.
You want to write a function prototype the same way as you write the function
header, the only difference is that you put a semicolon at the end.
And this is just declaring the function.
This is saying the function exists.
A function prototype enables the compiler to generate the
appropriate instructions at each point where you call the function.
It also checks that you're using the function correctly in each invocation.
The reason that you provide function headers is so that you can invoke
the function without a warning or a function declaration without it being
implemented, so it's really just the order of your code and I'll give
you an example of this in a second.
When you include a standard header file in a program, the header
file adds the function prototype.
Function prototypes usually go in header files if you want to reuse
them in many different source files.
If you're only using them in a single source file, you can just put them
at the top in the in the source file.
But because we're using the printf function which can be used in many
different source files, that header function will be defined in the
header or that function declaration will be defined in the header.
And in this case, stdio.h.
So they usually appear in source files if only that source
file is using that function.
Otherwise, you put them in a header file if they're being
reused by many source files.
It allows any of the functions in the file to call any of those functions
that are defined regardless of where you have placed the implementation.
If you place the implementation of a function, after you call it,
c is going to complain and say that it doesn't know about it.
Because when you compile, you're compiling line by line.
If you place the declaration at the top, then you can invoke the
function before it's defined, before it's implemented.
Parameter names do not have to be the same used in the function definition.
You can have different names.
When I say names, those are the identifiers of the data.
You can change the name of the parameters in the function prototype.
You still have to have the same data types, but you can change the names.
It's always good practice to include function declarations
for all the functions in your program at the top of the file.
It keeps your program more consistent to the design and it prevents
any errors occurring at any stage if you ever want to call that
function before implementing it.
Here's an example of some function prototypes.
At the very top of the file, we have our includes.
And then we have some functions that we've declared,
average, sum and get data.
You notice it looks just like a function header.
It has the return type, has the parameters that are
defined in the function, but it has a semicolon at the end.
There's no implementation for these functions.
You're just telling the compiler this is the function exist
and this is how it's defined.
What this allows you to do is now in your main function, you could
call the average, you could call the sum, you could call to get data.
And the actual implementations can be below where you're calling them.
If you didn't have the function headers here, when you tried to
call them in the main, if they were defined below the main routine,
you would have errors or warnings.
So let's take a look at that in code blocks.
Here we have our usual main routine.
Now let's say that we want to create a function or we
want to invoke a function.
We're going to invoke a function called add and it's not
going to take any parameters.
We're calling it in our main function on line 7.
We're going to implement it on line 10.
We're going to say void add and we're not even going to do anything.
We're just going to keep it empty.
When we go to compile this, what you're going to notice is this
compiler just gives a warning.
It says add has an implicit declaration.
You can get rid of that warning by using a function declaration.
You can declare the function up top basically telling the
compiler that you're going to use it somewhere, but you don't
know where the implementation is.
So what you can do is you can say void add right up here
and just add the semicolon.
Now it will get rid of that warning and some compiles will
actually give you an error.
Now when I compile it, I have no warnings and no errors.
So that's what a function declaration does.
It allows you to put the function anywhere.
Now if you didn't have the function declaration at the top and you had
the implementation above where you were calling it, then you also won't
see the error because the compiler is going to know about the function
before it actually tries to invoke it.
So if we build this, you're also going to see that there's no error.
Now I mentioned that you usually want to declare all your functions
at the top just for good practice and I think you do need to do that.
So keep doing that.
But if you do get out of that habit, you either have to declare
your functions before you call them or you have to provide function
prototypes because you do not want warnings or errors in your program.
This is how you can define a function, and this is how you
can use function prototypes.
In the next lecture, we're going to
talk about parameters.
How you can pass data to a function.
Thank you.