Elementary C Language - Functions (Part 2)

"The person who chases the light will eventually shine!" Today, let's continue to learn about functions.

5. Nested call and chain access of functions

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

5.1 Nested calls

#define _CRT_SECURE_NO_WARNINGS 1
//函数的嵌套调用
#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;
}

insert image description here
Note: Functions can be called nestedly, but not defined nestedly!

//函数不能嵌套定义
void test()
{
    
    
	void new_line()
	{
    
    
		printf("haha\n");
	}
}

insert image description here

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("abc"));//链式访问
	printf("%d\n", sizeof("abc"));
	return 0;
}

insert image description here

Note: sizeofIt is a unary operator that calculates the size of the space occupied by the data type. When calculating the space size of the string, it includes the position of the terminator \0; strlenit is The file #include <string.h>does not contain \0, that is, the length of the string before \0 is calculated.

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char arr1[20] = {
    
     0 };
	char arr2[] = "abc";
	printf("%d", strlen(strcpy(arr1, arr2)));
	return 0;
}

insert image description here

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	printf("%d", printf("%d", printf("43")));
	return 0;
}

By reviewing the data, we can findprintf函数的返回值是打印在屏幕上字符的个数。
insert image description here
insert image description here

6. Function declaration and definition

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//写一个函数实现两个数相加
int main()
{
    
    
	int num1 = 0;
	int num2 = 0;
	scanf("%d%d", &num1, &num2);
	//函数使用之前需要添加函数的声明
	int add(int x, int y);
	//函数的调用,传值调用
	int ret = add(num1, num2);
	printf("%d\n", ret);
	return 0;
}
//函数的定义
int add(int x, int y)
{
    
    
	return x + y;
}

insert image description here
Note: The function must be declared before it is used, otherwise a warning will appear!
insert image description here
Of course, some students may think that when we used the function before, we didn't declare it. Why didn't there be a warning?

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//写一个函数实现两个数相加
//函数的定义
int add(int x, int y)
{
    
    
	return x + y;
}
int main()
{
    
    
	int num1 = 0;
	int num2 = 0;
	scanf("%d%d", &num1, &num2);
	//函数的调用,传值调用
	int ret = add(num1, num2);
	printf("%d\n", ret);
	return 0;
}

insert image description here
We can find that the definition of the function is before the use of the function at this time, we need to pay attention to the definition of the function is also a special declaration.

6.1 Function declaration

1. Tell the compiler what a function is called, what are the parameters, and what is the return type. But whether it exists or not, the function declaration cannot decide.
2. The declaration of the function generally appears before the use of the function. 要满足先声明后使用.
3. The declaration of the function is generally placed in the header file.

6.2 Function definition

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

In the future, there will be more codes in the project, and functions are generally .h文件declared in and .c文件implemented in .

//add.h文件
#pragma once
//函数的声明
int add(int x, int y);
//add.c文件
#define _CRT_SECURE_NO_WARNINGS 1
//函数的定义
//函数具有外部链接属性
int add(int x, int y)
{
    
    
	return x + y;
}
//test.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "add.h"//通过#include" "引入自定义.h头文件
int main()
{
    
    
	int num1 = 0;
	int num2 = 0;
	scanf("%d%d", &num1, &num2);
	//函数的调用,传值调用
	int ret = add(num1, num2);
	printf("%d\n", ret);
	return 0;
}

Advantages of writing in separate files:
1. Facilitate multi-person collaboration
2. Protect code

7. Function recursion

7.1 What is recursion?

A programming technique in which a program calls itself is called recursion.
Recursion as an algorithm is widely used in programming languages. A process or function has a method of calling itself directly or indirectly in its definition or description. It usually transforms a large complex problem into a smaller 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.递归的主要思考方式在于:把大事化小。

7.2 Two Necessary Conditions for Recursion

  • There are constraints, and when this constraint is met, the recursion will not continue.
  • After each recursion, it gets closer and closer to this constraint.

7.2.1 Exercise 1

Exercise: 1: Accept an integer value (unsigned), and print each bit of it in order.
For example: Input: 1234 Output: 1 2 3 4

//递归
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void Print(int n)
{
    
    
	if (n > 9)
	{
    
    
		Print(n / 10);
	}
	printf("%d ", n % 10);
}
int main()
{
    
    
	int num = 0;
	scanf("%d", &num);
	Print(num);
	return 0;
}

insert image description here
The essence of recursion is recursion + regression, so how is this code implemented?
insert image description here

The reason why the function can be called and recursive is because the function maintains a function stack frame (an area on the memory) when it is called.函数调用开始,函数栈帧创建,函数调用结束后,栈帧销毁。

Assume here n=123(the numerical value is small and easy to draw) to show you the storage situation in the stack area:
insert image description here

7.2.2 Exercise 2

Exercise 2: Write a function that does not allow the creation of temporary variables, and find the length of the string.
If temporary variables are allowed, I believe all of us will think strlen函数of finding the length of the string.

#include <stdio.h>
#include <string.h>
int main()
{
    
    
	char arr1[] = "abc";
	int len = strlen(arr1);
	printf("%d\n", len);
	return 0;
}

insert image description here
Including the following piece of code, temporary variables are also used, which do not meet the requirements of the topic.

#include <stdio.h>
size_t my_strlen(char* str)
{
    
    
	size_t count = 0;//创建了临时变量,不满足题目要求
	while (*str != '\0')
	{
    
    
		str++;
		count++;
	}
	return count;
}
int main()
{
    
    
	char arr[] = "abc";
	size_t len = my_strlen(arr);
	printf("%zd\n", len);
	return 0;
}

So, now that temporary variables are not allowed in the title, how should we write this code? Here, we can use recursion to solve this problem.

#include <stdio.h>
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;
}

insert image description here
insert image description here

7.3 Recursion and iteration

7.3.1 Exercise 3

Exercise 3: Find the factorial of n (without considering overflow)

//求n的阶乘
#define _CRT_SECURE_NO_WARNINGS 1
#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;
}

insert image description here
insert image description here

However, there is a problem of stack overflow here, and this problem can be avoided if we write the program according to the following method.

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int Fac(int n)//将递归代码改为循环的方式
{
    
    
	int i = 0;
	int r = 1;
	for (i = 1; i <= n; i++)
	{
    
    
		r = r * i;
	}
	return r;
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret = Fac(n);
	printf("%d\n", ret);
	return 0;
}

insert image description here

7.3.2 Exercise 4

Exercise 4: Find the nth Fibonacci number. (Do not consider overflow)
斐波那契数列(Fibonacci sequence), also known as the golden section sequence, was introduced by the Italian mathematician Leonardo Fibonacci in 1202 using rabbit breeding as an example, so it is also called "rabbit sequence". ", refers to such a sequence:
insert image description here
this sequence starts from the third item, and each item is equal to the sum of the previous two items.

//斐波那契数列
#define _CRT_SECURE_NO_WARNINGS 1
#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);
	int ret = Fib(n);
	printf("%d\n", ret);
	return 0;
}

insert image description here

However, we found this to be problematic:

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

why? We found that many calculations are actually repeated during the call of the Fib function.
So how can we improve?

  • When debugging the factorial function, if your parameter is 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 quadruple recursion, it may lead to open space all the time, and eventually the stack space will be exhausted. This phenomenon is called stack overflow .

Here, we can change recursion to non-recursion:

#define _CRT_SECURE_NO_WARNINGS 1
#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;
}

insert image description here

Note: If it is easy to think of using recursion and the code written has no obvious defects, then recursion can be used. But if the recursive code written has obvious problems, such as: stack overflow, low efficiency, etc., we still need to use iteration (loop) to solve it.

To solve the problem of stack overflow:

  1. Write recursion as non-recursion.
  2. Use staticobject instead nonstatic 局部对象. In the design of recursive functions, staticobjects can be used instead nonstatic 局部对象(that is, stack objects), which can not only reduce the overhead generated and released during each recursive call and return nonstatic 对象, but static 对象also save the intermediate state of the recursive call, and can be used by each call layer access.

Well, the knowledge points about functions are over here, and the knowledge points related to C language will continue to be updated in the future. Welcome to continue to like, follow and comment! Everyone support the group!

Guess you like

Origin blog.csdn.net/qq_73121173/article/details/132008119
Recommended