Elementary level of C language – function

1. Concept of function

When we mention functions, we immediately think of learning functions in mathematics, such as: y=kx + b. But functions in C language are different from those in mathematics. Wikipedia defines C language functions like this: function, also known as subroutine.

  • A portion of code in a large program, consisting of one or more statement blocks. It is responsible for completing a specific task and is relatively independent from other codes.
  • Generally, there are input parameters and return values, providing encapsulation and hiding of details of the process. These codes are often integrated into software libraries.

Why do we use functions?
First of all, functions can be reused, which allows us to no longer rewrite code, which greatly improves work efficiency; secondly, it can also make the code more modular, making it easier to modify and improve the code later.
Code in C language is divided into two categories:

  • Library functions: existing functions in C language
  • Custom functions: functions designed independently according to needs

2. Library functions

2.1 Definition of library functions

The C language standard stipulates various grammatical rules of the C language. The C language does not provide library functions; the international standard of the C language ANSI C stipulates the standards for some commonly used functions, which are called standard libraries. Different compiler manufacturers According to the C language standard provided by ANSI, the implementation of the series of functions is given. These functions are called library functions.
The standard libraries of various compilers provide a series of library functions. These library functions are declared in different header files according to the division of functions.
Header files related to library functions: https://zh.cppreference.com/w/c/header

2.2 The significance of the existence of library functions

When we write C language code, we often use some functions frequently, such as

  • Print information to the screen in a certain format (printf)
  • Enter the information required by the program into the program (scanf)
  • Calculate the length of a string (strlen)

Basic functions like the above are often used when writing programs. Therefore, the basic library of C language provides a series of similar library functions to facilitate programmers to develop software. At the same time, the quality and execution efficiency of the library functions are more guaranteed.

2.3 Learning and using library functions

Commonly used library functions in C language include: IO functions, string operation functions, character operation functions, memory operation functions, time/date functions, mathematical functions, and other library functions.
Seeing this, some people may say: “I don’t want to memorize all these library functions at once, right?”
Ha ha! That’s not necessary. When we use it, we can go to the special website to check it, slowly understand and remember it, and defeat each one.
like:
C/C++ official link: https://zh.cppreference.com/w/c/header
Cplusplus.com: https://legacy.cplusplus.com/reference/clibrary/
The first website is in Chinese. If you are not good at English, you can use this website. The second website is in English. If you are confident in your English, you can use this website.
For example, sqrt:

2.3.1 Function syntax

image.png

  • double represents the return type of the function
  • sqrt represents the name of the function
  • double x represents the parameter type and parameters passed into the function

2.3.2 Function functions

image.png

2.3.3 The header file of the function contains

Library functions are declared in the corresponding header files in the standard library, so the use of library functions must include the corresponding header files. If not included, some problems may occur. For example, the header file containing the function aqrt:
image.png

2.3.4 Function practice

code show as below:

#include<stdio.h>
#include<math.h>
int main()
{<!-- -->
double a = 16.0;
double b = sqrt(a);
printf("%lf ", b);
return 0;
}

The running results are as follows:
image.png

2.3.5 General form of checking library function documentation

  • Function prototypeimage.png

  • Function introduction

    image.png

  • Parameter and return type description

    image.png

  • Code example

image.png

  • Code output results

image.png

  • Related knowledge links

image.png

3. Custom function

Sometimes when we write code to implement a program with complex functions, there is no corresponding function in the library function that can solve this problem, so we need to design a custom function ourselves to implement this complex function. Moreover, designing custom functions can best reflect the ability of a programmer, so custom functions are very important.

3.1 Syntax form of function

The syntax of custom functions is the same as that of library functions, such as:
image.png

  • ret_type represents the function return type. Sometimes the return type is void, which means nothing is returned.
  • fun_name represents the function name, which can be designed by yourself. It is recommended to name it according to the function function to avoid confusion.
  • para1 represents formal parameters (will be mentioned later)
  • { } is the function body, which is the process of completing the calculation.

3.2 Examples of functions

Let’s design an addition function to gain a deeper understanding of custom functions.
code show as below:

#include<stdio.h>
int Add(int x, int y)
{<!-- -->
int z = x + y;
return z;
}
int main()
{<!-- -->
int a = 10;
int b = 20;
//Call the Add function
//Save the return value of the Add function into c
int c = Add(a, b);
printf(“%d”, c);
return 0;
}

image.png

4. Function parameters

In the process of using a function, the parameters of the function are divided into actual parameters and formal parameters.

4.1 Actual parameters (actual parameters)

In the above code, the parameters a and b passed to the Add function are the actual parameters, referred to as actual parameters.
**Actual parameters are the parameters actually passed to the function; **Actual parameters can be constants, variables, expressions and functions.
No matter what type of quantities the actual parameters are, they must have definite values when the function is called so that these values can be passed to the formal parameters.

4.2 Formal parameters (formal parameters)

In the above code, when defining a function, the x and y written in parentheses after the function name Add are called formal parameters, or formal parameters for short.
Formal parameters only define a function. If it is not called, the parameters in the function only exist formally. They will not apply for memory space and will not actually exist.
Formal parameters only apply for space in memory to store the values passed by actual parameters when the function is called. This process is the instantiation of the form.
Through the code we can deeply understand the actual and formal parameters:
image.png
We can see through debugging that the values of a and b are indeed passed to x and y, but their addresses represent the same, so we can understand that the formal parameter is a temporary copy of the actual parameter.

5.return statement

In the design of functions, we often encounter return statements. If we don’t understand it, we may write bugs accidentally, so let’s talk about the precautions for return statements.

  • Return can be followed by a numerical value or an expression. If it is an expression, the expression will be executed first and then the result of the expression will be returned.
  • There can also be nothing after return, just write return; This writing method is suitable for situations where the return type of the function is void.
  • The value returned by return is inconsistent with the function return type, and the system will automatically implicitly convert the returned value to the function’s return type.
  • After the return statement is executed, the function returns completely and the following code is no longer executed.
  • If there are branch statements such as if in the function, make sure there is return in every case, otherwise a compilation error will occur.

6. Function call

6.1 Calling by value

The actual parameters and formal parameters of a function occupy different memory blocks respectively, and modifications to the formal parameters will not affect the actual parameters.
So we can use call by value without changing the actual parameters of the function.
Let’s write a code to illustrate: #include

int sub(int x, int y)
{<!-- -->
return x - y;
}
int main()
{<!-- -->
int a = 0;
int b = 0;
scanf(“%d%d”, & amp;a, & amp;b);
int c=sub(a, b);
printf(“%d”, c);
return 0;
}

In the above program, we only called the values of a and b without changing their values and attributes, so we used call by value.

6.2 Call by Address

Calling by address is a way of calling a function by passing the memory address of the variable created outside the function to the function parameter.
This method of passing parameters can establish a real connection between the function and the variables outside the function, that is, the variables outside the function can be directly manipulated inside the function.
Calling by value requires the use of two operators:
Operator & amp;: address operator, the function is to obtain the address
Operator *: dereference operation, placed before the pointer, can directly dereference the address within the pointer
Again, let’s illustrate it through code.
For example: Design a function to exchange two numbers
First code:

#include<stdio.h>
void swap(int x, int y)
{<!-- -->
int num = 0;
num = x;
x = y;
y = num;

}
int main()
{<!-- -->
int a = 10;
int b = 20;
swap(a, b);
printf(“a=%d b=%d”, a, b);
return 0;
}

The running result is:
image.png
Through the running results, we found that the values of a and b were not exchanged. This is because we only exchanged the values of the formal parameters and did not change the actual parameters, and the formal parameters were destroyed after the function ended.
So we have to use pass-by-reference calls so that variables inside the function can change variables outside the function.
Second code:

#include<stdio.h>
void swap(int* pa, int* pb)
{<!-- -->
int num = 0;
num = *pa;
*pa = *pb;
*pb = num;

}
int main()
{<!-- -->
int a = 10;
int b = 20;
swap( & amp;a, & amp;b);
printf(“a=%d b=%d”, a, b);
return 0;
}

The running result is:
image.png

7. Nested calls and chained access of functions

7.1 Nested calls

Nested calling is the mutual calling between functions. Each function is like a Lego part. It is precisely because multiple Lego parts work seamlessly with each other that we can build exquisite Lego toys. It is also precisely because of the effective interaction between functions. Calling each other, finally wrote a relatively large program.
And nested calls are not mysterious. In fact, we have used them when we first came into contact with code, such as:

#include<stdio.h>
int main()
{<!-- -->
printf(“hello word”);
return 0;

}In this code, the main function calls the printf function. Ha ha! Are you surprised?
What we need to pay attention to: functions can be nested in calls to each other, but they cannot be nested in definition.

7.2 Chain access

Chained access is to use the return value of one function as a parameter of another function. Stringing functions together like a chain is chained access of functions.
In fact, chain access is not difficult to understand, come on! Let’s take an example to understand better:
image.png
Directly use the return value of strlen as the parameter of the function printf, which forms a chained access.

#include<stdio.h>
int main()
{<!-- -->
printf(“%d”, printf(“%d”, printf(“43”)));
return 0;

}The running results are as follows:
image.png
The key to this question is knowing what the return value of return is.
image.png
The printf function returns the number of characters printed on the screen.
In the above example, the first printf prints the return value of the second printf, and the second printf prints the return value of the third printf.
The third printf prints 43, prints 2 characters on the screen, and then returns 2;
The second printf prints 2, prints 1 character on the screen, and puts 1 back;
The first printf prints 1;
So the final print on the screen is: 4321.

8. Declaration and definition of functions

8.1 Function declaration

  • Tell the compiler what the function is called, what the parameters are, and what the return type is. But whether it exists or not cannot be determined by the function declaration.
  • The declaration of a function generally appears before the use of the function. It must be declared first and then used.
  • Function declarations are generally placed in header files.
  • The definition of a function is a special declaration, so it is okay to place the function definition before the call.

8.2 Definition of functions

The definition of a function refers to the specific implementation of the function and explains the functional implementation of the function.

#include<stdio.h>
void swap(int* pa, int* pb)
{<!-- -->
int num = 0;
num = *pa;
*pa = *pb;
*pb = num;

}
int main()
{<!-- -->
int a = 10;
int b = 20;
swap( & amp;a, & amp;b);
printf(“a=%d b=%d”, a, b);
return 0;
}

Above the main function in the above code is the definition of the function. Note: Functions cannot be nested.

8.3 Block writing of programs

When we write code in the company in the future, there may be a lot of code. We will not put all the code in one file. Instead, we will put the code in different files according to the function of the code.

  • Function declarations and type declarations are placed in the header file (.h)
  • The implementation of the function is placed in the source file (.c).

test.c
image.png
add.h
image.png
add.c
image.png
operation result:
image.png
Writing the program in blocks can make the code concise and clear, and also facilitate the later improvement and repair of the code.

9. Recursion and iteration of functions

9.1 Function recursion

What is recursion?
Recursion is a method of solving problems. In C language, recursion is when the function calls itself.
The core idea of recursion is the process of reducing big things to small things.
Writing constraints on recursion:

  • There are restrictions on recursion. When these restrictions are met, the recursion will no longer continue.
  • Each recursive call gets closer and closer to this limit.

Let’s give an example.
Find the factorial of n:
Let’s analyze the topic:
image.png
So we can derive its recursive formula:
image.png
This way we can easily write code.

#include<stdio.h>
int Fact(int n)
{<!-- -->
if (n > 0)
{<!-- -->
return n*Fact(n - 1);
}
else
return 1;
}
int main()
{<!-- -->
int n = 0;
scanf(“%d”, & amp;n);
int ret = Fact(n);
printf(“%d”, ret);
return 0;
}

We enter 5 (cannot enter too large a value, which may easily cause stack overflow)
The running result is:
image.png

9.1 Function iteration

The iteration of a function is equivalent to the loop of a function.
Some people may say: “Function recursion is very convenient to use, why do we need to learn iteration?”
Because this involves memory overhead issues.

  • In C language, every time a function is called, it is necessary to apply for a block of memory space in the stack area for this function call to save the values of various local variables during the function call. This space is called the runtime stack, or Function stack frame.
  • If the function does not return, the stack frame space corresponding to the function will always be occupied. Therefore, if there is a recursive call in the function call, each recursive function call will open up its own stack frame space until the function recursion no longer continues and begins to return. The stack frame space is released layer by layer.
  • Therefore, if you use function recursion to complete the code, if the recursion level is too deep, too much stack frame space will be wasted, and it may also cause stack overflow (stack overflow) problems.

Let’s illustrate it through an example.
Use recursion to find the nth Fibonacci number:
Analysis: The first two Fibonacci numbers are 1,1. It is known that the third Fibonacci number is equal to the first Fibonacci number plus the second Fibonacci number; the fourth Fibonacci number The Fibonacci number is equal to the second Fibonacci number plus the third Fibonacci number; and so on…
So we derive the recursive formula as:
image.png
code show as below:

#include<stdio.h>
int Fib(int n)
{<!-- -->
if (n >= 3)
{<!-- -->
return Fib(n - 1) + Fib(n - 2);
}
else
return 1;
}
int main()
{<!-- -->
int n = 0;
scanf("%d", & amp;n);
int ret = Fib(n);
printf("%d", ret);

return 0;
}

Enter 10 and the output is:
image.png
Enter 50 and the output is:
image.png
We will find that the result takes a long time to calculate. The time spent on this calculation is difficult for us to accept. This also shows that the recursive writing method is very inefficient.
Why is this?
It’s not that the CPU is lazy, but because there are too many repeated calculations in the recursion, the CPU can’t do it at all.
Let’s calculate the number of calculations of Fib(3) in the above code to directly see the CPU computing pressure.
code show as below:

#include<stdio.h>
int count = 0;
int Fib(int n)
{<!-- -->
if(n==3)
count + + ;
if (n >= 3)
{<!-- -->
return Fib(n - 1) + Fib(n - 2);
}
else
return 1;
}
int main()
{<!-- -->
int n = 0;
\t
scanf("%d", & amp;n);
int ret = Fib(n);
printf("%d\
", ret);
printf("count=%d\
", count);
return 0;
}

We enter 40 and the result is:
image.png
When n is particularly large, the memory overhead caused by recursion will be very large, which can easily lead to stack overflow.
Use function iteration to find the nth Fibonacci number. The code is as follows:

#include<stdio.h>
Fib(int n)
{<!-- -->
int a = 1;
int b = 1;
int c = 1;
while (n >= 3)
{<!-- -->
c = a + b;
a = b;
b = c;
n–;
}
return c;
}
int main()
{<!-- -->
int n = 0;

scanf(“%d”, & amp;n);
int ret = Fib(n);
printf(“%d\
”,ret);
return 0;
}

We enter 40 and the result is:
image.png
We will find that the code runs very quickly this time and is much more efficient.

Choices between recursion and iteration:

  1. If you use recursion to write code, it is very easy. If the code you write is no problem, then use recursion.
  2. If the problem written using recursion has obvious flaws, then recursion cannot be used and an iterative method must be used.