Skip to content Skip to main navigation Skip to footer

C Functions

C Language functions

A function is a piece of code that can be executed repeatedly. It can accept different parameters and perform the corresponding operations. The following is an example of a function.

int plus_one(int n)
{
    return n+1;
}

The above code declares a function plus_one().

When declaring a function, the following factors should be considered.

  • Return type: When declaring a function, you first need to give the type of the return value, the above example is int, indicating that the function plus_one() returns an integer.
  • Parameters: In the brackets after the function name, you need to declare the type of parameters and parameter names, plus_one(int n) that the function has an integer parameter n.
  • Function body: Function body should be written in curly brackets, no need to add a semicolon in curly brackets.
  • Return statement: Return statement gives the return value of the function, the program runs to this line, will jump out of the function body, the end of the function call. If the function does not return a value, you can omit the return statement, or write return;.

To call a function, simply add parentheses after the function name and the actual arguments inside the parentheses, like the following.

int a = plus_one(13);

// a is equal to 14

When calling a function, the number of arguments must be the same as the number of arguments in the definition; too many or too few arguments will result in an error.

int plus_one(int n) {
  return n + 1;
}

plus_one(2, 2); // error
plus_one(); // error

In the above example, the function plus_one() can only take one argument; passing in two arguments or no arguments will result in an error.

The function must be declared first and then used, otherwise an error will be reported. In other words, you must declare the function before you can use plus_one(). If you write it like the following, an error will be reported at compile time.

int a = plus_one(13);
int plus_one(int n) {
  return n + 1;

}

In the above example, the function is declared after the call to plus_one(), which will report an error at compile time.

The C language standard states that functions can only be declared at the top level of the source code file and not inside other functions.

Functions that do not return a value use the void keyword to indicate the type of return value. Functions that have no parameters are declared with the void keyword to indicate the type of the parameters.

void myFunc(void) {
  // ...
}

The myFunc() function above has neither a return value nor arguments.

The function can call itself, which is called recursion. Here is an example of the Fibonacci sequence.

#include <stdio.h>

unsigned long Fibonacci(unsigned n)
{
    if (n>2)
        return Fibonacci(n-1)+Fibonacci(n-2);
    else
        return 1;
}

int main()
{
    int n=5;
    int fifthNum = Fibonacci(n);
    printf("The 5th number of the Fibonacci sequence is %d\n",fifthNum);
}

Output:

The 5th number of the Fibonacci sequence is 5

In the above example, the function Fibonacci() calls itself.

Main Function

The C language specifies main() as the entry function of a program, which means that all programs must contain the main() function. The program always starts execution from this function, and without it, the program cannot start. All other functions are introduced into the program through it.

The main() is written in the same way as other functions, giving the type of return value and the type of arguments, and the sample code is as follows.

int main(void) {

  printf("Hello World\n");

  return 0;

}

In the above example, the last line of code return 0; means that the function has finished running and returns 0.

In C programming, a return value 0 indicates that the function ran successfully, and if any other non-zero integer is returned, the program failed and something went wrong with the code. The system determines whether the program ran successfully based on the return value of main().

Normally, if the line return 0 is omitted from main(), the compiler will add it automatically, that is, the default return value of main() is 0. Therefore, the following code has the same effect as the above.

int main(void) {
  printf("Hello World\n");
}

Since C programming only adds return to the main() function by default, but not to other functions, it is always recommended to keep the return statement.

Function Call By Value

If a function’s argument is a variable, when it is called, a copy of the value of that variable is passed in, not the variable itself.

#include <stdio.h>

void increment(int a)
{
    a++;
}

int main()
{
    int i = 10;
    increment(i);
    printf("i = %d\n",i);
}

Output:

i = 10

In the above example, after calling the increment(i) function, the variable i itself does not change and remains equal to 10, because what is passed into the function is a copy of i, not i itself, and the change in the copy does not affect the original variable. This is called “call by value“.

If the parameter variable changes, it is best to return it to the calling function as a return value.

#include <stdio.h>

int increment(int a)

{
    a++;
    return a;
}

int main()

{
    int i = 10;
    i=increment(i);
    printf("i = %d\n",i);
}

Output:

i = 11

Let’s look at the following example. The Swap() function is used to swap the values of two variables, and since it is called by value, the following code will not take effect either.

#include <stdio.h>

void swap(int x,int y)

{

    int temp;

    temp =x;

    x=y;

    y=temp;

}

int main()

{

    int a=1;

    int b=2;

    printf("The initial value of variable a is: %d\n",a);

    printf("The initial value of variable b is: %d\n",b);

    swap(a,b);

    printf("The current value of variable a is: %d\n",a);

    printf("The current value of variable b is: %d\n",b);

}

Output:

The initial value of variable a is: 1

The initial value of variable b is: 2

The current value of variable a is: 1

The current value of variable b is: 2

The above code does not swap the values of the variables because the passed variables are copies of the original variables a and b.

If you want to pass in the variable itself, there is only one way to do it, and that is to pass in the memory address or reference to the variable.

#include <stdio.h>

void swap(int* x,int* y)

{

    int temp;

    temp =*x;

    *x=*y;

    *y=temp;

}

int main()

{

    int a=1;

    int b=2;

    printf("The initial value of variable a is: %d\n",a);

    printf("The initial value of variable b is: %d\n",b);

    swap(&a,&b);

    printf("The current value of variable a is: %d\n",a);

    printf("The current value of variable b is: %d\n",b);

}

Output:

The initial value of variable a is: 1

The initial value of variable b is: 2

The current value of variable a is: 2

The current value of variable b is: 1

In the above example, by passing in the addresses of the variables x and y, the function can internally manipulate these addresses directly, so that the values of the two variables can be swapped.

Although it is not related to passing parameters, it is specifically mentioned here that functions should not return pointers to internal variables.

int* f(void) {
  int i;
  // ...

  return &i;
}

In the above example, the function returns a pointer to the internal variable i. This is not allowed. This is because when the function is executed, the internal variable is destroyed and the memory address pointing to the internal variable i is no longer valid, so it is very dangerous to use this address again.

Function pointer

A function is a piece of code in memory, and C allows access to functions via pointers.

void print(int a) {

  printf("%d\n", a);

}

void (*print_ptr)(int) = &print;

In the above example, the variable print_ptr is a function pointer that points to the address of the function print(). The address of the function print() can be obtained with &print.

Note: (*print_ptr) must be written inside parentheses, otherwise the function argument (int) has higher priority than * and the execution statement will become void* print_ptr(int).

You can call a function through its pointer.

(*print_ptr)(10);

// Equivalent to

print(10);

In the C programming language, the function name itself is a pointer to the function code, and the function address can be obtained by the function name. That is, print and &print are the same.

if (print == &print) // true

Therefore, print_ptr in the above code is equivalent to print.

void (*print_ptr)(int) = &print;
// or
void (*print_ptr)(int) = print;
if (print_ptr == print) // true

Function Prototype

As mentioned above, functions must be declared first and used later. Because the program will execute the main() function first, other functions must be declared before the main() function.

void func1(void) {

}

void func2(void) {

}

int main(void) {

  func1();

  func2();

  return 0;

}

In the above code, the main() function must be declared at the end of the program, otherwise the compiler will generate a warning that it cannot find the declaration of func1() or func2().

However, main() is the entry point to the whole program and the main logic, so it is better to put it at the top. On the other hand, for programs with a large number of functions, it becomes difficult to ensure that each function is in the correct order.

The solution provided by C is to use the function first and declare it later, as long as the function prototype is given at the beginning of the program. The function prototype, that is, tells the compiler in advance, the return type and argument type of each function. No other information is needed, and it is not necessary to include the body of the function; the function can be implemented after the main function.

int twice(int);

int main(int num) {
  return twice(num);
}

int twice(int num) {
  return 2 * num;
}

In the above example, the twice() function is implemented after main() and the function prototype is given first in the code header so that it compiles correctly. It does not matter where the function is implemented as long as the function prototype is given first.

The function prototype can also contain parameter names, which are redundant to the compiler, but may help in understanding the intent of the function when reading the code.

int twice(int);
// Equivalent to
int twice(int num);

In the above example, the twice function has one argument name, num, which is allowed whether it appears in the prototype or not.

Note: The function prototype must end with a semicolon.

Generally, the prototypes of all the functions used are given in the header of each source code file.

Function exit()

The exit() function is used to terminate the entire program. Once this function is executed, the program will end immediately. The prototype of this function is defined in the header file stdlib.h.

The exit() can return a value to the outside of the program with a parameter that is the return value of the program. Normally, there are two constants as its arguments. EXIT_SUCCESS (equivalent to 0) means that the program executed successfully, and EXIT_FAILURE (equivalent to 1) means that the program aborted abnormally. These two constants are also defined in stdlib.h.

// The program runs successfully
// Equivalent to exit(0);

exit(EXIT_SUCCESS);

// Program aborted
// Equivalent to exit(1);

exit(EXIT_FAILURE);

If you use the exit() function in the main() function, it is equivalent to a return statement. If you use exit() in another function, it will terminate the whole program.

C also provides an atexit() function to register additional functions to be executed when exit() is executed. The prototype of this function is also defined in the header file stdlib.h.

int atexit(void (*func)(void));

The argument of atexit() function is a function pointer.

Alert Message

Note:The function pointed to by atexit() is called with no arguments and no return value.

#include <stdio.h>
#include <stdlib.h>

void print(void) {

  printf("something wrong!\n");

}

int main()

{
    atexit(print);
    exit(EXIT_FAILURE);

}

Output:

something wrong!

In the above example, when exit() is executed, the print() function registered by atexit() is automatically called before terminating the program.

Extern qualifier in Function

For projects with multiple files, the source code file will use functions declared in other files. In this case, you need to give the prototype of the external function in the current file and use the extern keyword to indicate that the function is defined from the other file.

extern int foo(int arg1, char arg2);

int main(void) {

  int a = foo(2, 3);

  // ...

  return 0;

}

In the above example, the function foo() is defined in another file. The extern keyword tells the compiler that the current file does not contain the definition of the function.

Static qualifier in Function

By default, the function’s internal variables are reinitialized each time the function is called, and the values from the previous run are not retained.

When static is used to declare a variable inside a function, it means that the variable only needs to be initialized once and does not need to be initialized on each call. That is, its value remains unchanged between calls.

#include <stdio.h>

void counter(void) {
  static int count = 1;  // Initialize once only
  printf("%d\n", count);
  count++;
}

int main(void) {
  counter();  // 1
  counter();  // 2
  counter();  // 3
  counter();  // 4

}

Output:

1
2
3
4

In the above example, the internal variable count of the function counter() is modified with a static descriptor, indicating that the variable is initialized only once and each subsequent call will use the previous value, causing an incremental effect.

Note: When static variables are initialized, they can only be assigned to constants, not variables.

int i = 3;

static int j = i; // error

In the above example, j is a static variable and cannot be assigned to another variable i during initialization.

The default value of a variable declared as static is 0.

static int foo;

// Equivalent to

static int foo = 0;

The static keyword can be used to limit the function itself.

static int Twice(int num) {
  int result = num * 2;
  return(result);
}

In the above example, the static keyword indicates that the function can only be used in the current file; if the static keyword is not present, the function can be used in other files (by declaring the function prototype).

Const qualifier

A const qualifier in a function argument means that the argument variable cannot be modified in the function.

void f(int* p) {

  // ...

}

In the above example, the argument to the function f() is a pointer p. The value *p pointed to by the pointer may be changed inside the function, which may affect the outside of the function.

To avoid this, you can declare the function with the const qualifier in front of the pointer argument, telling the compiler that the value pointed to by the argument cannot be changed inside the function.

void f(const int* p) {

  *p = 0; // Compilation error

}

In the above example, the const qualifier restricts the value pointed to by the pointer p from being modified when the function is declared, so *p = 0 would be reported as an error.

In the above code, only the value pointed to by p is restricted from being modified, while the address of p itself can be modified.

void f(const int* p) {
  int x = 13;
  p = &x; // Allowed to modify
}

In the above example, the pointer p itself is modifiable, while const only restricts *p from being modified.

If you want to restrict the modification of p, you can put the const qualifier in front of p.

void f(int* const p) {
  int x = 13;
  p = &x; // Compilation error
}

If you want to restrict the modification of both pointer p and *p, you need to use two const qualifiers.

void f(const int* const p) {

  // ...

}

Variable Length Argument in C

If the number of function arguments is uncertain, when declaring the function, you can use three dots… to indicate the variable number of arguments.

int printf(const char* format, ...);

The above example is a prototype of the printf() function. Except for the first argument, the function has a variable number of arguments, related to the number of placeholders in the format string. In this case, you can use three dots ..., to indicate the variable number of arguments.

Note: The symbol must be placed at the end of the argument sequence, otherwise an error will be reported.

The header file stdarg.h defines a number of macros that can manipulate variable arguments.

  • va_list: a data type used to define a variable length argument. It must be used first when manipulating variable length arguments.
  • va_start: A function to initialize the variable length parameter. It accepts two arguments, the first one is the variable length argument, and the second one is the argument before the variable length argument, used to locate the variable length argument.
  • va_arg: a function used to obtain the current variable argument, after each call, the internal pointer will point to the next variable argument. It accepts two arguments, the first argument is the variable argument object and the second argument is the type of the current variable argument.
  • va_end: a function used to clean up the variable length argument.

Here’s an example:

#include <stdio.h>

#include <stdarg.h>

double average(int i, ...) {

  double total = 0;

  va_list ap;

  va_start(ap, i);

  for (int j = 1; j <= i; ++j) {

    total += va_arg(ap, int);

  }

  va_end(ap);

  printf("total is %f\n",total);

  return total / i;

}

int main()

{

    double  avg = average(5,1,2,3,4,5);

    printf("the average value is: %f\n",avg);

}

Output:

total is 15.000000
the average value is: 3.000000

In the above example, va_list ap defines ap as a variable-length parameter variable. va_start(ap, i) puts the parameters after parameter i into ap, va_arg(ap, double) is used to take one parameter from ap in order and specify the parameter as double type, and va_end(ap) is used to clean up the variable-length parameter.

Was This Article Helpful?

25
Related Articles
0 Comments

There are no comments yet

Leave a comment

Your email address will not be published.