Broad Network


Function in the C Computer Language

Part 19 of Complete C Course

Foreword: Function in the C Computer Language

By: Chrysanthus Date Published: 4 Jun 2024

Introduction

A Function is a set of statements that perform a specific task. In a long program, there are groups of statements that will have to be doing the same task in different parts of the program. This group of statements in different parts of the program, do not need to be retyped, where and when they are needed. They are normally typed once, and then called as a group, wherever and whenever they are needed in the program or application (an application consists of more than one related program). Such group of statements are called functions, as explained in this section of the chapter.

Defining a Function
The group of statements that are to form the function, need to be grouped in a particular way. By doing this, the function is being defined. This process can be split into two phases. One phase is called, declaring the function, and the other phase is the proper definition of the function. For this section of the chapter, a combined phase will be used. How a function creation can be split into two phases, will be explained later in this chapter.

A function definition consists of the following parts, in the order given:

- The object returned type (see below)
- The name (identifier) of the function.
- A list of parameters to the function, enclosed in parentheses and separated by commas, if needed (see below).
- The statements that form the body of the function are enclosed in curly brackets. The statements in a function can have among them, calls to other functions defined in the current program (or application) - see below.

Example
In the following example, a function is defined that will add two numbers, find the square of the sum and then print (display) the result.

    #include <stdio.h>
    
    void myFn()
        {
            int num1 = 2;
            int num2 = 3;

            int sum = num1 + num2;
            int square = sum * sum;

            printf("%i\n", square);
        }

    int main(int argc, char *argv[])
    {
        myFn();

        return 0;
    }

The function begins with the word, void, then a space and myFn(), then a block of statements. There are actually two functions in the above code: one with the name, myFn and the other with the name, main. In C, any single program should have a main() function. The last statement in the main() function should be "return 0;".

In the block, there is the declaration with assignment of the two integer numbers. The third statement in the block sums the two numbers. The fourth statement squares the sum. The last statement displays (prints) the square.

Calling a Function

When the execution of a C program starts, all the statements in the main() function block are executed first. In the above case, there are two statements in the block of the main() function. Execution of statements in any block begins from top to bottom.

In the execution of the statements in the block of the main() function, the statement, "myFn()" will be executed first. This statement calls the function, myFn(), which is coded outside the block of the main() function. In other words this statement causes the statements in the function, myFn(), to be executed.

A function is called, just by typing the name of the function, followed by parentheses, in one statement. The parentheses may have what is called arguments - see below. In the above program, if the function, myFn() is never called, it will never be executed.

myFn() is an example of a user defined function. The function the programmer defines should be coded outside the block of the main() function. Note: the name or identifier of the whole myFn() function definition, is myFn and not myFn().

The expression (e.g. myFn()) that calls a function, is called the calling function. The function definition called, is called the called function.

The return value and return type
A function can return a value or pointer (address). Precisely, a function can return the value of an object or a pointer to an object. If a function returns a value or pointer, the calling expression e.g. myFn(), can be assigned to some identifier, which is the receiving identifier. The programmer can then do whatever he/she wants to do, with the identifier. The above myFn() does not return anything. Put another way, the above myFn() function returns void. Note: displaying something at the output terminal, is not function return. The following myFn() function, returns an integer value.

    #include <stdio.h>

    int myFn()
        {
            int num1 = 2;
            int num2 = 3;
            
             int sum = num1 + num2;
             int square = sum * sum;
            
            return square;
        }

    int main(int argc, char *argv[])
    {
        int result = myFn();
        
        printf("%i\n", result);

        return 0;
    }

In the myFn() function definition this time, instead of having the printf() function call, there is the return statement, which is:

            return square;

A return statement begins with the reserved word, return, followed optionally by an expression. This expression can be just a value or just an identifier, e.g. square, as in the above case. All statements (be it return or not) must end with a semicolon.

In the myFn() definition, square is an object of type, int. Now, look inside the block of the main() function. The right operand of = in the first statement, is a function call (calling function), that calls the function (definition), myFn(). This function call returns what was returned by the return statement in the function definition. It is the value of the object identified by square, that was returned. In the block of the main() function, this return value is assigned as content to the object, newly declared with the identifier, result. The programmer can then use result, in anyway he/she wants. The print statement in the block of the main() function, prints the value of result, which is the same value as that of square.

Now, if a function definition would return a value or a pointer, then the type of the value has to be indicated at the beginning of the function definition. In the first program above (in this section of the chapter), the function, myFn() does not return anything, and because of that, it does not have a return statement. So the function definition is begun with void. In the second program, the function, myFn() returns a value from an int object, so its definition is begun with int. A function definition is begun with the type of value of the object, it will return. That is, begin a function definition with the object type it will return. A function returns the value of an object, not the object itself. A function returning a pointer, returns the address which is the content of the pointer. This return address can be assigned to a newly declared pointer in the main() function.

Parameters and Arguments
Now, the above function can only deal with two particular numbers, which are 2 and 3. This is a disadvantage. If any two numbers are defined and being assigned to identifiers outside the function, then the values can always be changed through their identifiers, and then the identifiers sent to the function definition, before it executes. In this way, many other pairs of numbers can be dealt with. The following program illustrates this:

    #include <stdio.h>

    int num1 = 2;
    int num2 = 3;

    int myFn(int no1, int no2) {
             int sum = no1 + no2;
             int square = sum * sum;
            
            return square;
    }

    int main(int argc, char *argv[])
    {
        int result = myFn(num1, num2);
        printf("%i\n", result);

        return 0;
    }

As mentioned above, any C program must have a main() function. If any code segment has to be executed outside the main() function, that code segment has to be called from the block of the main() function.

This time the identifiers have been defined and assigned outside the function, myFn(). Some other function elsewhere in the program can actually change these values. However, no function can change the value of an identifier inside some other function (everything being equal). In the definition of the function, the parentheses now have two object declarations. Each of these declarations has the type of object and then the identifier of the object. The identifiers are for the two objects needed. These declarations in this position, in the parentheses of the function definition, are called Parameters. The parameters are separated by commas. The identifiers of these parameters are used within the function definition.

Such a first line of a function definition is called a function signature (without the return type), and sometimes loosely called a prototype. The function signature does not include the opening curly bracket of the block of the function definition. This opening curly bracket of the block, has been placed in the same line as the function signature, in order to reduce the typed length of the function definition, by one line.

Within the main() function body, where the myFn() function is called, the parentheses have two identifiers, without any preceding type (when calling a function, such identifiers must not have any preceding type). These identifiers in this position, are called Arguments. The arguments are separated by commas. These arguments for the function (call), are the identifiers defined (and assigned) outside the function; and simply have to be used without their preceding types. The arguments to a function call, can also be literals, something like:

    int result = myFn(2, 3);

Read the above program again and test it. It is advisable to always make the identifiers for the parameters, different from the corresponding identifiers for the arguments. If the programmer does not do this, then while manipulating the parameters within the function, the programmer might change the values of the corresponding identifiers outside (and above) the function.

Note: The definition of the predefined printf() function call, is in the library "stdio" of header "stdio.h", usually included at the beginning of the program.

Function returning a Pointer

If a function will return a pointer, then precede the function name with * in the definition. In the block of such a function definition, is a pointer object whose value (address) is returned. The following program illustrates this:

    #include <stdio.h>

    int *theFn() {
        int pointed = 6;
        int *pointer = &pointed;
            
        return pointer;
    }

    int main(int argc, char *argv[])
    {
        int *receiver = theFn();
        printf("%i\n", *receiver);

        return 0;
    }

The output is 6. The function, theFn() will return a pointer. So in the definition, the function name is preceded by the indirection operator, *. The type of value that the function will return precedes the *, both of which precedes the function name (identifier). The first statement in the block of the theFn() function definition, initializes an identifier, that will be used as the pointed object. The second statement initializes a pointer object for this pointed object. The last statement returns the value of the pointer, which is an address.

The first statement in the main() function is:

        int *receiver = theFn();

This statement initializes the pointer, receiver, which is different from pointer, in the function definition block. The right operand of = here, is a call to the function that returns a pointer (memory address). It is normal to initialize different pointers, using the memory address of the same pointed object. So in this initialization statement, a pointer (memory address) goes into the object identified by, receiver. So far as the whole code is concerned, two pointer objects now have the memory address of the one pointed object. The pointer objects consist of the pointer in the theFn() function block, and the receiver in the main() function block.

Now, two pointers are pointing to the same object. In order to get the value of the object they are pointing to, the programmer has to use the indirection (dereference) operator on any of the pointers in their different blocks. The printf() function call, uses the indirection operator, * on the pointer, receiver.

Note that when calling the function, the function name, theFn, was not preceded with *. Read and test the above code, if that has not already been done.

Passing a Memory Address to a Function

A parameter of a function (signature), may be a pointer type. The following program illustrates how it is used:

    #include <stdio.h>

    int aFn(int *no) {
        printf("%i\n", *no);

        int anInt = 77;
        no = &anInt;
            
        return *no;
    }

    int main(int argc, char *argv[])
    {
        int myInt = 88;
        int hisRet = aFn(&myInt);
    
        printf("%i\n", hisRet);

        return 0;
    }

The output is:

    88
    77

When the program starts, the statements in the main() function are executed, beginning from the top. The first statement in the main function creates an int object whose identifier is myInt. The value of 88 is assigned to it (initialized), in the declaration statement. For the next statement, the right operand of = calls the function, aFn(). This function returns an integer, that is assigned to an int object whose identifier is hisRet. The argument for this function call is the address of the object identified by myInt (in the main function). The value of the object whose address is sent as argument is 88.

The function definition, aFn is called, by the right operand of = of the second statement in the main() function. Outside the main function, the parameter of the aFn() function (signature) is,

        int *no

The identifier, no, is used inside the function definition block. Just before the statements in the aFn() function definition are executed, the following initialization takes place, unnoticed by the programmer:

    int *no = &myInt;

The right operand of this assignment operator, of this initialization (declaration), is the address of an object initialized in the main() function. This address was sent as argument in the function call. The left operand here, has the pointer declaration of the parameter of the function definition (signature). In the initialization of a pointer, the right operand is an address. The statements inside the function definition block, use this hidden initialization.

The first line in the aFn() function definition, prints the value pointed to by no; that is, the first line prints *no. By the hidden initialization, this value is 88. The second statement in the definition, initializes a new int identifier, anInt with the value 77. The third statement copies the address of this object identified by anInt, to become content of the object identified by no. no now points to an object whose value is 77, instead of the object whose value is 88. The fourth statement in the definition, returns the new value pointed to by no, that is *no, which is now 77.

Back to the main() function: When the program starts, the statements in the main() function are first executed. The first statement in the main() function assigns the value 88 to the object identified by myInt. The second statement in the main() function calls the function definition, aFn(). The third statement in main() will only execute after this called function has finished executing (beginning from its own top). When the execution of the called function, aFn() completes, the function aFn() returns the value 77. This value is assigned to the int object identified by hisRet in the main() function. After this, execution of the rest of the statements in the main() function continue.

As they continue, next is the printf() function call statement (third statement) in the main() function. This prints the value of hisRet, which is 77,  to the screen.

Note: Passing an object to a function, the normal way, ends up with two copies (memory regions) of the object in memory: the argument has one copy and the parameter has another copy. On the other hand, passing the memory address of the object, ends up with only one copy of the object: both the argument and parameter refer to the same object in memory.

Passing an Array to a Function

The following program shows how an array should be passed to a function:

    #include <stdio.h>
    
    void fn(char arra[]) {
        for (int i=0; i<11; i++)
            printf("%c", arra[i]);
        printf("\n");
    }

    int main(int argc, char *argv[])
    {
        char arr[11] = {'I', ' ', 'l','o','v','e', ' ', 'y', 'o', 'u', '.'};
        fn(arr);

        return 0;
    }

The output is:

    I love you.

The first statement in the main() function defines an array of chars. This is not a string. It is an array of characters. If it were a string, its last character would have been null (\0). The second statement calls the user defined function, fn() passing the array as argument. Note that the argument does not have the [] operator and any number within. It has just the identifier name, without being succeeded by the subscript.

At the function signature of the definition of the user defined function, fn(), the corresponding parameter is "char arra[]". It does not have any number within its square brackets, and it should not have. Under such condition, the parameter is referred to as a variable length array. The total number of characters in the array is 11, used by the for-loop in the fn() function definition. The for-loop has only one statement in its body, so it does not need the curly brackets.

So, to pass an array to a function, the argument should have just the identifier name, without the [] operator and without any number within it. The parameter should be an array declaration, without any number in its square brackets (and without initialization).

Note: a function in C cannot return an array. Also, when passing an array to a function, the number of elements of the array has to be sent as an extra argument, if it would be needed within the function definition.

Passing a String to a Function and Function returning a String

The following program, shows how to pass a string to a function and how to return a string.

    #include <stdio.h>
    
    char *const fn(char *const strParam) {
    
        return strParam;
        
    }

    int main(int argc, char *argv[])
    {
        char *const str = "I love you.";
        char *const strRet = fn(str);
        printf("%s", strRet);
        printf("\n");

        return 0;
    }

The output is:

    I love you.

The first statement in the C main() function defines the string. The compiler will convert the content of "I love you." into an array of chars, terminating the array with the null character, '0'. The declaration of the string in this statement definition, is "char *const str". The second statement in main(), calls the fn() function, using the argument, str which is the identifier of the user defined string. The function fn() returns a constant pointer to the first element of a string (ending with \0) in memory. The parameter in the signature of the fn() function, is the classical declaration of a string, with possibly a different identifier (name). For a function definition to return a string, it has to be preceded by "char *const". The fn() function, does nothing, other than just return the const string pointer passed to it.

So, passing a string to a function means, passing the constant string pointer. A function definition that returns a string, returns the string's constant pointer. A function definition that returns a string, should be preceded with "char *const", which is the string declaration without the string identifier. The appropriate identifier in this case, is the identifier, which is the name of the function.

Pointer to a Function
A function returning a pointer is not the same thing as a pointer to a function. To create a pointer to a function, do,

    int (*func)() = &fn;

where fn() is a function defined elsewhere, and func is the pointer. func has the address of the start of the fn() function definition. To call the function definition, use "fn()" ordinarily or "func()" without preceding it with * or &. The following program illustrates all these:

    #include <stdio.h>
    
    int fn(int num) {
        return num;
    }
    
    int (*func)() = &fn;
    
    int main(int argc, char *argv[])
    {
        int val1 = fn(5);
        printf("%i\n", val1);
        
        int val2 = func(5);
        printf("%i\n", val2);

        return 0;
    }

The output is 5, 5; with each 5 on its own line. Note the stand-alone statement, "int (*func)() = &fn;".




Related Links

More Related Links

Cousins

BACK NEXT

Comments