Function (super detailed explanation)

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 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. Classification of functions in C language

  1. Library Functions
  2. custom function

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. In programming, we also calculate, and always calculate the operation (pow) of n to the kth power.
    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.

Are the library functions provided by the C language?
It is agreed in the C language standard that the compiler manufacturer provides the implementation of
the C language standard:

  1. Function function - measure the length of a string
  2. function name strlen
  3. parameter const char* str
  4. return type size_t

So how to learn library functions?
Here we simply take a look: www.cplusplus.com

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

  • I/O function
  • String manipulation functions
  • character manipulation functions
  • memory manipulation function
  • time/date functions
  • Numeric function
  • Other library functions

Learn several library functions:
strcpy :
insert image description here
code example:

int main()
{
    
    
	char arr1[20] = "xxxxxxxxxxxx";
	//
	char arr2[] = "hello C";
	printf("%s\n", strcpy(arr1, arr2));
	return 0;
}

Running result:
insert image description here
memset :
insert image description here
code example:

int main()
{
    
    
	char arr[] = "hello C";
	memset(arr, 'c', 5);
	printf("%s\n", arr);

	return 0;
}

Running result:
insert image description here
Note:

But a secret that the library function must know is: to use the library function, the header file corresponding to #include must be included .
Here we learn the above library functions according to the documentation, the purpose is to master the usage of the library functions.

2.1.1 How to use library functions

Need to memorize them all? No
need to learn how to use query tools:

MSDN (Microsoft Developer Network)
www.cplusplus.com
http://en.cppreference.com (English version)
http://zh.cppreference.com (Chinese version)
English is very important. At least read 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 designed by ourselves, which gives programmers a lot of room to play.

The composition of the function:

ret_type fun_name(para1, * )
{ statement;//statement item } ret_type-- return type fun_name --function name para1 - function parameter




Let's take an example:

Write a function to find the largest of two integers.

Code demo:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int get_max(int x, int y)
{
    
    
	//函数体
	if (x > y)  
		return x;
	else     
		return y;
}
int main()
{
    
    
	int num1 = 0;
	int num2 = 0;
	scanf("%d %d", &num1, &num2);
	printf("%d\n", get_max(num1, num2));
	return 0;
}

Running result:
insert image description here
choose to write or not to write parameters according to actual needs (the following parameters do not need to be written)

void print()
{
    
    
	printf("hehe\n");
}

Another example:

Write a function that swaps the contents of two integer variables

//实现成函数,但是不能完成任务
//形式参数
void Swap1(int x, int y)
{
    
    
	int z = 0;
	z = x;
	x = y;
	y = z;
}
int main()
{
    
    
	int num1 = 0;
	int num2 = 0;
	scanf("%d %d", &num1, &num2);

	printf("交换前:num1 = %d num2 = %d\n", num1, num2);
		//实际参数
	Swap1(num1, num2);
	//函数调用的时候,将实参传递给形参
	//形参其实是实参的一份临时拷贝
	//对形参的修改,不会改变实参
	printf("交换后:num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

Running result:
insert image description here
Why is it useless to exchange? Let's take a look at the following code:

//正确的版本
//形式参数
void Swap2(int* p1, int* p2)
{
    
    
	int tmp = 0;
	tmp = *p1;//tmp = num1;
	*p1 = *p2;//num1 = num2;
	*p2 = tmp;//num2 = tmp;
}

int main()
{
    
    
	int num1 = 0;
	int num2 = 0;
	scanf("%d %d", &num1, &num2);
	printf("交换前:num1 = %d num2 = %d\n", num1, num2);
	//实际参数
	Swap2(&num1, &num2);
	printf("交换后:num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

operation result:
insert image description here

Case analysis diagram:
insert image description here
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.
The formal parameter is automatically destroyed after the function call is completed , so the formal parameter is only valid in the function.

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.

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.

4.3 Exercises

  • Write a function to check whether a number is prime or not
bool is_prime(int n)
{
    
    
	int j = 0;
	for (j = 2; j <= sqrt(n); j++)
	{
    
    
		if (n % j == 0)
			return false;
	}
	return true;
}
int main()
{
    
    
	int i = 0;
	int count = 0;
	for (i = 101; i <= 200; i += 2)//素数不可能是奇数
	{
    
    
		if (is_prime(i))
		{
    
    
			count++;
			printf("%d ", i);
		}
	}
	printf("\ncount=%d\n", count);
	return 0;
}

operation result:

insert image description here

  • Write a function to determine whether a year is a leap year
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdbool.h>
//is_run_year 不推荐的
bool is_leap_year(int y)
{
    
    
	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
		return true;
	else
		return false;
}
int main()
{
    
    
	int y = 0;
	int count = 0;
	for (y = 1000; y <= 2000; y++)
	{
    
    
		//判断y是否是闰年
		if (is_leap_year(y))
		{
    
    
			count++;
			printf("%d ", y);
		}
	}
	printf("\ncount = %d\n", count);

	return 0;
}

operation result:
insert image description here

  • Write a function to implement binary search of an integer ordered array
//找到了,就返回下标
//找不到,返回 -1
int binary_search(int arr[], int k, int sz)
{
    
    
	int left = 0;
	int right = sz - 1;
	while (left<=right)
	{
    
    
		//int mid = (left + right) / 2;
		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[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int k = 0;
	scanf("%d", &k);//输入的是要查找的值
	//FindNum();
	int sz = sizeof(arr) / sizeof(arr[0]);
	int ret = binary_search(arr, k, sz);
	if (ret == -1)
	{
    
    
		printf("找不到\n");
	}
	else
	{
    
    
		printf("找到了,下标是:%d\n", ret);
	}
	return 0;
}

operation result:
insert image description here

  • Write a function that increases the value of num by 1 every time the function is called
1.
void Add(int* p)
{
    
    
	*p = *p + 1;
}

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;
}

2.
int Add(int n)
{
    
    
	return n + 1;
}
int main()
{
    
    
	int num = 0;
	num = Add(num);
	printf("%d\n", num);
	num = Add(num);
	printf("%d\n", num);
	num = Add(num);
	printf("%d\n", num);

	return 0;
}

insert image description here
Note: When to use value and address

  • When you want to change the actual parameters passed into the function through the operation in the function, then use call by reference.
  • If no changes are made to the actual parameters, then call by value is used.

5. Nested use 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.

int main()
{
    
    
	char arr1[20] = {
    
    0};//abc\0.....
	char arr2[] = "abc";

	printf("%d\n", strlen(strcpy(arr1, arr2)));//链式访问

	return 0;
}

case:

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

operation result:
insert image description here

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.

Case 1:
insert image description here
Case 2:
insert image description here
insert image description here

In the future, there will be more codes in the project, and the functions are generally placed in the .h file and declared in the .c file to implement!

Case three:
insert image description here

Why split the code into .c .h files?

  1. Collaboration
  2. code protection

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 procedure or function has a method of directly or indirectly calling itself 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.
Recursive strategy
Only 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

The simplest recursive case:

int main()
{
    
    
	printf("hehe\n");
	main();
	return 0;
}

operation result:
insert image description here
insert image description here

A stack overflow occurs

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

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

Code demo:

void Print(int n)
{
    
    
	if (n > 9)
	{
    
    
		Print(n/10);
	}
	printf("%d ", n % 10);
}
int main()
{
    
    
	int num = 0;
	scanf("%d", &num);//1234
	//Print能将参数的num的每一位打印在屏幕上
	Print(num);
	//Print(1234)
	//Print(123)       + 4
	//Print(12)    + 3
	//Print(1) +2
	//1
	return 0;
}

operation result·:
insert image description here
insert image description here

The reason why the function can be called
is recursive because when the function is called, it will maintain a function stack frame (an area on the memory).
The function call starts, the stack frame is created, the function call ends, and the stack frame is destroyed.

7.2.2 Exercise 2

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

Code demo:

#include <string.h>
//size_t 是一种类型,是无符号整型的
//size_t就是为sizeof设计的
//size_t 类型的数据打印的时候使用%zd
size_t my_strlen(char* str)
{
    
    
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str+1);
}
int main()
{
    
    
	char arr[] = "abc";
	size_t len = my_strlen(arr);//传递的是数组首元素的地址
	printf("%zd\n", len);
	return 0;
}

operation result:
insert image description here
insert image description here

7.3 Recursion and iteration

7.3.1 Exercise 3

Find the factorial of n. (overflow is not considered)
insert image description here

Code demo:

int Fac(int n)
{
    
    
	if (n <= 1)
		return 1;
	else
		return n* Fac(n - 1);
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int r = Fac(n);
	printf("%d\n", r);

	return 0;
}

operation result:
insert image description here

7.3.2 Exercise 4

Find the nth Fibonacci number. (overflow is not considered)
insert image description here

Code demo:

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);//20
	int ret = Fib(n);
	printf("%d\n", ret);
	return 0;
}

Running results:
insert image description here
But we found a problem;

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

why?

  • We found that many calculations of the Fib function are actually repeated during the calling process.

So how can we improve?

  • When debugging the Fac function, if your parameters are relatively large, an error will be reported: **stack overflow (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 stack space being opened up 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). This 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 is implemented in a non-recursive way

Find the factorial of n

int Fac(int n)
{
    
    
	int i = 0;
	int r = 1;
	for (i = 1; i <= n; i++)
	{
    
    
		r = r * i;
	}
	return r;
}

Find the nth Fibonacci number

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;
}

NOTE: When to use recursion:

If it is easy to think of using recursion, and the written code has no obvious defects, then we can use recursion.
But if the recursive code written has obvious problems, such as: stack overflow, low efficiency, etc., then we still use iterative methods to solve them.

This content has come to an end, I believe that you have gained a lot from reading this content. Looking forward to seeing you in the next article! ! !

Guess you like

Origin blog.csdn.net/2201_75642960/article/details/131861121