[Elementary C language] The most complete function explanation on the whole network of epic works (full dry goods + recommended collection)

Kind tips

Hello everyone, I am Cbiltps. In my blog, if there are sentences that are difficult to understand or key points that are difficult to express in words , I will have pictures . So my blog with pictures is very important ! ! !

If you are interested in me please see my first blog !

Key points of this chapter

This chapter mainly masters the basic use of functions and recursion

  1. what is the function
  2. Library Functions
  3. custom function
  4. function parameters
  5. function call
  6. Nested calls and chained access of functions
  7. Function declaration and definition
  8. function recursion

start of text


1. What is a function?


We often see the concept of function in mathematics. But do you understand functions in C language? Definition of function in
Wikipedia :子程序

  1. 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.
  2. Generally, there will be input parameters and a return value, providing the encapsulation of the process and the hiding of details. These codes are usually integrated as software
    libraries.

2. Classification of functions in C language


1. Library functions
2. Custom functions

2.1 Library functions

Why are there library functions?

  1. We know that when we are learning C language programming, we can't wait to know the result after a code is written, and want to print the result to our screen to see. At this time, we will frequently use a function: print information to the screen in a certain format (printf).
  2. In the process of programming, we will frequently do some string copy work (strcpy).
  3. When programming, we will calculate, and always calculate operations such as n to the kth power (pow).

Like the basic functions described above, they are not business code. 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.

A brief summary, the library functions commonly used in C language are:

  • IO functions ( input/output input and output functions : printf scanf gethcar putchar ...)
  • String manipulation functions (strlen strcmp strcpy strcat ...)
  • Character manipulation functions (tolower toupper ...)
  • Memory manipulation functions (memcpy memset memmove memcmp ...)
  • time/date functions (time ...)
  • math functions (sqrt asb fabs pow ...)
  • Other library functions

2.1.1 How to learn to use library functions

So how to learn library functions?
库函数不需要全部记住,需要学会查询工具的使用。

First of all, I recommend a few query tools for everyone (English is very important, at least you have to understand the literature):

2.2 Custom functions

If library functions can do everything, what do programmers need to do?
All the more important are 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 . This gives programmers a lot of room to play.

The composition of the function:

ret_type fun_name(para1, *)
{
    
    
	statement;//语句项
}

ret_type 返回类型
fun_name 函数名
para1 函数参数

Let's take an example:

Write a function to find the maximum of two numbers

#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)//形式参数
{
    
    
	return (x > y) ? (x) : (y);
}

int main()
{
    
    
	int num1 = 10;
	int num2 = 20;
	int max = get_max(num1, num2);//实际参数
	printf("max = %d\n", max);
	
	return 0;
}

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

Difficulties to overcome : For example, first pour soy sauce into an empty bottle, then pour vinegar into the soy sauce bottle, and finally pour soy sauce into the vinegar bottle.
directly on the map

#include <stdio.h>

void Swap1(int x, int y)
{
    
    
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;
}

void Swap2(int* px, int* py)
{
    
    
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}

int main()
{
    
    
	int num1 = 1;
	int num2 = 2;
	Swap1(num1, num2);//传值调用,在下面讲解
	printf("Swap1:num1 = %d num2 = %d\n", num1, num2);
	Swap2(&num1, &num2);//传址调用,下面讲解
	printf("Swap2:num1 = %d num2 = %d\n", num1, num2);
	
	return 0;
}

The parameters in the above Swap1 and Swap2x functions , y, px, pyare all formal parameters . The actual parameters are passed to Swap1 in the main functionnum1 and num2passed to the Swap2 function &num1.&num2


3. Function parameters


3.1 Actual parameters (actual parameters)

The actual parameter passed to the function is called 实参.

Arguments can be: 常量, 变量, 表达式, 函数等.

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)

The formal parameter refers to the variable in the parentheses after the function name , because the formal parameter is only instantiated (allocated memory unit) when the function is called
, so it is called 形式参数.

Formal parameters are automatically destroyed when the function call completes.

Therefore formal parameters are only valid within the function.

3.3 Parameter conclusion

Open the memory window, and we analyze the actual parameters and formal parameters of the previous code.
insert image description here
Here we 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: 形参实例化之后其实相当于实参的一份临时拷贝.


4. Function call


4.1 Call by value

The formal parameters and actual parameters of the function occupy different memory对形参的修改不会影响实参 blocks respectively .

4.2 Call by address

  • 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
    .

(This is a supplementary explanation) Regarding the parameter passing of functions : When I was writing the structure (structure parameter passing) , I clicked it 函数传参的时候,参数是需要压栈的, and you can expand it.

4.3 Exercises

1. Write a function to judge the prime numbers in the range

#include <stdio.h>
#include <math.h>

//判断n是否为素数
int is_prime(int n)
{
    
    
	//试除法,有两种方法
	//2~n-1
	//2~sqrt(n)
	int j = 0;
	for (j = 2; j <= sqrt(n); j++)
	{
    
    
		if (n % j == 0)
			return 0;
	}
	return 1;
}

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

	return 0;
}

2. Write a function to judge the leap year within the judgment range

#include <stdio.h>

//int is_leap_year(int y)
//{
    
    
//	if (y % 4 == 0)
//	{
    
    
//		if (y % 100 != 0)
//		{
    
    
//			return 1;
//		}
//	}
//	if (y % 400 == 0)
//		return 1;
//
//	return 0;
//}

// 优化后
//int is_leap_year(int y)
//{
    
    
//	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
//		return 1;
//	else
//		return 0;
//}

//再优化后
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) == 1)
		{
    
    
			printf("%d ", y);
		}
	}

	return 0;
}

3. Write a function to realize the binary search of an integer ordered array

#include <stdio.h>

int main()
{
    
    
	int arr[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int reft = 0;
	int right = sz - 1;
	int mid = 0;
	int k = 0;//要查找的元素
	int flag = 0;//找不到
	printf("请输入您要查找的数字:");
	scanf("%d", &k);
	while (reft <= right)//即使是 reft==right 也有一个元素需要查找
	{
    
    
		mid = (reft + right) / 2;//每次二分查找都要求出中间元素的下标
		if ((arr[mid] < k))
		{
    
    
			reft = mid + 1;
		}
		else if (arr[mid] > k)
		{
    
    
			right = mid - 1;
		}
		else
		{
    
    
			flag = 1;//找到了
			break;
		}
	}
	if (1 == flag)
	{
    
    
		printf("找到了,下标是%d\n", mid);
	}
	else
	{
    
    
		printf("没找到\n");
	}
	return 0;
}

4. Write a function that increases the value of num by 1 every time the function is called

#include <stdio.h>

void Use(int* p)
{
    
    
	*p = *p + 1;
}

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

	return 0;
}

5. Nested call and chain access of functions


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 (two examples)

//例子1:
#include <stdio.h>
#include <string.h>

int main()
{
    
    
	int len = strlen("abc");

	printf("%d\n", len);
	printf("%d\n", strlen("abc"));//链式访问

	return 0;
}
//例子2:
#include <stdio.h>
int main()
{
    
    
printf("%d", printf("%d", printf("%d", 43)));//链式访问
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}

//解析:所以打印的是4321

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. 要满足先声明后使用.
  3. Function declarations are generally placed in header files.

The content of xxx.h : the declaration of the placement function

//方法1:
#ifndef __TEST_H__
#define __TEST_H__
//函数的声明
int Add(int x, int y);
#endif

//方法2:完全等价的且简单
#pragam once//这段代码的意思是 防止头文件重复,多次的应用
//后面进行函数的声明
extern int Add(int x, int y);

6.2 Function definition

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

The content of xxx.c : the implementation of the placement function

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

6.3 Sub-module design idea

This form of sub-file writing has many advantages in large-scale projects:

  1. Collaboration
  2. encapsulation and hiding

Suppose a piece of code is very complicated and we don’t want to write it ourselves, we can buy other people’s code, but they won’t sell you the source code !
But people will compile and sell something to you, which is a static library (.lib file) .

6.3.1 How to compile the .lib file (see the picture directly)

insert image description here
The content in the .lib file is binary information , and then the .lib file and the header file will be sold to you, so that you can not only protect your own code, but also sell the function to you, so as to achieve packaging and hiding!

6.3.2 How to add static library

insert image description here
Then run it!


7. Function recursion and iteration


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 direct or indirect method in its definition or description . It usually converts 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 a small amount of The program 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:把大事化小 .

7.2 Two Necessary Conditions for Recursion

  • There are constraints, and when this constraint is met, the recursion will not continue.
  • This limit is getting closer and closer after each recursive call.

7.2.1 Exercise 1: (drawing pictures to explain)

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

Reference Code:

#include <stdio.h>

void print(unsigned int n)
{
    
    
	if (n > 9)//这个就是限制条件
	{
    
    
		print(n / 10);
	}
	printf("%d ", n % 10);
}

int main()
{
    
    
	unsigned int num = 1234;
	print(num);

	return 0;
}

Drawing explanation:

1. Difficulties to overcome:
insert image description here
2. Analysis ideas:
insert image description here

3. Supplementary explanation:
What happens if you don’t add it to the code if (n > 9)//这个限制条件?
insert image description here
At this time, it will be stuck when running, and then an error will be reported when debugging.
When a recursion goes on infinitely, an error is called 栈溢出.
insert image description here
Regarding the creation and destruction of function stack frames, I will write an article in [Advanced C Language] to explain, please look forward to it!
When the time comes, post the link directly! !

7.2.2 Exercise 2: (drawing and explaining)

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

Reference Code:

#include <stdio.h>

int my_strlen(char* s)
{
    
    
	if (*s != '\0')
	{
    
    
		return 1 + my_strlen(s + 1);
	}
	else
	{
    
    
		return 0;
	}
}

int main()

{
    
    
	char arr[20] = "abcd";//数组名arr是数组首元素的地址 — char*
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

Drawing explanation:

1. Overcoming difficulties:
insert image description here
2. Analytical thinking:
insert image description here
3. Supplementary explanation:

Suggestion: function at design time 能不创建全局变量就不创建全局变量.

7.2.3 Several classic problems of recursion

About this part, I will write a separate blog to teach you how to do it!
So I won’t introduce too much, I will post the link directly later, please look forward to it!

  1. Towers of Hanoi problem
  2. frog jumping stairs problem

7.3 Iteration

First, let's use function recursion to do the following questions!

7.3.1 Exercise 3:

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

Key code reference:

int factorial(int n)
{
    
    
	if (n <= 1)
		return 1;
	else
		return n * factorial(n - 1);
}

7.3.2 Exercise 4:

Find the nth Fibonacci number. (overflow is not considered)

Key code reference:

int fib(int n)
{
    
    
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}

But we found a problem :

  1. When using the fib function, it is particularly time-consuming if we want to calculate the 50th Fibonacci number.
  2. Using the factorial function to find the factorial of 10000 (regardless of the correctness of the result), the program will crash.

why?
We found that many calculations are actually repeated during the call of the fib function.

If we modify the code a bit:

int count = 0;//全局变量
int fib(int n)
{
    
    
	if (n == 3)//第三个斐波那契数
		count++;
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}

Finally, we output the count, which is a very large value.

The problem is that:

  • When debugging the factorial function, if your parameters are relatively large, an error will be reported: stack overflow(栈溢出)such information.
  • The stack space allocated by the system to the program is limited, but if there is an infinite loop, or (dead recursion), this may lead to the creation of stack space all the time, and eventually the stack space will be exhausted. This phenomenon is called stack overflow .

So how to solve the above problems:

  1. Rewrite recursively to non-recursively.
  2. Use static objects instead of nonstatic local objects. In the design of recursive functions, static objects can be used instead of nonstatic local objects (that is, stack objects), which not only reduces the overhead of generating and releasing nonstatic objects for each recursive call and return, but also saves the intermediate state of recursive calls , and can be accessed by each calling layer.

For example, the following code implements it in a non-recursive way:

//求n的阶乘
int factorial(int n)
{
    
    
	int result = 1;
	while (n > 1)
	{
    
    
		result *= n;
		n -= 1;
	}
	return result;
}
//求第n个斐波那契数
int Fib(int n)
{
    
    
	int a = 1;
	int b = 1;
	int c = 1;

	while (n > 2)
	{
    
    
		c = a + b;
		a = b;
		b = c;
		n--;
	}

	return c;
}

7.3.1 Summary

  1. When solving a problem both recursive and non-recursive can be used , and there is no obvious problem , then use recursion
  2. When solving a problem recursively written is very simple , and there is no obvious problem , then use recursion
  3. When solving a problem recursively is easy to write , but there are obvious problems , then recursion cannot be used.

Finally, let me remind you:

  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.

end of text

Guess you like

Origin blog.csdn.net/Cbiltps/article/details/119814668