【C】Function

Table of contents

【1】What is a function?

【2】Classification of functions in C language

【2.1】Library functions

【2.2】How to learn to use library functions

【2.3】Custom function

【3】Parameters of function

【3.1】Actual parameters (actual parameters)

【3.2】Formal parameters (formal parameters)

【4】Function call

【4.1】Call by value

【4.2】Call by address

【4.3】Practice

【5】Nested calls and chained access of functions

【5.1】Nested calls

【5.2】Chain access

【6】Declaration and definition of function

【6.1】Function declaration

【6.2】Function definition

【7】Function recursion

【7.1】What is recursion?

【7.2】Two necessary conditions for recursion

【7.3】Recursion exercise

【7.4】Recursion and iteration

【8】Function stack frame and destruction

【8.1】Premise

【8.2】Register

【8.3】Function stack frame creation

【8.4】Function stack frame destruction


【1】What is a function?

In mathematics we often see the concept of functions. But do you know the functions in C language?

        Wikipedia definition of function: subroutine

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

【2】Classification of functions in C language

  • Library Functions

  • Custom function 

【2.1】Library functions

Why are there library functions?

        We know that when we learn C language programming, we always want to know the result after writing a code, and want to print the result on our screen. At this time, we will frequently use a function: printing information to the screen in a certain format (printf) .

        In the process of programming, we will frequently do some string copy work (strcpy) .

        In programming, we also calculate, and we always calculate operations such as n raised to the kth power (pow) .

        Like the basic functions we described above, they are not business code. Every programmer may use it during our 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 programmers to develop software.

So how to learn library functions?

Here we take a brief look: www.cplusplus.com

To briefly summarize, the commonly used library functions in C language include:

  • IO function

  • String manipulation functions

  • Character manipulation functions

  • Memory operation function

  • time/date functions

  • Math functions

  • Other library functions

We refer to the documentation and learn several library functions: (Teach students how to use the documentation to learn library functions).

strcpy : cplusplus.com/reference/cstring/strcpy/?kw=strcpy

char * strcpy ( char * destination, const char * source );
// 描述:
	复制字符串
	将source指向的C字符串复制到destination指向的数组中,包括结束的null字符(并在该点停止)。
	为了避免溢出,destination指向的数组的大小应该足够长,以包含与source相同的C字符串(包括结束的null字符),并且不应该在内存中与source重叠。
// 参数:
	destination:指向要在其中复制内容的目标数组的指针。
	source:要复制的C字符串。
// 返回值:
	destination 返回

memset : cplusplus.com/reference/cstring/memset/?kw=memset

void * memset ( void * ptr, int value, size_t num );
// 描述:
	填充内存块,将ptr指向的内存块的前num个字节设置为指定的值(解释为unsigned char)。
// 参数:
	ptr:指向要填充的内存块的指针。
	value:需要设置的值。该值作为int类型传递,但函数使用该值的unsigned char转换来填充内存块。
	num:要设置为该值的字节数,Size_t是一个无符号整型。
// 返回值:
	返回PTR。

[Note] But one secret you must know about library functions is: when using library functions, you must include the header file corresponding to #include. Here, refer to the documentation to learn the above library functions in order to master the use of library functions.

【2.2】How to learn to use library functions

You need to learn how to use query tools:

【2.3】Custom function

If library functions can do everything, what do programmers need to do? All the more important are custom functions .

Custom functions have the same function name, return value type and function parameters as library functions.

But the difference is that we design these ourselves. This gives programmers a lot of room to play.

【Composition of function】

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

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

[Code Example] Write a function to find the maximum value of two integers.

#include <stdio.h>

int GetMain(int val1, int val2) {
	return val1 > val2 ? val1 : val2;
}

int main(int argc, char** argv) {
	int a = 10;
	int b = 20;

	int c = GetMain(a, b);
	printf("a = %d, b = %d(最大的那个值是%d)\n", a, b, c);
}

[Code Example] Write a function to exchange the contents of two integer variables.

#include <stdio.h>

void MySwap(int *val1, int *val2) {
	int temp = *val1;
	*val1 = *val2;
	*val2 = temp;
}
int main(int argc, char** argv) {
	int a = 0;
	int b = 0;

	scanf("%d %d", &a, &b);
	printf("Swap Front: a = %d, b = %d\n", a, b);
	MySwap(&a, &b);
	printf("Swap After: a = %d, b = %d\n", a, b);
	return 0;
}

【3】Parameters of function

【3.1】Actual parameters (actual parameters)

  • The parameters that are actually passed to the function are called actual parameters .

  • Actual parameters can be: constants, variables, expressions, functions, etc.

  • No matter what type of quantities the actual parameters are, they must have definite values ​​when making a function call so that these values ​​can be passed 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 (memory units allocated) when the function is called, they are called formal parameters. Formal parameters are automatically destroyed after the function call is completed. Therefore formal parameters are only valid within functions.

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

Here we analyze the actual parameters and formal parameters of the function:

[General transfer of parameters]

[Pointer parameter passing]

        Here you can see that when the Swap1 function is called, x and y have their own spaces 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 a function occupy different memory blocks respectively, and modifications to the formal parameters will not affect the actual parameters.

【4.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.

【4.3】Practice

[Exercise Question 1] Write a function that can determine whether a number is prime.

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

bool IsPrime(int n) {
	for (int i = 2; i <= sqrt(n); i++) {
		if (n % i == 0) {
			return false;
		}
	}

	return true;
}

// 判断是不是素数
int main(int argc, char** argv) {
	// 质数(Prime number),又称素数,指在大于1的自然数中,除了1和该数自身外,无法被其他自然数整除的数。
	// (也可定义为只有1与该数本身两个正因数的数)。
	int count = 0;
	for (int i = 101; i <= 200; i += 2) {
		if (IsPrime(i)) {
			count++;
			printf("%d ", i);
		}
	}

	printf("\n素数一共有:> %d\n", count);
	return 0;
}

[Exercise 2] Write a function to determine whether a year is a leap year.

bool IsLeapYear(int year) {
	if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
		return true;
	}

	return false;
}

// 写一个函数判断一年是不是闰年。
int main(int argc, char** argv) {
	int count = 0;
	for (int year = 1000; year <= 2000; year++) {
		if (IsLeapYear(year)) {
			printf("%d ", year);
			count++;
		}
	}
	printf("\n闰年一共有:> %d", count);
}

[Exercise Question 3] Write a function to implement binary search in an integer ordered array.

#include <stdio.h>
#include <stdbool.h>

int BinarySearch(int* buffer, int findK, int size) {
	int leftSub = 0;
	int rightSub = size - 1;

	while (leftSub <= rightSub) {
		int midSub = leftSub + (rightSub - leftSub) / 2;
		if (buffer[midSub] < findK) {
			leftSub = midSub + 1;
		}
		else if (buffer[midSub] > findK) {
			rightSub = midSub - 1;
		}
		else
		{
			return midSub;
		}
	}

	return -1;
}

int main(int argc, char** argv) {
	int buffer[] = { 1,2,3,4,5,6,7,8,9,10 };
	int size = sizeof(buffer) / sizeof(buffer[0]);
	int findK = 4;

	int resultSub = BinarySearch(buffer, findK, size);
	if (resultSub != -1) {
		printf("找到了->[%d]下标是[%d]\n", findK, resultSub);
	}
	else
	{
		printf("未找到[]下标!\n");
	}

	return 0;
}

[Exercise Question 4 ] Write a function that will increase the value of num by 1 every time this function is called.

void StaticAdd(int *num) {
	(*num)++;
}

int main(int argc, char** argv) {
	int num = 0;
	for (int i = 0; i < 10; i++) {
		StaticAdd(&num);
		printf("%d ", num);
	}

	return 0;
}

【5】Nested calls and chained access of functions

        [Explanation] Functions can be combined according to actual needs, that is, they can call each other.

        [Explanation] Functions can be called in nested ways, but they cannot be defined in nested ways.

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

【5.2】Chain access

[Explanation] Use the return value of one function as a parameter of another function.

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[20] = "hello";
	int ret = strlen(strcat(arr, "bit"));//这里介绍一下strlen函数
	printf("%d\n", ret);
	return 0;
}

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

【6】Declaration and definition of function

【6.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, and must be declared first and then used .

  • Function declarations are generally placed in header files.

【6.2】Function definition

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

  • Contents of test.h (where the declaration of the function is placed)

#ifndef __TEST_H__
#define __TEST_H__

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

#endif //__TEST_H__
  • Contents of test.c (implementation of placement function)

#include "test.h"
//函数Add的实现
int Add(int x, int y)
{
	return x+y;
}

【7】Function recursion

【7.1】What is recursion?

        The 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 directly or indirectly calling itself in its definition or description. It usually transforms a large and complex problem into a smaller problem similar to the original problem to solve. The recursive strategy only A small number of programs are needed to describe the multiple repeated calculations required in the problem-solving process, which greatly reduces the amount of program code.

The main way of thinking about recursion is to make big things smaller

【7.2】Two necessary conditions for recursion

  • There is a constraint, and when this constraint is met, the recursion will no longer continue.

  • Each recursive call gets closer and closer to this limit.

【7.3】Recursion exercise

[Exercise 1] Accept an integer value (unsigned) and print each bit of it in order.

#include <stdio.h>

void Print(int num) {
	if (num > 9) {
		Print(num / 10);
	}
	printf("%d ", num % 10);
}

int main(int argc, char** argv, char* enpv) {
	int num = 1234;
	// 递归函数调用
	Print(num);

	return 0;
}

[Exercise Question 2] The function written does not allow the creation of temporary variables. Find the length of the string.

int MyStrlen(char* str) {
	if (*str == '\0') {
		return 0;
	}
	else
	{
		return 1 + MyStrlen(str + 1);
	}
}

int main(int argc, char** argv, char* enpv) {
	int len = MyStrlen("abc");
	printf("%d", len);
}

【7.4】Recursion and iteration

[Exercise 3] Find the factorial of n. (overflow is not considered).

// 非递归方式
int Factorial(int n) {
	int ret = 1;
	for (int i = 1; i <= n; i++) {
		ret *= i;
	}
	return ret;
}

int main(int argc, char** argv, char* enpv) {
	int n = 5;
	printf("%d\n", Factorial(5));

	return 0;
}

// 递归方式
int Factorial(int n) {
	if (n <= 1)
		return 1;
	else
		return n * Factorial(n - 1);
}

int main(int argc, char** argv, char* enpv) {
	int n = 5;
	printf("%d\n",Factorial(5));

	return 0;
}

[Exercise 4] Find the nth Fibonacci number. (overflow is not considered).

// 非递归方式
int Fib(int n) {
	int a = 1;
	int b = 1;
	int c = 0;

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

	return c;
}

// 递归方式
int Fib(int n) {
	if (n <= 2)
		return 1;
	else
		return  Fib(n - 1) + Fib(n - 2);
}

int main(int argc, char** argv, char* enpv) {
	int n = 40;
	printf("%d\n", Fib(n));
}

But we found something wrong:

  • When using the fib (recursive version) function, it is particularly time-consuming if we want to calculate the 50th Fibonacci number.

  • Using the factorial (recursive version) function to find the factorial of 10,000 (regardless of the correctness of the result), the program will crash.

why?

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

So how to solve the above problems:

  • Rewrite recursion into non-recursion.

  • Use static objects instead of nonstatic local objects. In recursive function design, 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 during each recursive call and return, but static objects can also save the intermediate state of recursive calls. , and can be accessed by each calling layer

【hint】

  • Many problems are explained in recursive form simply because it is clearer than non-recursive form.

  • But iterative implementations of these problems are often more efficient than recursive implementations, although the code is slightly less readable.

  • When a problem is too complex to be solved iteratively, the simplicity of the recursive implementation can compensate for the runtime overhead.

【8】Function stack frame and destruction

【8.1】Premise

        Use the demonstration environment of VS2013. Do not use a compiler that is too advanced. The more advanced the compiler, the harder it is to learn and observe. At the same time, under different compilers, the stack frames during the function call process are slightly different. The specific details depend on Implemented by the compiler.

【8.2】Register

【8.3】Function stack frame creation

【8.4】Function stack frame destruction

        Destruction is based on the order in which function stack frames are created, and the creation process will be popped step by step. I will not delve into it in depth, as the principle is not very useful.

Guess you like

Origin blog.csdn.net/lx473774000/article/details/131535912