C Language Notes: Functions

content

1. Classification of functions in C language:

1.1 Library functions

1.2 Custom Functions

Example 1: Write a function to find the maximum of two integers

Example 2: Write a function to swap the contents of two integer variables

1.2.1*** Need to learn the process of finding bugs: debugging ( F10 ) 

2. Parameters of the function

2.1 Actual parameters (actual parameters)

2.2 Formal parameters (formal parameters)

3. Function call:

3.1 Call by value

3.2 Call by address

4. Nested calls and chained access of functions

4.1 Nested calls

4.2 Chained Access

5. Declaration and Definition of Functions

5.1 Function declaration

5.2 Function Definition

6. Function recursion

6.1 Two necessary conditions for recursion

Supplement: Area division in memory

6.1.1 Exercise 1: Take an integer value (unsigned) and print its bits in order. (solve with recursion)

6.1.2 Exercise 2: Write a function that does not allow the creation of temporary variables and finds the length of a string.

6.1.3 Exercise 3: Use recursion to find the factorial of n (regardless of overflow).

6.1.4 Exercise 4: Find the nth Fibonacci number. (regardless of overflow).


1. Classification of functions in C language:

1. Library functions

2. Custom function

1.1 Library functions

In order to support portability and improve program efficiency, the basic library of C language provides a series of similar library functions (which are encapsulated in C language by common functions (such as printing data, finding string length, inputting data, etc.) ), which is convenient for programmers to develop software.

Recommend a website for learning library functions: www.cplusplus.com

After opening, you can query the library functions contained in each header file, as well as the usage and examples of library functions, etc.

For example: query strcpy function:

 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 .

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

IO function

String manipulation functions

character manipulation functions

memory manipulation functions

time/date functions

math function

Other library functions

Do library functions need to be memorized all?

Not really. But you need to learn how to use the query tool :

MSDN(Microsoft Developer Network)

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

http://zh.cppreference.com (Chinese version)

1.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 us. This gives programmers a lot of room to play.

The basic composition of the function:

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

Example 1: Write a function to find the maximum of two integers

#include <stdio.h>

int get_max(int x, int y)
  {
	 return (x>y)? (x):(y);
  }
/*或者
int get_max(int x, int y)
{
	int z = 0;
	if (x > y)
		z = x;
	else
		z = y;
	return z;
*/


int main()
{	
	int num1 = 0;
	int num2 = 0;

	scanf("%d %d", &num1, &num2);

	//get_max为自己创建的自定义函数
	int max = get_max(num1, num2);
	
	printf("max = %d\n", max);

	return 0;
}

Example 2: Write a function to swap the contents of two integer variables

Let's first look at an example of an error:

//错误实例
#include <stdio.h>
//bug:结果并没有交换
//找bug的过程叫:调试 F10 按F10一步一步向下走,遇到函数时按F11进入函数内部
void Swap(int a, int b)
 {
    int temp = 0;
    temp = a;
    a = b;
    b = temp;
 }
  

int main()
{	
    int num1 = 0;
	int num2 = 0;

	scanf("%d %d", &num1, &num2); 

	Swap(num1, num2);
	
	printf("num1 = %d num2 = %d\n",num1,num2);  /num1与num2的值并未发生交换

	return 0;
}

1.2.1*** Need to learn the process of finding bugs: debugging ( F10 ) 

Press F10 to go down step by step, and when you encounter a function, press F11 to enter the function

In the error instance:

It can be seen that when the swap function is executed, the values ​​of a and b are exchanged, but the values ​​of num1 and num2 are not exchanged

Look at the addresses of a, b, num1, and num2:

When the parameters num1 and num2 are passed to a and b, a and b open up space independently . The swap function does exchange the values ​​of a and b, but because a, b and num1, num2 are not in the same space , so num1 and The value of num2 does not change with ab.

 num1 and num2 in the example are called actual parameters - actual parameters a and b are called formal parameters - formal parameters

When an actual parameter is passed to a formal parameter, the formal parameter is a temporary copy of the actual parameter ==" Modifications to the formal parameter will not affect the actual parameter

So how to do this example correctly? 

Using a pointer: Pass in the addresses of num1 and num2 through the swap function. The address of num1 stored in pnum1, so that pnum1 points to num1; similarly, pnum2 points to num2. This makes the swap function connect inside and outside.

//正确示例
#include <stdio.h>

void Swap(int* pnum1, int* pnum2)
{
	int temp = *pnum1;
	*pnum1 = *pnum2;
	*pnum2 = temp;
}

int main()
{	
	int num1 = 0;
	int num2 = 0;

	scanf("%d %d", &num1, &num2);

	Swap(&num1, &num2);
	
  	printf("num1 = %d num2 = %d\n",num1,num2);

	return 0;
}

Summary: When you need to change some external variables inside the function, you need to use a pointer (the address of the variable passed into the function is entered)

2. Parameters of the function

2.1 Actual parameters (actual parameters)

The actual parameters passed to the function are called actual parameters.

Arguments can be: constants, variables, expressions, functions , etc.

Regardless of the type of quantities the actual parameters are, they must have definite values ​​in order to pass those values ​​to the formal parameters when the function is called.

2.2 Formal parameters (formal parameters)

Formal parameters refer to the variables in parentheses after the function name, because the 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 is complete. So formal parameters are only valid within functions.

After the formal parameter is instantiated, it is actually equivalent to a temporary copy of the actual parameter.

Exercise: Implementing Binary Search with Functions

//实现函数在arr数组中查找k
//若找到返回下标,找不到返回-1(因为数组下标可以为0,所以查找失败返回值采用-1)

#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) / 2;
		if (arr[mid] > k)
			right = mid - 1;
		else if (arr[mid] < k)
			left = mid + 1;
		else
			return mid;     
	}
	return -1;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int k = 7; //k为被查找的数字
	int ret = binary_search(arr, k, sz);
	
	if (ret == -1)
		printf("未找到指定元素");
	else
		printf("找到了,该元素下标是:%d", ret);
	return 0;
}

It should be noted that if sz = sizeof(arr) / sizeof(arr[0]) is used inside the binary_search function to calculate the length of the array, the search will fail:

#include <stdio.h>

int binary_search(int arr[], int k) 
{											
	int sz = sizeof(arr) / sizeof(arr[0]);							
	int left = 0;
	int right = sz - 1;

	while (left <= right)
	{
		int mid = (left + right) / 2;
		if (arr[mid] > k)
			right = mid - 1;
		else if (arr[mid] < k)
			left = mid + 1;
		else
			return mid;     
	}
	return -1;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int k = 7; //k为被查找的数字
	int ret = binary_search(arr, k);
	
	if (ret == -1)
		printf("未找到指定元素");
	else
		printf("找到了,该元素下标是:%d", ret);
	return 0;
}                          //未找到指定元素

The reason for the failure of the search is that when arr is passed to the binary_search function, only the address of the first element in the arr array is passed to the function. At this time, sizeof(arr) = 4, so the search fails.

When an array is passed as a parameter, the address of the first character of the array is passed in

So when the number of array elements needs to be calculated in the function, after the calculation is done outside the function, it is passed into the function.

3. Function call:

3.1 Call by value

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

3.2 Call by address

Call by reference is a way to call a function by passing the memory address of a variable created outside the function to the function parameter.

This method of passing parameters allows the function to establish a real connection with the variables outside the function , that is, the function can directly operate the variables outside the function.

Example: Write a function that increments the value of num by 1 each time the function is called

#include <stdio.h>

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

int main()
{
	int num = 0;

	Add(&num);
	printf("num = %d\n", num);  //1

	Add(&num);
	printf("num = %d\n", num);  //2
	return 0;
}

4. Nested calls and chained access of functions

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

4.1 Nested calls

Functions can be called nested, but not nested definitions

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

4.2 Chain access

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

#include <stdio.h>

int main()
{
    printf("%d", printf("%d", printf("%d", 43)));  //4321

    return 0;

 In MSDN, it can be found that the return value of the printf function is the number of characters printed, and a negative value is returned when an error occurs

5. Declaration and Definition of Functions

5.1 Function declaration

1. Tell the compiler what a function is called, what the parameters are, and what the return type is. But whether it exists or not is not determined by the function declaration.

2. The declaration of a function generally appears before the use of the function . To satisfy the declaration before use .

3. Function declarations are generally placed in header files .

The writing method in the textbook: (This writing method is generally not used)

#include <stdio.h>

//声明一下

//声明函数 - 告诉编译器有一个函数,名叫ADD,2个参数都是int类型,函数返回类型是int

int Add(int x, int y);

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

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

Writing in the project:

5.2 Function Definition

The definition of a function refers to the specific implementation of the function, and the function implementation of the function is explained.

6. Function recursion

The programming technique in which a program calls itself is called recursion.

Recursion as an algorithm is widely used in programming languages.

A procedure or function has a method in its definition or specification that calls itself directly or indirectly.

It usually converts a large and complex problem into a smaller problem similar to the original problem to solve. The recursive strategy only needs a small number of programs to describe the repeated calculations required for the problem-solving process. The code size of the program is reduced.

The main way of thinking about recursion is: make big things small

6.1 Two necessary conditions for recursion

Baseline case : There is a constraint , when this constraint is met, the recursion does not continue.

Keep pushing : getting closer and closer to this limit after each recursive call .

Supplement: Area division in memory

Every function call needs to allocate space.

Therefore, when writing recursive functions, you must pay attention to two necessary conditions, otherwise stack overflow may occur .

6.1.1 Exercise 1: Take an integer value (unsigned) and print its bits in order. (solve with recursion)

Analysis, when inputting 1234, you need to use the function to output 1 2 3 4 , then use the recursive simplification idea:

Assuming that the custom print function is print, then:   

1. When the incoming number is greater than or equal to two digits (>9), continue to divide     

2. When divided to the one digit, print              

The picture is:

                                                                    

code show as below:

#include <stdio.h>

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

int main()
{
    unsigned int num = 0;
    scanf("%d", &num);

    print(num);
    return 0;
}

6.1.2 Exercise 2: Write a function that does not allow the creation of temporary variables and finds the length of a string.

Create a version of the temporary variable (count):

#include <stdio.h>

int  my_strlen(char* str)
{
    int count = 0;//计数器
    while (*str != '\0')
    {
        count++;
        str++;
    }
    return count;
}

int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);//传入的是首字符的地址,所以用字符指针接收
    printf("%d\n", len);  //6
    return 0;
}

Version without creating temporary variables (recursive version) :

Recursive idea:

#include <stdio.h>

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

int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);//传入的是首字符的地址,所以用字符指针接收
    printf("%d\n", len);
    return 0;
}

6.1.3 Exercise 3: Use recursion to find the factorial of n (regardless of overflow).

Recursive idea:

n! = n * (n-1) , n>= 2;

n! = 1            , n<= 1;

int Fac(int a)
{
    if (a > 1)
        return a * Fac(a - 1);
    else
        return 1;
}

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fac(n);
    printf("%d\n", ret);
    return 0;
}

6.1.4 Exercise 4: Find the nth Fibonacci number. (regardless of overflow).

Fibonacci sequence: 1 1 2 3 5 8 13 21 34 55...

Rule: The nth number is equal to the sum of the first two numbers of n

Fib(n) = 1,                                   n <= 2

Fib(n) = Fib(n - 1) + Fib(n - 2),   n <= 2

Recursive idea:

#include <stdio.h>

int Fib(int a)
{
    if (a > 2)
        return Fib(a - 1) + Fib(a - 2);
    else
        return 1;
}

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fib(n);
    printf("%d\n", ret);
    return 0;
}

The Fibonacci recursion code without pruning operation is very inefficient. If the parameter is relatively large, it will report an error: stack overflow (stack overflow).

The stack space allocated by the system to the program is limited, but if there is an infinite loop, or (dead recursion), it may cause the stack space to be opened all the time, and eventually cause the stack space to be exhausted, that is, stack overflow.

How to solve the above problem:

1. Rewrite recursion to be non-recursive.

2. Use static objects instead of nonstatic local objects .

In recursive function design, static objects can be used to replace nonstatic local objects (ie stack objects), which can not only reduce the overhead of generating and releasing nonstatic objects each time recursive calls and returns are made, but also static objects can save the intermediate state of recursive calls , and can be accessed by various call layers.

Non-recursive version:

the first time:

a = 1,b = 1,c = a + b

the second time:

a = b,b = c,c = a + b

So the non-recursive code is:

//更简洁的代码
#include <stdio.h>

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

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fib(n);
    printf("%d\n", ret);
    return 0;
}


//初始构思的代码
#include <stdio.h>

int Fib(int n)
{
    int a = 1;
    int b = 1;
    int c = 1;
    int i = 0;
    if (n <= 2)
        return 1;
    for (i = 0; i < n - 2; i++) //因为逻辑上当n > 2时,才开始循环,所以n=3对应第1次循环 所以 
                          
                                //判断条件为i < n - 2
    {
        a = b;          
        b = c;
        c = a + b;
    }
    return c;
}

int main()
{
    int n = 0;
    scanf("%d", &n);
    int ret = Fib(n);
    printf("%d\n", ret);
    return 0;
}

summary:

1. Many problems are explained in recursive form simply because it is clearer than the 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 quite complex and difficult to implement iteratively, the simplicity of recursive implementation can compensate for the runtime overhead it brings.

Guess you like

Origin blog.csdn.net/m0_62934529/article/details/123219325