Elementary C language - function [detailed explanation] (emphasis: function recursion)

Supplementary knowledge points: goto statement

The C language provides goto statements that can be abused at will and labels that mark jumps.
Theoretically, the goto statement is not necessary, and in practice, the code can be easily written without the goto statement.
However, the goto statement is still useful in some occasions. The most common usage is to terminate the processing of the program in some deeply nested structures.
For example: Jump out of two or more layers of loops at a time.
In this case, using break in a multi-layer loop is useless (break can only jump out of the loop once). It can only exit from the innermost loop to the previous loop.
The scenarios where the goto language is really suitable are as follows

for(...)
   for(...)
     {
    
    
        for(...)
           {
    
    
              if(disaster)
              goto error;
           }
      }     
error:
if(disaster)
// 处理错误情况

Shutdown program:
After the program runs, it will shut down after a countdown of 60 seconds. If you enter I am a pig, the shutdown will be cancelled.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    
    
	char input[10] = {
    
     0 };
	system("shutdown -s -t 60");
	//shutdown为Windows系统自带的关机操作程序
	//-s设置关机;-t设置时间关机;60关机倒计时
again:
	printf("电脑将在1分钟内关机,如果输入:我是猪,就取消关机!\n请输入:>");
	scanf("%s", input);
	if (0 == strcmp(input, "我是猪"))
	{
    
    
		system("shutdown -a");
		//-a取消关机
	}
	else
	{
    
    
		goto again;
	}
	return 0;
}

1. What is a function

Definition of function from Wikipedia: Subroutine

  • In computer science, a subroutine (English: Subroutine, procedure, function, routine, method,
    subprogram, callable unit) is a certain part of the 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 will be input parameters and a return value, providing the encapsulation of the process and the hiding of details. These codes are often integrated as software libraries.

2. Function classification

  1. Library Functions
  2. custom function

2.1 Library functions:

When writing code, we will frequently use a function such as printing to the screen, copying strings, and calculations during programming.
Like the basic functions we described above, they are not business codes. Every programmer may use it during the development process.
In order to support portability and improve program efficiency, the basic library of C language provides a series of similar library functions to facilitate
software development by programmers.
How to learn library functions?
Here we have a website for learning: www.cplusplus.com
Click on it and we can see the introduction of various functions, which is convenient for searching when using. You don’t need to
insert image description here
memorize all the library functions , you need to learn how to use query tools :

www.cplusplus.com
http://en.cppreference.com (English version)
http://zh.cppreference.com (Chinese version)

Summary: The library functions commonly used in C language have

  • IO function (input and output function)
  • String manipulation functions (strlen function, strcmp function)
  • Character operation function (case conversion, judging whether it is a number or a letter)
  • memory manipulation function
  • Time/date function (time function)
  • math function
  • Other library functions
    Note: To use library functions, the header files corresponding to #include must be included.

2.2 Custom functions

Like library functions, custom functions have function names, return value types, and function parameters.
But the difference is that these are all designed by ourselves.
The composition of the function:

ret_type fun_name(para1, * )
{
    
    
      statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参数

Example:

Write a function to find the largest of two integers.

#include <stdio.h>
int get_max(int x,int y)
{
    
    
	if (x > y)
		return x;
	else
		return y;
}
int main()
{
    
    
	int a=0;
	int b=0;
	scanf("%d %d", &a, &b);
	int m = get_max(a, b);
	printf("%d", m);
	return 0;
}

operation result
insert image description here

3. Function parameters

3.1 Actual parameters (actual parameters)

The parameters actually passed to the function are called actual parameters.
Actual parameters can be: constants, variables, expressions, functions, etc.
No matter what type of quantity the actual parameters are, they must have definite values ​​when the function is called, so that these values ​​can be transferred to the formal parameters.

3.2 Formal parameters (formal parameters)

Formal parameters refer to the variables in parentheses after the function name, because formal parameters are only instantiated (allocated memory units) when the function is called, so they are called formal parameters. Formal parameters are automatically destroyed when the function call completes. Therefore formal parameters are only valid within the function.

for example:

Write a function that swaps the contents of two integer variables.

#include <stdio.h>
void Swap1(int x, int y)
{
    
    
	int tmp = x;
     x = y;
	 y = tmp;
}

int main()
{
    
    
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);

	printf("交换前:a=%d b=%d\n", a, b);
	Swap1(a, b);
	printf("交换后:a=%d b=%d\n", a, b);

	return 0;
}

The running results are as follows.
insert image description here
We found that the compiled results are not exchanged. At this time, we need to consider the problem of formal parameters and actual parameters.

insert image description here
Here you can see that when the Swap1 function is called, x and y have their own space and have exactly the same content as the actual parameters.
So we can simply think that: After the formal parameter is instantiated, it is actually equivalent to a temporary copy of the actual parameter.
after modification

#include <stdio.h>
void Swap2(int* pa, int* pb)
{
    
    
	int tmp = *pa;//tmp = a;
	*pa = *pb;    //a=b;
	*pb = tmp;    //b=tmp
}

int main()
{
    
    
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);

	printf("交换前:a=%d b=%d\n", a, b);
	Swap2(&a, &b);
	printf("交换后:a=%d b=%d\n", a, b);

	return 0;
}

The running results are as follows.
insert image description here
The addresses of a and b are passed to Swap, a can be found remotely through pa, and b can be found remotely through pb
insert image description here

When to pass by value and when to pass by address?
When we just use this value, pass the value, if we need to modify this value, pass the address

4. Function call:

4.1 Call by value

The formal parameters and actual parameters of the function occupy different memory blocks, and the modification of the formal parameters will not affect the actual parameters.

4.2 Call by reference

Call by address is a way to call a function by passing the memory address of the variable created outside the function to the function parameter.
This way 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.

insert image description here

Call by value: directly transfer the variable itself, the formal parameter is equivalent to a temporary copy of the actual parameter, and the modification of the formal parameter will not affect the actual parameter.
Call by address: Pass the address of the variable, find the external variables a and b through the address, and modify the variables a and b outside the function through the pointer

4.3 Exercises

  1. Write a function to check whether a number is prime or not.
#include <math.h>
#include <stdio.h>
//是素数返回1
//不是素数返回0
int is_prime(int i)
{
    
    
	int j = 0;
	for (j = 2; j <= sqrt(i); j++)
	{
    
    
		if (i % j == 0)
			return 0;
	}
	return 1;
}

int main()
{
    
    
	int i = 0;
	int count = 0;
	for (i = 100; i <= 200; i++)
	{
    
    
		//判断i是否为素数
		if (1 == is_prime(i))
		{
    
    
			count++;
			printf("%d ", i);
		}
	}
	printf("\ncount = %d\n", count);

	return 0;
}
  1. Write a function to check if a year is a leap year.
#include <stdio.h>
 int is_leap_year(int y)
{
    
    
	return ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0);
}

int main()
{
    
    
	int y = 0;
	for (y = 1000; y <= 2000; y++)
	{
    
    
		//判断y是否为闰年
		//如果是闰年返回1
		//不是闰年返回0
		if (is_leap_year(y))
		{
    
    
			printf("%d ", y);
		}
	}
	return 0;
}

  1. Write a function to implement binary search of an integer ordered array
#include <stdio.h>
int binary_search(int arr[], int k , int sz)
{
    
    
	int left = 0;
	int right = sz - 1;

	while (left<=right)
	{
    
    
		int mid = left + (right - left) / 2;
		if (arr[mid] < k)
		{
    
    
			left = mid + 1;
		}
		else if (arr[mid] > k)
		{
    
    
			right = mid - 1;
		}
		else
		{
    
    
			return mid;
		}
	}

	return -1;
}

int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int k = 0;
	scanf("%d", &k);
	int sz = sizeof(arr) / sizeof(arr[0]);
	//找到了就返回下标,找不到就返回-1
	int ret = binary_search(arr, k, sz);
	if (ret == -1)
		printf("找不到\n");
	else
		printf("找到了,下标是:%d\n", ret);

	return 0;
}

  1. Write a function that increases the value of num by 1 each time the function is called
#include <stdio.h>
void Add(int* p)
{
    
    
	//*p = *p+1;
	(*p)++;
}

int main()
{
    
    
	int num = 0;
	Add(&num);
	printf("%d\n", num);
	Add(&num);
	printf("%d\n", num);
	Add(&num);
	printf("%d\n", num);
	return 0;
}

5. Nested call and chain access of functions

Functions and functions can be combined according to actual needs, that is, they call each other.

5.1 Nested calls

#include <stdio.h>
void new_line()
{
    
    
	printf("hehe\n");
}
void three_line()
{
    
    
	int i = 0;
	for (i = 0; i < 3; i++)
	{
    
    
		new_line();
	}
}
int main()
{
    
    
	three_line();
	return 0;
}

Functions can be called nestedly, but definitions cannot be nested.

5.2 Chain access

Use the return value of one function as an argument to another function.

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	printf("%d\n", strlen("abcdef"));
	return 0;
}
//打印结果为6,是字符串的长度
#include <stdio.h>
int main()
{
    
    
	printf("%d", printf("%d", printf("%d", 43)));
	//结果是啥?
	//结果为4321
	return 0;
}

Here we can use www.cplusplus.com to query.
insert image description here
After querying, we find that the return value of the printf function is the number of characters printed on the screen.

6. Function declaration and definition

6.1 Function declaration:

  1. Tell the compiler what a function is called, what its parameters are, and what its return type is. But whether it exists or not, the function declaration cannot decide.
  2. The declaration of a function generally appears before the use of the function. It must be declared before use.
  3. Function declarations are generally placed in header files.

6.2 Function definition:

The definition of a function refers to the specific realization of the function, explaining the function realization of the function.

#include <stdio.h>
//函数的声明
int Add(int x, int y);

int main()
{
    
    
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

//函数的定义
int Add(int x, int y)
{
    
    
	return x + y;
}

But generally we don’t write code like this, it’s just for easier understanding of function definition and declaration
Normal writing

#include <stdio.h>
//函数的定义
int Add(int x, int y)
{
    
    
	return x + y;
}

int main()
{
    
    
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

The contents of add.h
place the declaration of the function

//函数的声明
int Add(int x, int y);

Contents of add.c
Function definition

int Add(int x, int y)
{
    
    
	return x + y;
}

The content of test.c
The realization of the function
insert image description here

7. Function recursion

7.1 What is recursion?

The programming technique in which a program calls itself is called recursion.
Recursion is widely used as an algorithm in programming languages.
A process or function has a method of calling itself directly or indirectly in its definition or description , which usually transforms a large and complex problem layer by layer into a smaller-scale problem similar to the original problem to solve.
The recursive strategy
only needs to A small number of programs can describe the multiple repeated calculations required in the problem-solving process, which greatly reduces the amount of code in the program.
The main way of thinking about recursion is: to make big things small

7.2 Two Necessary Conditions for Recursion

1. There are restrictions. When this restriction is met, the recursion will not continue.
2. After each recursive call, it gets closer and closer to this constraint.

#include <stdio.h>
int main()
{
    
    
	printf("hehe\n");
	main();
	return 0;
}

When we wrote such a function, we found that there is a problem with the function printing result, so what is the problem? insert image description here
Because the infinite calls of functions lead to stack overflow, as for the stack area and heap area of ​​the function, I will publish a separate article to explain it later.

7.2.1 Exercise:

  • exercise 1

Takes an integer value and prints its bits in order.
For example:
input: 1234, output 1 2 3 4

%u - unsigned integer
%d - signed integer
Ideas:Convert 1234 to (123) 4, (12) 3 4, (1) 2 3 4 Print
(1234) Print(123
) 4 Print
(12) 3 4
Print(1) 2 3 4

**void Print(unsigned int n)
{
    
    
	if (n > 9)//递归的限制条件
	{
    
    
		Print(n / 10);//每次递归后越来越接近这个限制条件。
	}
	printf("%d ", n % 10);
}

int main()
{
    
    
	unsigned int num = 0;
	scanf("%u", &num);
	Print(num);
	return 0;
}**

Drawing is explained as follows:
insert image description here

  • exercise 2

Write a function that does not allow the creation of temporary variables to find the length of the string.

Ideas:Convert a long string into several small additions, as shown in the figure
insert image description here

#include <stdio.h>
int my_strlen(char* str)
{
    
    //strlrn统计的是\0之前出现的字符个数
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}

int main()
{
    
    
	char arr[10] = "abc";

	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

Image caption:
insert image description here

7.3 Recursion and iteration

Iteration: A process is repeated continuously through a loop, which is a type of iteration. Iteration and recursion are essentially a method.

7.3.1 Exercises

1. Find the factorial of n. (overflow is not considered)

Iteration method:

#include <stdio.h>
int fac(int n)
{
    
    
	int i = 0;
	int ret = 1;
	for (i = 1; i <= n; i++)
	{
    
    
		ret = ret * i;
	}
	return ret;
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret = fac(n);
	printf("%d\n", ret);
	return 0;
}

Recursive method:

#include <stdio.h>
int fac(int n)
{
    
    
	if (n <= 1)
		return 1;
	else
		return n * fac(n - 1);
}

int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret = fac(n);
	printf("%d\n", ret);
	return 0;
}
  1. Find the nth Fibonacci number. (overflow is not considered)

The Fibonacci sequence is a sequence in which the sum of the first two numbers equals the third number
insert image description here

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

But we found a problem: when using the fib function, if we want to calculate the 50th Fibonacci number, it will take a lot of time.
Let's take 40 as an example.
insert image description here
Here we can find that if we want to find the Fibonacci number of 40, the decimal number like 35 below it will undergo a lot of repeated calculations, which will cause low efficiency. The modification is as follows
:

#include <stdio.h>
int 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", &n);
	int ret = Fib(n);
	printf("%d\n", ret);
	return 0;
}

A recursive method is used to complete, but this method will overflow, which will lead to wrong answers, but as long as we do not ask for too large a number, it can still be used. Here we mainly explain the method selection of recursion and iteration, regardless of overflow Case.
hint:

  1. Many problems are explained in a recursive form simply because it is clearer than a non-recursive form.
  2. But iterative implementations of these problems are often more efficient than recursive implementations, although the code is slightly less readable.
  3. When a problem is too complex to implement iteratively, the simplicity of the recursive implementation can compensate for the runtime overhead it brings.

Guess you like

Origin blog.csdn.net/2201_75366661/article/details/128869042