Introducción a las funciones en lenguaje c (20.000 palabras en sangre!!!!)

Directorio de artículos

función

Uno: ¿Qué es una función?

Definición de Wikipedia de una función : subrutina Es responsable de completar una tarea específica y es relativamente independiente de otro código. (2): Generalmente, hay parámetros de entrada y valores de retorno, que proporcionan la encapsulación del proceso y la ocultación de detalles. Estos códigos suelen estar integrados como bibliotecas de software.


Dos: Clasificación de funciones en lenguaje C

(1) Funciones de biblioteca: funciones proporcionadas dentro del lenguaje C.
(2) Funciones autodefinidas: funciones escritas por usted mismo.

1: función de biblioteca

(1): El significado de la existencia de funciones de biblioteca:

  1. Sabemos que cuando aprendemos a programar en lenguaje C, no podemos esperar a saber el resultado después de escribir un código y queremos imprimir el resultado en nuestra pantalla para verlo. En este momento, usaremos con frecuencia una función:
  2. Imprime información a la pantalla en un formato determinado (printf).
  3. En el proceso de programación, con frecuencia haremos algún trabajo de copia de cadenas (strcpy).
  4. En programación también calculamos, y siempre calculamos la operación (pow) de n elevado a k. **
  5. Para mejorar la eficiencia de nuestro trabajo y evitar la aparición de errores, hemos introducido funciones de biblioteca.

(2): Aprendizaje y uso de las funciones de la biblioteca

Al igual que las funciones básicas que describimos anteriormente, no son código comercial. En el proceso de desarrollo, todos los programadores pueden usarlo. Con el fin de admitir la portabilidad y mejorar la eficiencia del programa, se proporciona una serie de funciones de biblioteca similares en la biblioteca básica del lenguaje C, que es conveniente para que los programadores desarrollen software.

No es necesario memorizar el uso de las funciones de la biblioteca, podemos averiguar cómo se utilizan.

Se recomienda un sitio web y una aplicación aquí.

(1) www.cplusplus.com
(2) MSDN (Microsoft Developer Network)
(3) http://en.cppreference.com (versión en inglés)
(4) http://zh.cppreference.com (versión en chino)

aprobado De esta manera, podemos encontrar su información, como: nombre de la función, parámetros formales, archivos de encabezado requeridos y valores de retorno y otra información necesaria.

Los idiomas de estas herramientas están todos en inglés. En el proyecto de aprender programación, necesitamos aprender inglés.
Aquí hay dos ejemplos de funciones de biblioteca.

estresado

char * strcpy ( char * destination, const char * source );

inserte la descripción de la imagen aquí

El inglés anterior simplemente explica que el destino es el valor de retorno.
La comprensión más profunda es: después de
que la función strcpy copie los datos del origen en el espacio de destino, devolverá la dirección inicial del espacio de destino.

//strcpy的用法
#include<stdio.h>
#include<string.h>
int main()
{
    
    
	char arr1[20] = {
    
     "xxxxxxxxxxxxxxx" };
	char arr2[]   = {
    
     "hello bit" };
	strcpy(arr1, arr2);//arr1为目的地,arr2为源头,为了将arr2数组里面的内容拷贝到arr1里面
	printf("%s\n", arr1);
	return 0;
//arr1与t对齐的后面的x,也就是数组下标为9的x,被改成\0拷贝了过来
//实际上打印的结果为hello bit(\0不显示但存在)
}

inserte la descripción de la imagen aquí

memset (función de configuración de memoria)

void * memset ( void * ptr, int value, size_t num );

inserte la descripción de la imagen aquí
Suplemento: Este valor se refiere al número de bytes en num

//memset的用法
#include<stdio.h>
#include<string.h>
int main()
{
    
    
	char arr[] = {
    
     "hello bit" };
	memset(arr, 'x', 5);
	printf("%s\n", arr);
	return 0;
}
//memset中间一定是unsigned char类型,5则表示arr数组里面前五个东西被x所取代

Nota:
Pero un secreto que la función de biblioteca debe saber es: para usar la función de biblioteca, se debe incluir el archivo de encabezado correspondiente a #include.
Aquí, aprenderemos las funciones de biblioteca anteriores de acuerdo con los documentos, y el propósito es dominar el uso de las funciones de biblioteca.

2: función personalizada

(1): Composición de funciones personalizadas

Las funciones definidas por el usuario están diseñadas por programadores y tienen nombres de función, tipos de devolución, parámetros formales, etc., al igual que las funciones ordinarias.

La estructura básica es la siguiente:

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

(2): Preguntas de ejemplo

Ejemplo 1: escribe una función para encontrar el valor máximo de dos números enteros
//写一个函数找出两个整数的最大值
#include<stdio.h>
int get_max(int x, int y)
{
    
    
    if (x > y)
    {
    
    
        return x;
    }
    else
    {
    
    
        return y;
    }
    //简便的代码,一行代替上面函数体里面的代码
    //return(x > y ? x : y);
}
//从之前笔记一:函数是什么里面的定义得知-
//上述函数就是一个语句块,它负责完成某项特定的任务—求两个数的最大值

int main()
{
    
    
    int a = 0;
    int b = 0;
    scanf("%d %d", &a, &b);
    int c = get_max(a, b);
    printf("%d\n", c);
    return 0;
}
//输入:10 20
//输出:20
Ejemplo 2: escribe una función para intercambiar el contenido de dos variables enteras
//写一个函数交换两个整型变量的内容

//错误示范
#include<stdio.h>
void swap(int a, int b)
{
    
    
    int temp = 0;
    temp = a;
    a = b;
    b = temp;
}
int main()
{
    
    
    int a = 0;
    int b = 0;
    scanf("%d %d", &a, &b);
    printf("交换前:a=%d,b=%d\n", a, b);
    swap(a, b);
    printf("交换前:a=%d,b=%d\n", a, b);
    return 0;
}
//输入:10 20
//输出:
//交换前:a=10,b=20
//交换后:a=10,b=20
//没有达到相应的效果

Código correcto:

//正确代码
#include<stdio.h>
void swap(int* pa, int* pb)
{
    
    
    int temp = 0;
    temp = *pa;
    *pa = *pb;
    *pb = temp;
}
int main()
{
    
    
    int a = 0;
    int b = 0;
    scanf("%d %d", &a, &b);
    printf("交换前:a=%d,b=%d\n", a, b);
    swap(&a, &b);
    printf("交换前:a=%d,b=%d\n", a, b);
    return 0;
}
//输入:10 20
//输出:
//交换前:a=10,b=20
//交换后:a=20,b=10
//

El motivo del error, podemos ir directamente a la página de la solicitud de transferencia y se explicará claramente

Tres: Parámetros en la función

1: Parámetro real (parámetro real)

Los parámetros reales pasados ​​a la función se denominan parámetros reales.
Los argumentos pueden ser: constantes, variables, expresiones, funciones, etc.
Cuando se llama a una función, todas deben tener valores definidos para poder pasar esos valores a los parámetros formales.

Por ejemplo, get_max(a,b) y ab del código anterior son los parámetros reales y se pasarán a los parámetros formales xy en la función, respectivamente.

2: Parámetros formales (parámetros formales)

Los parámetros formales son variables entre paréntesis después del nombre de la función.
Los parámetros formales solo se instancian (unidades de memoria asignadas) cuando se llama a la función, por lo que se denominan parámetros formales. Entonces, los parámetros formales solo son válidos dentro de las funciones.

¡Recuerda que la siguiente oración es muy importante! ! ! ! !
Cuando se llama a la función, el parámetro real se pasa al parámetro formal, y el parámetro formal será una copia temporal del parámetro real, por lo que la modificación del parámetro formal no afectará al parámetro real. ¡Recuerde las palabras de que
el parámetro será una copia temporal del parámetro real ! ! !

Cuatro: llamada de función

Las funciones funcionan al ser llamadas. Ser llamado es para ser usado en otro código

1: llamada por valor

Los parámetros formales y los parámetros reales de la función ocupan diferentes bloques de memoria respectivamente, y la modificación de los parámetros formales no afectará a los parámetros reales.

Entonces, podemos usar la llamada por valor sin cambiar los argumentos de la función .

Tomemos un ejemplo para
escribir un programa para calcular la suma de dos números enteros:

#include<stdio.h>
int add(int x, int y)
{
    
    
    return x + y;
}
int main()
{
    
    
    int a = 0;
    int b = 0;
    scanf("%d %d", &a, &b);
    int c = add(a, b);
    printf("%d\n", c);
    return 0;
}

En este programa, a y b son parámetros reales. Solo usamos a y b para operar sin cambiar las propiedades de a y b. En este momento, podemos usar la llamada por valor y luego devolver el valor obtenido por la operación.

2: llamar por dirección

La llamada por referencia es una forma de llamar a una función pasando la dirección de memoria de una variable creada fuera de la función al parámetro de función .
Este método de pasar parámetros permite que la función establezca una conexión real con las variables fuera de la función, es decir, la función puede operar directamente las variables fuera de la función.

Usemos el código anterior para intercambiar dos números como ejemplo:

#include<stdio.h>
void swap(int* pa, int* pb)
{
    
    
    int temp = 0;
    temp = *pa;
    *pa = *pb;
    *pb = temp;
}
int main()
{
    
    
    int a = 0;
    int b = 0;
    scanf("%d %d", &a, &b);
    printf("交换前:a=%d,b=%d\n", a, b);
    swap(&a, &b);
    printf("交换前:a=%d,b=%d\n", a, b);
    return 0;
}

En este programa, cambiamos los valores de a y b, luego necesitamos usar la llamada por referencia, porque el cambio de los parámetros formales en la llamada por valor no afectará los parámetros reales.

A continuación, sabremos por qué está mal
, interpretemos en detalle los dos códigos de la función Swap.

//错误代码
#include<stdio.h>
void swap(int a, int b)//返回类型为void表示不返回,
//此处的int a与int b表示形式参数和它们的类型
{
    
    
    int temp = 0;//定义一个临时变量
    temp = a;//把a的值赋给temp
    a = b;//把b的值赋给a
    b = temp;//把temp的值赋给b,完成交换操作
    //注意,因为形参只是实参的一份临时拷贝,
    //在整个函数中我们改变的只是实参,
    //出函数后形参被销毁无法改变实参
}
int main()
{
    
    
    int a = 0;//创建变量a
    int b = 0;//创建变量b
    scanf("%d %d", &a, &b);//输入数值
    printf("交换前:a=%d,b=%d\n", a, b);//展示
    swap(a, b);//交换函数,将a,b传进去
    printf("交换前:a=%d,b=%d\n", a, b);//实参依旧是a和b的原始值,没有达到我们的目的
    return 0;
}

//正确代码
#include<stdio.h>
void swap(int* pa, int* pb)//void表示不返回,此处的int* pa与int* pb表示形式参数和它们的类型
{
    
    
    int temp = 0;//定义临时变量
    temp = *pa;//用地址找到实参a并赋给temp
    *pa = *pb;
    //把用地址找到的实参b赋给用地址找到的实参a
    *pb = temp;//用地址找到实参b并赋给temp
    //跳出函数时,被销毁的形参只是两个指针变量,此时实参的交换已经完成
}
int main()
{
    
    
    int a = 0;
    int b = 0;
    scanf("%d %d", &a, &b);
    printf("交换前:a=%d,b=%d\n", a, b);
    swap(&a, &b);//传入地址
    printf("交换前:a=%d,b=%d\n", a, b);
    return 0;
}

3: Practica

(1): Escribe una función para determinar si un número es primo o no.

//写一个函数可以判断一个数是不是素数。
#include<stdio.h>
#include<math.h>
int is_primer(int n)
{
    
    
	int j = 0;
	for (j = 2; j <= sqrt(n); j++)
	{
    
    
		if (n % j == 0)
		{
    
    
			return 0;
			//如果经过return 0,
			//则下一步往函数int is_primer(int n)的大括号走,
			//然后进入主函数is_primer(i)那里进行判断i是否等于1	
		}
	}
	//j没有任何一个数能整除n,说明它一定是素数,则返回1,否则返回0
	return 1;
}
int main()
{
    
    
	//打印100~200之间的素数
	int i = 0;
	for (i = 100; i <= 200; i++)
	{
    
    
		//判断i是否为素数
		if (is_primer(i) == 1)//如果是素数返回的结果是1,则令函数==1,将素数打印出来
		{
    
    
			printf("%d ", i);
		}
		//如果这个函数返回值不等于1
		//则继续进入主函数的for循环,然后for循环再进入if中判断,
		//判断完后,将i传到形式参数里面去
	}
	return 0;
}

Nota: Si la función no escribe un valor devuelto, la función devolverá un valor por defecto.Algunos compiladores devuelven el resultado de la última instrucción.

(2): Escribe una función para determinar si un año es bisiesto.

//写一个函数判断一年是不是闰年。
int is_leap_year(int year)
{
    
    
	if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
		return 1;
	else
		return 0;
}
//有一种更简单的方法,不需要用if...else
//int is_leap_year(int year)
//{
    
    
//	return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
//}
int main()
{
    
    
	int y = 0;
	int count = 0;
	for (y = 1000; y <= 2000; y++)
	{
    
    
		//判断y是不是闰年
		if (is_leap_year(y) == 1)
		{
    
    
			count++;
			printf("%d ", y);
		}
	}
	printf("\ncount = %d\n", count);
	return 0;
}

(3): Escriba una función para implementar una búsqueda binaria de una matriz ordenada de enteros.

//写一个函数,实现一个整形有序数组的二分查找
//错误代码
//#include<stdio.h>
//int binary_search(int* arr, int a)//地址传过去,得要指针接收,所以本质还是一个指针
//int binary_search(int arr[], int a)
//{
    
    
	//int left = 0;
	//int right = sizeof(arr)/sizeof(arr[0]) - 1;//right一直是0
	//上面的那一行代码是有逻辑错误的,
	//因为数组传参,传递的是数组首元素的地址
	//所以sizeof(arr)/sizeof(arr[0])=1,所以1-1=0
	//int mid = 0;
	//while (left <= right)
	//{
    
    
		//mid = left + (right - left) / 2;//找中间的元素,防止越界
		//if (arr[mid] > a)//中间元素大于查找值,就从右缩小一半的范围
		//{
    
    
			//right = mid - 1;//可以使用--mid,但不推荐
		//}
		//else if (arr[mid] < a)//中间元素小于查找值,就从左缩小一半的范围
		//{
    
    
			//left = mid + 1;//可以使用++mid,但不推荐
		//}
		//else
		//{
    
    
			//return mid;//找到了,返回下标
		//}
	//}
	//if (left > right) //正常情况下不会出现
	//{
    
    
		//return -1;//找不到,返回-1
	//}
//}
//int main()
//{
    
    
	//int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//int k = 0;
	//scanf("%d", &k);
	//找到了返回下标,0~9
	//找不到返回 -1(这里不能返回0,因为数组的第一个数字下标是0,如果return 0很有可能会对结果造成影响)
	//传数组,一个元素4个字节,数组中10个元素40个字节,
	//把40个字节全都移过去,如果int arr[]为了接收也创建了一个40字节的空间
	//这样空间会有大量的浪费,所以c语言有了以下规定:
	//数组传参,传递的是数组首元素的地址
	//int ret = binary_search(arr, k);
	//if (-1 == ret)
		//printf("找不到\n");
	//else
		//printf("找到了,下标是:%d\n", ret);
	//return 0;
//}
//正确代码(在主函数里面定义一个sz变量)
#include<stdio.h>
//地址传过去, 得要指针接收, 所以本质还是一个指针
//int binary_search(int* arr, int a, int sz)//数组传参,传递的是数组首元素的地址
int binary_search(int arr[], int a, int sz)//形参为数组、需要查找的整数、数组的元素个数
{
    
    
	int left = 0;
	int right = sz - 1;
	int mid = 0;
	while (left <= right)
	{
    
    
		mid = left + (right - left) / 2;//找中间的元素,防止越界
		if (arr[mid] > a)//中间元素大于查找值,就从右缩小一半的范围
		{
    
    
			right = mid - 1;//可以使用--mid,但不推荐
		}
		else if (arr[mid] < a)//中间元素小于查找值,就从左缩小一半的范围
		{
    
    
			left = mid + 1;//可以使用++mid,但不推荐
		}
		else
		{
    
    
			return mid;//找到了,返回下标
		}
	}
	if (left > right) //正常情况下不会出现
	{
    
    
		return -1;//找不到,返回-1
	}
}
int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	//只能在主函数里面定义sz变量,sizeof(arr)在主函数里使用不需要传递地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	int k = 0;
	scanf("%d", &k);
	//找到了返回下标,0~9
	//找不到返回 -1(这里不能返回0,因为数组的第一个数字下标是0,如果return 0很有可能会对结果造成影响)
	//数组传参,传递的是数组首元素的地址
	int ret = binary_search(arr, k, sz);//再多加一个sz变量,传到形参上去
	if (-1 == ret)
		printf("找不到\n");
	else
		printf("找到了,下标是:%d\n", ret);
	return 0;
}

(4): Escriba una función que aumente el valor de num en 1 cada vez que se llama a la función

#include<stdio.h>
void Add(int* p)//在主程序内定义一个变量储存调用的次数,因为需要改变变量的值,所以进行传址调用
{
    
    
    printf("hehe\n");
    (*p)++;//解引用找到变量再加1,注意这个括号不能忘
    //否则,*p++就表示每次这个指针先向后移动4个字节,然后解引用
}
int main()
{
    
    
	int num = 0;
	Add(&num);
	return 0;
}

Cinco: llamadas anidadas y acceso encadenado de funciones

1. Llamadas anidadas

Las funciones pueden llamarse entre sí según sea necesario.
da dos ejemplos

#include<stdio.h>
int main()
{
    
    
    printf("Hello world\n");
    return 0;
}
//这是每一个初学者都会写的代码,我们先调用了main函数,
//然后在main函数的内部又调用了printf函数,这就是嵌套调用.(可以理解为复合函数)
#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;
}

Nota: Las funciones se pueden llamar definiciones anidadas, pero no anidadas.

2. Cadena de acceso

Tome el valor de retorno de una función como argumento para otra función .

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	int len = strlen("abcdef");
	printf("%d\n", len);
	//可以利用链式访问简化代码,如下:
	printf("%d\n", strlen("abcdef"));
	return 0;
}

El valor de retorno de la función strlen se convierte en el parámetro de la función printf , que conecta las dos funciones como una cadena, es decir, un acceso en cadena.

(1): ejemplo clásico de acceso en cadena

¿Cuál es la salida del siguiente programa?

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

Respuesta: 4321

Puntos de conocimiento importantes: el valor de retorno de la función printf es la cantidad de caracteres que imprime ,

Primero ingrese la función printf más externa

Esta capa de función requiere el valor de retorno de la función de la segunda capa printf("%d", printf("%d", 43))

La función printf de segundo nivel necesita el valor de retorno de la función de tercer nivel printf("%d", 43)

Ejecute primero la función de tercer nivel y, después de ejecutar la función printf("%d", 43) de tercer nivel , el número de caracteres de impresión devueltos es 2

Entonces queda así printf("%d", printf("%d", 2))

La segunda capa obtiene el valor devuelto 2, imprime 2, y en este momento la función de la segunda capa también devuelve el número de caracteres que escribió 1

Entonces se convierte en printf("%d", 1) y finalmente imprime 1,

También forma la salida de 4321

Seis: declaración y definición de funciones

1: Declaración de la función

1. Dígale al compilador cómo se llama una función, cuáles son los parámetros y cuál es el tipo de retorno. Pero si existe o no, no está determinado por la
declaración de la función.

2. La declaración de una función generalmente aparece antes del uso de la función. Para satisfacer la declaración antes de su uso.
3. Las declaraciones de funciones generalmente se colocan en archivos de encabezado.

2: Definición de la función

1. La definición de una función se refiere a la implementación específica de la función, y se explica la implementación de la función de la función. Es equivalente a los pasos que solemos dar para crear una función personalizada.

2. Las funciones no se pueden definir anidadas

3: El bloque de escritura del programa

Cuando escribimos código, podemos pensar: escribo todo el código en un archivo fuente, por lo que no es conveniente encontrarlo.

De hecho, tal hábito es perjudicial para el futuro desarrollo del programa.

Nuestra sociedad tiene su propia división del trabajo. Cuando desarrollamos un programa, a menudo solo necesitamos ser responsables de una parte de un proyecto grande, como una persona para escribir el programa principal, una persona para escribir las funciones, etc. La separación de partes de un proyecto hace que sea más rápido encontrar errores y corregirlos en consecuencia.

Por lo tanto, cuando escribimos una función, necesitamos una asignación de archivos como esta:

La declaración de la función, la definición del tipo, la inclusión del archivo de encabezado—el archivo de encabezado.hDefinición de la función
—el archivo fuente de la implementación de
la función.cCada función se puede escribir en estos dos archivos, o se pueden escribir varias funciones en dos en el archivo.

Por ejemplo, cuando escribimos un proyecto que es la suma de dos números, muchos programadores dividen el trabajo para completarlo.A
creó el archivo de encabezado add.h

#pragma once
//头文件中放:函数的声明,类型的定义,头文件的包含
int Add(int x,int y)

El pequeño archivo fuente add.c creado por B

int Add(int x,int y)
{
    
    
	return x + y;
}

prueba interna.c

#include "add.h"//引用头文件
int main()
{
    
    
	int a = 0;
	int b = 0;
	scanf("%d %d", &a. & b);
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

La ventaja de escribir código en módulos es que podemos ocultar código necesario para implementar archivos. Por
ejemplo, hay un programador llamado Zhang San.
Escribió un motor de juego en su tiempo libre. Una empresa quiere comprar su motor de juego y
Zhang San accedió a venderlo. Dáselo, pero no quiero mostrarle el código fuente, entonces, ¿qué debería hacer?
Lo hizo así:
Zhang San escribió estos dos códigos inserte la descripción de la imagen aquí
, asumiendo que trabaja en el entorno VS. , haga clic en el archivo de encabezado, agregue un elemento existente y establezca el Se agregaron los dos archivos de código anteriores.
Este motor de juego resultó ser un proyecto que Li Si quería comprar. inserte la descripción de la imagen aquí
Zhang San pensó: Puedo dejar que lo uses, pero No quiero que veas cómo se implementa,
así que hizo clicinserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Haga clic en Aplicar
Después de su uso, el código en add.c no se ejecutará.

inserte la descripción de la imagen aquí
Este archivo add.lib es la biblioteca estática del motor del juego escrita por Zhang San
. Después de que Li Si lo compró, lo abrió y miró el archivo add.lib. Era un montón de caracteres ilegibles,
por lo que Zhang San le dio una manual de instrucciones
El
inserte la descripción de la imagen aquí
código para test.c debería terminar luciendo así

#include "add.h"//引用头文件
#pragma comment(lib, "add.lib")//加载引用静态库
int main()
{
    
    
	int a = 0;
	int b = 0;
	scanf("%d %d", &a. & b);
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

Siete: función recursiva

1: ¿Qué es la recursividad?

La habilidad de programación del programa que se llama a sí mismo se llama recursividad.( Recursión es el programa que se llama a sí mismo )
.La recursividad es ampliamente utilizada en los lenguajes de programación como un algoritmo.
Un procedimiento o función tiene un método para llamarse directa o indirectamente a sí mismo en su definición o especificación
. Por lo general, transforma un problema grande y complejo en un problema más pequeño similar al problema original a resolver. La
estrategia recursiva
solo se puede resolver un pequeño número de programas. se utiliza para describir los cálculos repetidos necesarios para el proceso de resolución de problemas, lo que reduce en gran medida la cantidad de código del programa.

La forma principal de pensar en la recursividad es: hacer pequeñas las cosas grandes

Aquí hay una recursividad simple

#include<stdio.h>
int main()
{
    
    
	printf("hehe\n");
	main();
	return 0;
}
//死递归hehe,到最后程序因为栈溢出而自动跳出(挂掉了)

¿Cuándo ocurrirá el desbordamiento de la pila?
Cada llamada de función se aplicará al espacio de memoria en el área de la pila , y el código anterior se aplica de forma recursiva infinitas veces, lo que eventualmente conduce a un desbordamiento de la pila.

2: dos condiciones necesarias para la recursividad

Hay una restricción, cuando se cumple esta restricción, la recursividad no continúa.

Se acerca más y más a este límite después de cada llamada recursiva.

(1): Ejercicio 1: (Explicación del dibujo)

Toma un valor entero (sin signo) e imprime sus bits en orden

Por ejemplo: Entrada: 1234, Salida: 1 2 3 4

#include<stdio.h>
void print(unsigned int n)
{
    
    
	if (n > 9)//如果n是两位数的话
	{
    
    
		print(n / 10);//满足递归的两个必要条件
	}
	printf("%d", n % 10);
	
}
int main()
{
    
    
	unsigned int num = 0;
	//无符号整型用%u
	scanf("%u", &num);//1234
	print(num);//按照顺序打印num的每一位
	return 0;
}

Ahora analicemos el proceso recursivo

//递归的思考方式:大事化小
//
//print(1234)
//print(123) 4   
//print(12) 3 4
//print(1) 2 3 4
//1 2 3 4
//
print(1234);//这个函数从上到下,先递进后回归
//1234大于9,进入if语句,第一层
print(1234)
{
    
    
    if (n > 9)//n=1234,满足条件,进入if
    {
    
    
        print(123);
    }
    printf("%d ", n % 10);//第一层,n%10=4
}
//print(123)展开,n=123满足条件,继续进入下一层,接着递归
print(123)
{
    
    
    if (n > 9)//n/10=123,满足条件,进入if
    {
    
    
        print(12);
    }
    printf("%d ", n % 10);//第二层,n%10=3
}
//print(12)展开,n/10=1此时不满足条件,不会继续进入下一层的if语句
print(12)
{
    
    
    if (n > 9)//n=12,不满足条件,不进入if
    {
    
    
        print(1);
    }
    printf("%d ", n % 10);//第三层,n%10=2
}
print(1)
{
    
    
    if (n > 9)//n=1,不满足条件,不进入if
    {
    
    
        print(0);
    }
    printf("%d ", n % 10);//第三层,n%10=1
}
//递归的“递”此时已经完成,我们将这个代码整理一下,查看它时如何“归”的
print(1234)
{
    
    
    {
    
    
        {
    
    
            {
    
    
                printf("%d ", n % 10);//第四层,n%10=1
            }
            printf("%d ", n % 10);//第三层,n%10=2
        }
        printf("%d ", n % 10);//第二层,n%10=3
    }
    printf("%d ", n % 10);//第一层,n%10=4
}
//代码从第四层开始向外执行,故可以实现数字的按位打印
//输出:1 2 3 4

inserte la descripción de la imagen aquí
Agreguemos algunos puntos de conocimiento del marco de la pila de funciones (practique las habilidades internas) (el artículo del blogger está escrito en detalle y es fácil de entender, puede recopilarlo)

Estructura y destrucción de la pila de funciones

Aquí hablaré brevemente sobre
los tipos de registros mencionados anteriormente, recordémoslos ahora

eax: registro de propósito general, guarda datos temporales, a menudo se usa para valores de retorno
ebx: registro de propósito general, retención de datos temporales
ebp: registro inferior de la pila
edp: registro de la parte superior de la pila
eip: registro de instrucciones, que contiene la dirección de la siguiente instrucción de la instrucción actual

ebp y edp se utilizan para mantener marcos de pila de funciones

inserte la descripción de la imagen aquí
¿Cómo se llama exactamente la función? Primero presionamos F10 en VS, y luego hacemos clic en desensamblar, en este momento podemos ver el código ensamblador. Cancelamos "Mostrar nombres de símbolos" porque queremos observar el diseño específico de las direcciones de memoriainserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí
Consejos:
1. dword tiene cuatro bytes
2. Se inicializan 4 bytes cada vez, se inicializa un total de veces ecx y finalmente se inicializa el contenido de eax, 0CCCCCCCCch
3. empujar: empujar la pila: poner un elemento en la parte superior
de the stack pop: pop the stack : elimina un elemento de la parte superior de la pila

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí
A partir de esto podemos encontrar que, de hecho, no definimos parámetros formales en
absoluto. Pasamos los parámetros a través de las instrucciones mov, push (empuje los parámetros formales de b y c a [ebp+8], [ebp+0Ch( 12)]. )
Add(a,b) se pasa a la derecha y luego a la izquierda.
a' y b' (x e y) son en realidad copias temporales de los parámetros reales de a y b

inserte la descripción de la imagen aquí

Después de devolver z, la función comienza a destruir.Echemos un vistazo al código desensamblado.

00C213F1 5F   		pop   edi
00C213F2 5E   		pop   esi
00C213F2 5B   		pop   ebx
//pop三次 相当于之前连续push三次的逆过程,连续在栈顶弹出值分别存放到edi,esi,ebx中
//pop完后,这些空间没必要存在了,应该被销毁
00C213F4 8B E5 		mov	  esp,ebp
//mov将ebp赋给了esp,这个时候esp与ebp在同一个位置上

00C213F6 5D			pop   ebp
//把ebp pop出来之后ebp回到main函数的栈底,esp回到main函数的栈顶	

inserte la descripción de la imagen aquí
Como se muestra en la figura, esp vuelve a la parte superior de la pila de la función principal.

00C213F7 C3 		ret
//ret指令的执行,跳到栈顶弹出的值就是call指令的下一条指令的地址
//此时esp+4(pop了一下),栈顶的地址也弹出去了,esp指向了00C21450(call)的底部		

Después de llamar a la función Agregar, al regresar a la función principal, continúe ejecutando

00C21450 83 C4 08		add			esp,8
//回到call指令的下一条地址,esp直接+8,把x,y形参的空间释放,esp这个时候指向edi的顶端
00C21453 89 45 E0		mov			dword ptr [ebp-20h],eax
//把eax中的值,存放到[ebp-20h(也就是c)]里面去
//函数的返回值是由eax寄存器带回来的

Después de leer, podemos responder las siguientes preguntas.

  • ¿Cómo se crean las variables locales?
    Respuesta: Primero asigne el espacio del marco de pila para la función, inicialice una parte del espacio y luego asigne un poco de espacio en el espacio del marco de pila de las variables locales.
  • ¿Por qué el valor de una variable local es un valor aleatorio cuando no se inicializa?
    Respuesta: Si no se inicializa, dado que la variable local proviene del marco de pila de la función principal, el valor que contiene se ingresa aleatoriamente y el valor aleatorio se puede sobrescribir después de la inicialización.
  • ¿Cómo pasan parámetros las funciones? ¿Cuál es el orden en que se pasan los parámetros?
  • ¿Cuál es la relación entre los parámetros formales y los parámetros reales?
    Respuesta: Un parámetro formal es una copia temporal de un parámetro real.
  • ¿Qué sucede cuando se llama a la función?
    (3, 5) Respuesta: cuando desea llamar a una función, se ha presionado antes de que se llame, y los dos parámetros (a, b) se presionan de derecha a izquierda. Cuando ingresamos la función de parámetro formal, de hecho, en el marco de la pila de la función Agregar, los parámetros formales de la función se recuperan a través del desplazamiento del puntero.
  • ¿Cómo regresa la función después del final de la llamada a la función?
    R: Antes de la llamada, almacenamos la dirección de la siguiente instrucción de la instrucción de llamada, y almacenamos el ebp del marco de pila de función anterior de la función llamada por ebp.Cuando la función necesita regresar después de la llamada de función, aparece el ebp, puede encontrar el ebp de la llamada de función anterior, cuando el puntero baja (dirección alta), puede ir a la parte superior del esp, para poder volver al espacio de marco de pila de la función principal, después de eso, recordamos la instrucción de llamada La dirección de la siguiente instrucción, cuando regresamos, podemos saltar a la dirección de la siguiente instrucción de la instrucción de llamada, para que la función pueda regresar después de la llamada de función, y el valor de retorno es devuelto a través del registro eax.

(2): Ejercicio 2: (Dibujo para explicar)

A continuación se describen tres métodos para encontrar la longitud de una cadena:
Método 1:

//方法一
#include<stdio.h>
#include<string.h>
int main()
{
    
    
	char arr[] = {
    
     "abc" };
	int len = strlen(arr);
	printf("%d", arr);
	return 0;
}

Método 2: Usar funciones y bucles

//方法二
#include<stdio.h>
int my_strlen(char* str)
{
    
    
	int count = 0;
	while (*str != '\0')
	{
    
    
		count++;
		str++;
	}
	return count;
}
int main()
{
    
    
	char arr[] = {
    
     "abc" };
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

inserte la descripción de la imagen aquí

Método 3: recursividad (fácil, sin necesidad de crear variables temporales)

//strlen("abc");
//1+strlen("bc");
//1+1+strlen("c");
//1+1+1+strlen("");
//1+1+1+0=3
#include<stdio.h>
int my_strlen(char* str)
{
    
    
	if (*str != '\0')
	{
    
    
//如果第一个字符不是"\0",则字符串至少包含一个字符
//可以剥出来个1,str+1是下一个字符所指向的地址
//空字符第一个是"\0"
		return 1 + my_strlen(str + 1);
//注意:这里str++并不能代替str+1的作用
//我们把str+1之后的地址传下去了,而留下来str还是原来的str.
//因为str++是先使用再++,
//那么根据原理传进去的str还是原来的str(原来是a的地址,传进去还是a的地址)
//所以按照原理:++str能代替str+1的作用,但并不推荐这样做,
//因为如果递归回来之后使用str的话,留下来的str不是原来的str了.
	}
	else
		return 0;
}
int main()
{
    
    
	char arr[] = {
    
     "abc" };
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

inserte la descripción de la imagen aquí

El método 3 parece tener muy poco código, pero en realidad se repiten muchos cálculos dentro del programa.

3: Recursión e iteración de funciones

(1): ¿Qué es la iteración?

La iteración es en realidad repetición, y un bucle es un tipo especial de iteración.

(2): Ventajas y desventajas

En la recursividad de funciones, llamamos funciones capa por capa, lo que tiene la ventaja de requerir menos código y ser conciso . Pero hay dos desventajas principales: por un lado, una gran cantidad de cálculos repetidos ralentizan la velocidad de ejecución del programa , por otro lado, cada vez que se llama a la función, necesita abrir un espacio correspondiente en la pila y cuando la recursividad es demasiado profunda, puede ocurrir un desbordamiento de pila. (El espacio en el área de la pila se ha agotado y el programa no puede continuar)

Cuando usamos la iteración , el bucle no necesita llamar a muchas funciones, el cálculo repetido será mucho menor, la velocidad de ejecución de este programa será mucho más rápida, pero la cantidad de código de este programa será mucho mayor .

Encuentre el n-ésimo número de Fibonacci. (independientemente del desbordamiento)
//斐波那契数列
//1 1 2 3 5 8 13 21 34 55 ....
//方法一:递归法
//		 n<=2 1
//Fib1(n) 
//		 n>2 Fib1(n-1)+Fib1(n-2);//第三个数加第二个数
#include<stdio.h>
int Fib1(int n)
{
    
    
	//如果想知道某个斐波那契数究竟计算了多少次,可以设置一个全局变量count
	//if (n == k)//算第k个斐波那契数被计算了多少次
		//count++;
	if (n <= 2)
		return 1;
	else
		return Fib1(n - 1) + Fib1(n - 2);
}
//int count = 0;
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret1 = Fib1(n);//定义一个ret来接受上面函数的返回值
	printf("%d", ret1);
	//printf("count=%d\n", count);
	return 0;
}
encontrar el factorial de n
//求n的阶乘
//方法一:递归法
//1*2*3=Fac(3)
//1*2*3*4=Fac(3)*4
// 
//		 n<=1  1			
//Fac(n)
//		 n>1 n*Fac(n-1);

#include<stdio.h>
int Fac1(int n)
{
    
    
	if (n <= 1)
		return 1;
	else
		return n * Fac1(n - 1);
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret1 = Fac1(n);//定义一个ret来接受上面函数的返回值
	printf("%d", ret1);
	return 0;
}

Los dos códigos anteriores parecen ser muy simples, pero cuando realmente se ejecutan, encuentran un gran problema:

Cuando se usa la función Fib1, se requiere mucho tiempo si queremos calcular el número 50 de Fibonacci.

Use la función Fac1 para encontrar el factorial de 10000 (sin considerar la exactitud del resultado), el programa fallará

¿por qué?
Al usar la función Fib, cuando n=50, la luz n=46 se calcula (aparece) 3 veces, lo que hará que el programa se ejecute muy lentamente.
Al usar la función Fac1, cuando se calcula el factorial de 10000, aparecerá la información de desbordamiento de pila ( stack overflow ), el espacio de pila asignado por el sistema al programa es limitado, pero si hay un ciclo infinito, o (muerto recursividad), esto puede hacer que el espacio de la pila se abra todo el tiempo y, finalmente, el espacio de la pila se agote. Este fenómeno se denomina desbordamiento de la pila.

Entonces, ¿cómo corregimos este problema?
Cambiar recursión a no recursión
El código corregido es el siguiente

//斐波那契数列
//1 1 2 3 5 8 13 21 34 55 ....
//方法二:迭代  
#include<stdio.h>
int Fib2(int n)
{
    
    
	int a = 1;//
	int b = 1;//前两个数都是1
	int c = 1;//对斐波那契数列中前两个数之和的第三个数初始化
	//不能令c=0,如果输入的n小于2,那直接return 0了,很显然不行
	while (n > 2)//第三个斐波那契数的时候开始循环
	{
    
    
	//1 1 2 3 5 8 13 21 34 55 ....
	//
	//  
	//      a b c.....(斐波那契数前两个数之加赋给第三个数,以此类推...)
	//		  a b c....
	//			a b c....	  
		c = a + b; //斐波那契数前两个数之加赋给第三个数
		a = b;
		b = c;
		n--;
	}
	return c;
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret2 = Fac2(n);//定义一个ret来接受上面函数的返回值
	printf("%d", ret2);
	return 0;
}
//求n的阶乘
//方法二:迭代法
int Fac2(int n)
{
    
    
	int i = 0;
	int ret2 = 1;//阶乘初始化必然为1
	for (i = 1; i <= n; i++)
	{
    
    
		ret2 = ret2 * i;
	}
	return ret2;
}
int main()
{
    
    
	int n = 0;
	scanf("%d", &n);
	int ret2 = Fac2(n);//定义一个ret来接受上面函数的返回值
	printf("%d", ret2);
	return 0;
}

insinuación:

  1. Muchos problemas se explican en forma recursiva simplemente porque es más claro que la forma no recursiva.
  2. Pero las implementaciones iterativas de estos problemas tienden a ser más eficientes que las implementaciones recursivas, aunque el código es un poco menos legible.
  3. Cuando un problema es demasiado complejo para implementarlo de forma iterativa, la simplicidad de la implementación recursiva puede compensar la sobrecarga de tiempo de ejecución que impone.

4: El problema clásico de la función recursiva

(1): Problema de la Torre de Hanoi

una. Origen:
  La Torre de Hanoi (también conocida como la Torre de Hanoi) es un juguete educativo que se originó a partir de una antigua leyenda india. Cuando Brahma creó el mundo, hizo tres pilares de diamantes, sobre los cuales se apilaron 64 discos de oro en orden de abajo hacia arriba. Brahma ordenó al brahmán que reorganizara el disco en otro pilar en orden de tamaño, comenzando desde abajo. Y se estipula que el disco no se puede agrandar en el disco pequeño, y solo se puede mover un disco a la vez entre las tres columnas.

dos. La abstracción es un problema matemático:
  como se muestra en la figura a continuación, hay tres pilares A, B y C de izquierda a derecha, entre los cuales hay n discos apilados de pequeño a grande en el pilar A. Ahora se requiere mover el disco en el pilar A al C Cuando la columna sube, solo hay un principio durante el período: solo se puede mover una placa a la vez, y la placa grande no puede estar sobre la placa pequeña. número de veces de movimiento.

inserte la descripción de la imagen aquí

(1) n == 1

1er disco 1 A---->C suma = 1 vez

(2) norte == 2

1er conjunto No. 1 A---->B
2do conjunto No. 2 A---->C
3er conjunto No. 1 B---->C suma = 3 veces

(3) n == 3

1.er grupo N.° 1 A---->C
2.° grupo N.° 2 A---->B
3.er grupo N.° 1 C---->B
4.° grupo N.° 3A---->C
5. ° grupo N.° 1 B---->A
6.º conjunto N.º 2 B---->C
7.º conjunto N.º 1 conjunto A---->C suma = 7 veces

No es difícil encontrar la ley: el número de veces 2 de un disco se reduce en 1 a la potencia de 1

El grado de 2 de los 2 discos se reduce en 1 a la potencia de 2.
El grado de 2 de los 3 discos se reduce en 1
. . . . .
Grado 2 de n discos menos 1 elevado a la n-ésima potencia

Por lo tanto: el número de movimientos es: 2^n - 1

Análisis de Algoritmos

(paso 1) Si es un plato

Mueva la placa en la columna a directamente de a a c

de lo contrario

(Paso 2) Primero mueva las placas n-1 en la columna a a b (Figura 1) con la ayuda de c.

Ciertamente, no hay una columna c que no se pueda mover, y el parámetro de función conocido es hanoi (int n, char a, char b, char c).

Representa mover la placa de la columna A a la columna B con la ayuda de la columna C. Al llamar a la función aquí, se mueve n-1 en la columna A.

La placa se mueve al pilar B con la ayuda del pilar C. Entonces, aquí debe cambiar la posición hanoi (n-1, a, c, b).

inserte la descripción de la imagen aquí

(Paso 3) En este punto, el movimiento se completa como se muestra en la Figura 1, pero el movimiento aún no ha terminado. Primero, mueva la última placa (n-ésima) placa en la columna a a c (imagen 2)
inserte la descripción de la imagen aquí
(Paso 4) Finalmente , mueva la columna b Mueva las placas n-1 a c con la ayuda de a (Figura 3)

inserte la descripción de la imagen aquí

// 汉诺塔
#include<stdio.h>
void hanoi(int n, char a, char b, char c)//这里代表将a柱子上的盘子借助b柱子移动到c柱子
{
    
    
	if (1 == n)//如果是一个盘子直接将a柱子上的盘子移动到c
	{
    
    
		printf("%c-->%c\n", a, c);
	}
	else
	{
    
    
		hanoi(n - 1, a, c, b);//将a柱子上n-1个盘子借助c柱子,移动到b柱子
		printf("%c-->%c\n", a, c);//再直接将a柱子上的最后一个盘子移动到c
		hanoi(n - 1, b, a, c);//然后将b柱子上的n-1个盘子借助a移动到c
	}
}
int main()
{
    
    
	int n = 0;
	printf("请输入需要移动的圆盘个数:");
	scanf("%d", &n);
	hanoi(n, 'A', 'B', 'C');//移动A上的所有圆盘到C上
	return 0;
}

(2): pasos de salto de rana

(1) Descripción del problema
Una rana puede saltar 1 o 2 escalones a la vez. Halla el número total de saltos que la rana puede dar n escalones.

Tarea: ¿De cuántas maneras puede una rana subir n escalones?

Reglas: Las ranas pueden saltar uno o dos pasos a la vez.

(2) Análisis del problema
Cuando n = 1, hay una forma de saltar;
cuando n = 2, la rana salta un nivel y luego otro nivel; la rana salta directamente a dos niveles. Hay 2 formas de saltar,
cuando n = 3, la rana salta un nivel tres veces; la rana salta un nivel y luego dos niveles; la rana salta dos niveles y luego un nivel, hay 3 formas de saltar;
cuando n = 4, hay 5 Hay 8 clases de saltos;
cuando n = 5, hay 8 clases de saltos;  …

La ley es similar al juicio del final de la
inserte la descripción de la imagen aquí
recursión de la secuencia de Fibonacci (3)

Por ejemplo, una rana necesita saltar cinco escalones.

Para encontrar el número de cinco pasos, necesitamos encontrar los valores de cuatro pasos y tres pasos.

Para encontrar el número de cuatro pasos, necesitamos encontrar los valores de tres pasos y dos pasos, y uno de los valores no necesita recursividad.

Para encontrar el número de tres pasos, necesitamos encontrar el valor del segundo paso y el primer paso, y el otro valor no necesita recursión en este momento.

En este punto, nuestra recursión puede interrumpirse en este momento, por lo que obtenemos la base de juicio para la terminación de la recursión: cuando el número de discos en la columna 1 es menor o igual a uno, salta de la recursión.

//青蛙跳台问题
#include<stdio.h>
//或者运用void函数,但之前要定义一个全局变量count
//int count = 0;//创建全局变量来统计个跳法个数
//void frog_jump(int n)
//{
    
    
//	if (n == 0)//当台阶数为0是跳法个数加1
//		count++;
//	else if (n < 0);
//	else
//	{
    
    
//		frog(n - 1);
//		frog(n - 2);
//	}
int frog_jump(int n)
{
    
    
	//int sum = 0;
	if (1 == n)//等于1时,sum=1
	{
    
    
		return 1; //sum += 1
	}
	else if (2 == n)//等于2时,sum=2
	{
    
    
		return 2;//sum += 2;
	}
	else//大于3时,开始递归
	{
    
    
		return frog_jump(n - 1) + frog_jump(n - 2);//sum = frog_jump(m - 1) + frog_jump(m - 2);
	}
	//return sum;
}
int main()
{
    
    
	int n = 0;
	printf("请输入台阶的个数:");
	scanf("%d", &n);
	int ret = frog_jump(n);
	printf("一共有%d种跳法\n", ret);
	return 0;
}

(3): pasos de salto de rana (versión avanzada)

Pregunta (1): Érase una vez una rana que quería saltar los escalones para llegar a la cima. Si la rana podía saltar hasta 1 escalón a la vez, también podía saltar hasta 2 escalones... (n-1) nivel, n nivel. Entonces, ¿cuántos métodos de salto tiene la rana en total cuando salta el enésimo escalón? (La premisa es que habrá un método de salto de n pasos para n pasos)

(1) Ideas para resolver problemas:

Si n es 1 paso: f(1) = 1 (solo es posible un salto)
Si n es 2 pasos: f(2) = f(2 - 1) + f(2 - 2) (habrá dos métodos A de saltar pasos, 1 paso a la vez o 2 pasos a la vez)
Si n es 3 pasos: f(3) = f(3 - 1) + f(3 - 2) + f(3 - 3) (habrá ser tres El método de saltar pasos, 1, 2, 3 a la vez)
……
……
Si n es (n - 1) pasos:

f(n-1) = f((n-1)-1) + f((n-1)-2) + … + f((n-1)-(n-2)) + f((n -1)-(n-1))
f(n-1) = f(0) + f(1)+f(2)+f(3) + … + f((n-1)-1)
f (n-1) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)

Si n es n pasos:

f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(n-(n-1)) + f(nn)
f(n) = f( 0) + f(1) + f(2) + f(3) + … + f(n-2) + f(n-1)

Combinando los casos de f(n-1) y f(n) encontrarás que f(n) = f(n-1) + f(n-1), por lo que obtenemos: f(n) = 2*f (n-1).

#include<stdio.h>
int frog_jump_step(int n)
{
    
    
	if (n <= 1)
	{
    
    
		return 1;
	}
	else
		return 2 * frog_jump_step(n - 1);
}
int main()
{
    
    
	int n = 0;
	printf("请输入青蛙应该跳的台阶个数:");
	scanf("%d", &n);
	int sum = frog_jump_step(n);
	printf("一共有种%d种跳法\n", sum);
	return 0;
}

Problema (2): Una rana puede saltar hasta 1 escalones a la vez, y también puede saltar hasta 2 escalones... También puede saltar hasta n escalones. ¿De cuántas maneras puede la rana saltar una escalera de nivel m?

(1) Ideas para resolver problemas
La situación que es diferente a la anterior es que los últimos pasos del salto de la rana no son n pasos, por lo que puede haber un fenómeno de salto.
Reservar polinomio:
f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(nm)
f(n-1) = f(n-2) + f(n-3) + … + f(nm) + f(nm-1)
se simplifica a: f(n) = 2f(n-1) - f(nm-1)

#include<stdio.h>
int frog_jump_step(int n,int m)
{
    
    
	if (n > m)
	{
    
    
		return 2 * frog_jump_step(n - 1, m) - frog_jump_step(n - 1 - m, m);
	}
	else
	{
    
    
		if (n <= 1)
		{
    
    
			return 1;
		}
		else
			return 2 * frog_jump_step(n - 1);
	}	
}
int main()
{
    
    
	int n = 0;
	int m = 0;
	printf("请输入青蛙应该跳的台阶个数:");
	scanf("%d", &n);
	int sum = frog_jump_step(n, m);
	printf("一共有种%d种跳法\n", sum);
	return 0;
}

Supongo que te gusta

Origin blog.csdn.net/yanghuagai2311/article/details/125928613
Recomendado
Clasificación