Functions

 

This web page is about functions - without functions, a C programme wouldn`t really achieve very much.

 

Functions

Functions are the blocks of code that do things - every C programme must have at least one function, and that must be called "main", and it is always the function that is run first.

The code relating to each function must be enclosed in a pair of curly braces, and so a minimilist C programme would look something like -


        #include <stdio.h>

        main()
        {
        c code stuff;
        more code;
        return(0);
        }

The C code in a function usually is written in a series of statements, each statement on a seperate line, and each statement must end with a semi-colon.

Function names can contain the underscore character, lower case letters and numbers, but not hyphens or spaces.

 

Nesting functions

Nesting functions is just a way of describing the fact that a function can be used inside another function - the first function calls the second function, the processor saves the current instruction address, jumps to and executes the second function, then returns to the saved instruction address.

 

Declarations and definitions

Functions must either be defined or declared before they are called in the programme, otherwise the script may not compile, or will generate error messages.

The definition or declaration of a function must take place outside of any function, within the outer layer of the programme.

In a definition of a function, the function name is given, and this is followed by the code which the function uses to achieve the required result - the code is wrapped in a pair of curly brackets, or braces.

Going back to the original minimilist script, this is the definition of the main function.


        main()               // the name of the function

        {                    // open the curly brackets

        c code stuff;        // the start of the code
        more code;

        return(0);           // specifies the information
                             // returned to the environment
                             // that called the function

        }                    // close the curly brackets

So now the compiler knows

Now if the main function wants to make use of sub-functions, these also have to be defined, before the main function requires to use them. However there is a choice - they don`t have to be defined right away, they can be declared - which is a way of telling the compiler that they are coming, and they will be defined later.

So if the sub-function is defined in full, the source code script would look something like -


        #include <stdio.h>

        sub_function()         // start of definition of sub-function
        {
        c code stuff;
        return(0);
        }

        int main()             // start of definition of main function
        {
        c code stuff;
        more code;
        return(0);
        }

Or else it can be declared before it is called, and then the definition for it is provided later in the programme. The declaration tells the compiler that the function exists, and the declaration must end with a semi-colon.


        #include <stdio.h>

        sub_function();       /* declaration of sub-function has
                               * no curly brackets and no code -
                               * just the name of the function
                               * - and ends with a semicolon    */

        int main()            // definition of main function
        {
        c code stuff;
        more code;
        return(0);
        }

        sub_function()        // start of definition of sub-function 
        {
        c code stuff;
        return(0);
        }

The process of declaring the function before it is defined is known as prototyping.

 

Types of function

There are various kinds of functions, and they are characterised by the type of value ( or return ) that the function produces, when has it has finished its run. Some of them are the same as data types in C, but there are other possibilities as well. Some of the data types types of function are :-

For type void, the function doesn`t return anything, so there is no requirement to have the statement about the return value at the end of a definition of a void function.

The type of a function should be given, before the name of the function, in both definitions and declarations.

The "main" function MUST be of type "int", and it must return a value. So a more accurate source code script could be


        #include <stdio.h>

        float sub_function();      // sub-function is declared as float type

        int main()                 // main function is defined as int type 
        {
        c code stuff;
        more code;
        return(0);
        }

        float sub_function()       // sub-function is defined as float type
        {
        c code stuff;
        return(0);
        }

 

Function parameters

The parenthesis after the function name can contain a list of the parameters that are going to be passed into the function by whatever is calling the function - maybe it is the operating system, maybe it is another function.

Some texts don`t suggest this, others do - for each parameter there must be a definition of the type of the parameter. gcc requires it, or the compilation fails.

So a declaration of a function could look something like


        int sub_function(int nn);      /* sub-function is declared as int type
                                        * with a parameter nn, which is an
                                        * int type                         */

        int main()                     /* main function is defined as int type 
                                        * with no parameters               */
        {
        .
        .
        . etc

Something that seems a bit weird is that when a sub-function is called, and a variable is passed to it as a parameter, the sub-function doesn`t operate on that variable - it actually operates on the value of it, not the variable itself. Another way of looking at it is to say the sub-function operates on a copy of the variable.

Once the sub-function has run its course, and the programme has gone back to the main function, the variable still contains the original value that it had before the sub-function was called, and run.

Here is a source code script that demonstrates this - the script uses a variable called "nn" with the initial value of 100, and a sub-function called "multipli".


        #include <stdio.h>

        int nn = 100;                          // set up variable

        int multipli(int nn);                  // declare sub-function

        int main(void)

             {

        printf(" \n \n \t value of nn before multipli = %d \n \n",nn);


        multipli(nn);                          // call sub-function


        printf(" \n \n \t value of nn after multipli = %d \n \n",nn);

             }

        int multipli(nn)                      // define sub-function

             {

        nn = nn * 2;

        printf(" \n \n \t value of nn inside multipli = %d \n \n",nn);

        return(0);

              }

When it is run, it produces 3 lines of text on the monitor -


        value of nn before multipli = 100

        value of nn inside multipli = 200

        value of nn after multipli = 100

Another curious thing about C is that the name of the parameter(s) in the declaration of a function doesn`t have to be the same as the name of the parameter(s) when the function is called. So this script works just the same.


        #include <stdio.h>

        int nn = 100;                          // set up variable

        int multipli(int nngg);               // declare sub-function

        int main(void)

             {

        printf(" \n \n \t value of nn before multipli = %d \n \n",nn);


        multipli(nn);                          // call sub-function


        printf(" \n \n \t value of nn after multipli = %d \n \n",nn);

             }

        int multipli(nn)                      // define sub-function

             {

        nn = nn * 2;

        printf(" \n \n \t value of nn inside multipli = %d \n \n",nn);

        return(0);

              }

However if the name of the parameter given when the function is called is different from the name of the parameter used when the function is defined, then the script still compiles, but it produces a different result when the programme is run. The result is


        value of nn before multipli = 100

        value of nn inside multipli = 200

        value of nn after multipli = 200

So the value in the original variable has been changed. Whether this is a quirk of gcc, or something built into C, I don`t know. But it is introducing an area of uncertainty, and I don`t know if it is a reliable way of producing a result.

 

Using a pointer variable

Now there may be circumstances where it is wanted that the sub-function changes the value of the variable, and the change sticks, after the sub-function has exited.

I had a lot of trouble getting this to work, but eventually I found two ways to do it - using a pointer variable, and using a pointer.

Using a pointer variable was the first way I found - and it is fairly straightforward in concept. Define a global pointer variable, and then do the declaration and the definition of the sub-function, and also call the sub-function without any parameters or arguments, and this works - it does a sticky change to the value in the original variable. Here is my script that does it.


        #include <stdio.h>

        int nn = 100;

        int *ptr_nn = &nn;             // define the pointer variable


        int multipli();                   // declare the sub-function

 

        int main(void)

             {

        printf(" \n \n \t value of nn before multipli = %d \n \n",nn);

        multipli();

        printf(" \n \n \t value of nn after multipli = %d \n \n",nn);

             }



        int multipli()

             {

        *ptr_nn = *ptr_nn * 2;

        printf(" \n \n \t value of nn inside multipli = %d \n \n",nn);

        return(0);

              }

This compiles okay without any warnings, and when the programme is run, the following is displayed -


        value of nn before multipli = 100

        value of nn inside multipli = 200

        value of nn after multipli = 200

I guess that using a global pointer variable means that there is no requirement for the main function to pass a parameter down to the sub-function, which is why no parameter or argument is required.

 

Using a pointer

This second way is a lot more difficult in concept, and it took me a long time to get it to work. However it has the advantage (?) that you don`t have to create a global pointer variable - you work with pointers to the memory location.

Here is a script that works -


        #include <stdio.h>

        int nn = 100;

        int multipli(int *pnn);             /* declare the sub-function using
                                             * the * character to indicate that
                                             * that the parameter is going to 
                                             * be a memory location        */

        int main(void)

             {

        printf(" \n \n \t value of nn before multipli = %d \n \n",nn);

        multipli(&nn);                 /* call the function with the 
                                            * parameter equal to the address
                                            * of the variable nn           */

        printf(" \n \n \t value of nn after multipli = %d \n \n",nn);

             }


        int multipli(int *pnn)            // define the sub-function

             {

        *pnn = *pnn * 2;

        printf(" \n \n \t value of *pnn inside multipli = %d \n \n",*pnn);

        printf(" \n \n \t value of nn inside multipli = %d \n \n",nn);

        return(0);

              }

This compiles fine on gcc, and when run, the programme produces the result of


        value of nn before multipli = 100

        value of *pnn inside multipli = 200

        value of nn inside multipli = 200 

        value of nn after multipli = 200

If nn is not set up as a global variable, but is set up as a local variable inside the main function, then it has to be passed down to the sub-function, and so has to be added to the declaration, definition, and call, of the sub-function.


        #include <stdio.h>

        int multipli(int *pnn, int nn);     /* declare the sub-function using
                                             * the * character to indicate that
                                             * that the parameter is going to 
                                             * be a memory location, and also
                                             * add nn as a parameter        */

        int main(void)

             {

        int nn = 100;                      // define nn as a local variable

        printf(" \n \n \t value of nn before multipli = %d \n \n",nn);

        multipli(&nn, nn);                 /* call the function with the 
                                            * parameter equal to the address
                                            * of the variable nn, and with
                                            * the parameter of nn           */

        printf(" \n \n \t value of nn after multipli = %d \n \n",nn);

             }


        int multipli(int *pnn, int nn)            // define the sub-function

             {

        *pnn = *pnn * 2;

        printf(" \n \n \t value of *pnn inside multipli = %d \n \n",*pnn);

        printf(" \n \n \t value of nn inside multipli = %d \n \n",nn);

        return(0);

              }

Interestingly, it produces a different result when the programme is run. In this case, when nn is a variable local to the main function, the value of *pnn gets changed inside the sub-function, but the value of nn doesn`t get changed until the sub-function exits.


        value of nn before multipli = 100

        value of *pnn inside multipli = 200

        value of nn inside multipli = 100 

        value of nn after multipli = 200

I have no idea whether this is something that is designed into C, and can be used, or whether it is some kind of quirk.

 

 

 

 

 

website design by ron-t

 

website hosting by freevirtualservers.com

 

© 2024   Ron Turner

 

+                                   +  

 

Link to the W3C website.   Link to the W3C website.