Functions, parameters and prototypes (4)

The structure of a C program requires to put the lines of code into functions. Every C program, which is designated to be compiled into an executable file should have a "main" function :

int main(void) {
    return 0;
}

We already have seen how the "main" function works in this article

This is how we create a function :

void <identifier>(<?parameters>) { ... }
<return type> <identifier>(<?parameters>) {
    ...
    return <value>;
}

And this is how we call a function :

<identifier>(<?parameters>);

Return value

When a function returns a value, the call instruction stands for this value. Not clear ? Look at this example :

int returns_5(void) {
    return 5;
}

int main(void) {
    return returns_5();
}

Now, "main" is returning the value of 5

So, we can assign a variable to the returned value :

#include <assert.h>

...

int main(void) {
    int number = returns_5();
    assert(number == 5);

    return 0;
}

What's assert ???

It's a function coming from the standard library. It requires a boolean expression and checks it. When the expression is true, it does nothing, but when it's false the program panics.

If you see assert in this course, the result is always good. It avoids some more explication, you directly get the idea.

Parameters

When the function doesn't have parameters, we simply put the void keyword, by convention.

But you can declare a function that needs parameters, and then play with a parameter like if it was a simple variable, because it is.

Then, when you will call that function, you have to specify the parameters :

int foo(int a, float b) {
    assert(a == 10);
    assert(b == 69.96);

    return 0;
}

int main(void) {
    foo(10, 69.96);
    return 0;
}

Every parameter requires a data type, same as when you declare a variable data type. When you call the function, you have to respect the correct needed data type.

Command line arguments

We have always seen main without parameters. But the function can have some. It's called "command line parameters", it's the parameters you give when you call the executable.

int main(int argc, char** argv) {
    for (int i = 0; i < argc; ++i) {
        printf("%i : %s\n", i, argv[i]);
    }

    return 0;
}
gcc main.c -o program
./program "hey" 6 zzz 97
0 : ./program
1 : hey
2 : 6
3 : zzz
4 : 97

The first parameter is always the first part of your call in the command line, here it's "./program". We often just ignore it when we play with the arguments

Naming rules for identifiers

The rules for variable identifiers are the same for function identifiers, check this article

Prototypes

The C compiler has to know every existing function in the file. If you try to call a function defined below the line where you call it, you have to create a prototype.

int foo(int x) { // new function known !
    return x;
}

int main(void) {
    foo(5); // call to the known function "int foo(int x)"
    return 0;
}

This example works. As you can see, the compiler knows the function before calling it because it was defined above the call.

But, this one doesn't work :

int main(void) {
    foo(5); // call to the unknown function "??? foo(??? x)"
    return 0;
}

int foo(int x) { // new function known !
    return x;
}

Well, to avoid this problem we simply create a prototype for the function.

int foo(int x); // new function known !

int main(void) {
    foo(5); // call to the known function "int foo(int x)"
    return 0;
}

int foo(int x) {
    return x;
}

A prototype is a function definition without any code lines defined inside it. You have to respect the same definition when you create a function prototype :

void foo(long x, int y); // new function known !

int foo(void) { // what ? "foo" should be "void foo(long x, int y)"
    return 0;
}

Think about making the prototype at the file's beginning for every function you create.


Exercises

  1. Create a function returning its first argument
  2. Print the second element from the command line arguments
  3. Fix the following code :

     #include <stdio.h>
    
     int returns(int x);    
    
     int main(void) {
         say_hello();
         return 0;
     }
    
     void say_hello(void) {
         printf("Hello !\n");
     }
    
     float returns(float x) {
         return x;
     }
    

Solutions

  1. Create a function returning its first argument

     int returns_first_arg(int to_return) {
         return to_return;
     }
    

    You can choose any type for to_return, but the return type of the function should have the same type.

  2. Print the second element from the command line arguments

     #include <stdio.h>
    
     int main(int argc, char** argv) {
         printf("%s\n", argv[1]);
    
         return 0;
     }
    
  3. Fixing code

     #include <stdio.h>
    
     void say_helo(void);
     float returns(float x);    
    
     int main(void) {
         say_hello();
         return 0;
     }
    
     void say_hello(void) {
         printf("Hello !\n");
     }
    
     float returns(float x) {
         return x;
     }