[Langage C élémentaire] L'explication de fonction la plus complète sur l'ensemble du réseau d'œuvres épiques (marchandises sèches complètes + collection recommandée)

Bons conseils

Bonjour à tous, je suis Cbiltps. Dans mon blog, s'il y a des phrases difficiles à comprendre ou des points clés difficiles à exprimer avec des mots , j'aurai des photos . Alors mon blog avec des photos est très important ! ! !

Si vous êtes intéressé par moi s'il vous plaît voir mon premier blog !

Points clés de ce chapitre

Ce chapitre maîtrise principalement l'utilisation de base des fonctions et de la récursivité

  1. quelle est la fonction
  2. Fonctions de la bibliothèque
  3. fonction personnalisée
  4. paramètres de fonction
  5. appel de fonction
  6. Appels imbriqués et accès chaîné des fonctions
  7. Déclaration et définition de la fonction
  8. fonction récursive

début de texte


1. Qu'est-ce qu'une fonction ?


On retrouve souvent le concept de fonction en mathématiques. Mais comprenez-vous les fonctions en langage C ? Définition de fonction dans
Wikipedia :子程序

  1. En informatique, une sous-routine (en anglais : Subroutine, procedure, function, routine, method,
    subprogram, callable unit) est une certaine partie du code d'un grand programme, consistant en un ou plusieurs blocs d'instructions
    . Il est chargé d'accomplir une tâche spécifique et est relativement indépendant des autres codes.
  2. Généralement, il y aura des paramètres d'entrée et une valeur de retour, fournissant l'encapsulation du processus et le masquage des détails. Ces codes sont généralement intégrés sous forme de
    bibliothèques logicielles.

2. Classification des fonctions en langage C


1. Fonctions de la bibliothèque
2. Fonctions personnalisées

2.1 Fonctions de la bibliothèque

Pourquoi y a-t-il des fonctions de bibliothèque ?

  1. Nous savons que lorsque nous apprenons la programmation en langage C, nous avons hâte de connaître le résultat après l'écriture d'un code et voulons imprimer le résultat sur notre écran pour le voir. À ce stade, nous utiliserons fréquemment une fonction : afficher des informations à l'écran dans un certain format (printf).
  2. Dans le processus de programmation, nous effectuerons fréquemment un travail de copie de chaînes (strcpy).
  3. Lors de la programmation, nous allons calculer, et toujours calculer des opérations telles que n à la puissance k (pow).

Comme les fonctions de base décrites ci-dessus, elles ne sont pas du code métier. Chaque programmeur peut l'utiliser pendant le processus de développement.Afin de prendre en charge la portabilité et d'améliorer l'efficacité du programme , la bibliothèque de base du langage C fournit une série de fonctions de bibliothèque similaires pour faciliter le développement de logiciels par les programmeurs.

En bref, les fonctions de la bibliothèque couramment utilisées en langage C sont :

  • Fonctions IO ( fonctions d'entrée/sortie d'entrée et de sortie : printf scanf gethcar putchar ...)
  • Fonctions de manipulation de chaînes (strlen strcmp strcpy strcat ...)
  • Fonctions de manipulation de caractères (tolower toupper ...)
  • Fonctions de manipulation de la mémoire (memcpy memset memmove memcmp ...)
  • fonctions heure/date (heure ...)
  • fonctions mathématiques (sqrt asb fabs pow ...)
  • Autres fonctions de la bibliothèque

2.1.1 Comment apprendre à utiliser les fonctions de la bibliothèque

Alors, comment apprendre les fonctions de la bibliothèque ?
库函数不需要全部记住,需要学会查询工具的使用。

Tout d'abord, je recommande quelques outils de requête pour tout le monde (l'anglais est très important, il faut au moins comprendre la littérature) :

2.2 Fonctions personnalisées

Si les fonctions de la bibliothèque peuvent tout faire, que doivent faire les programmeurs ? Les fonctions personnalisées
sont d'autant plus importantes .

Comme les fonctions de bibliothèque, les fonctions personnalisées ont des noms de fonction, des types de valeur de retour et des paramètres de fonction.
Mais la différence est que ceux-ci sont tous conçus par nous-mêmes . Cela donne aux programmeurs beaucoup d'espace pour jouer.

La composition de la fonction :

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

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

Prenons un exemple :

Écrire une fonction pour trouver le maximum de deux nombres

#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)//形式参数
{
    
    
	return (x > y) ? (x) : (y);
}

int main()
{
    
    
	int num1 = 10;
	int num2 = 20;
	int max = get_max(num1, num2);//实际参数
	printf("max = %d\n", max);
	
	return 0;
}

Écrivez une fonction qui échange le contenu de deux variables entières.

Difficultés à surmonter : Par exemple, versez d'abord de la sauce soja dans une bouteille vide, puis versez du vinaigre dans la bouteille de sauce soja, et enfin versez de la sauce soja dans la bouteille de vinaigre.
directement sur la carte

#include <stdio.h>

void Swap1(int x, int y)
{
    
    
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;
}

void Swap2(int* px, int* py)
{
    
    
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}

int main()
{
    
    
	int num1 = 1;
	int num2 = 2;
	Swap1(num1, num2);//传值调用,在下面讲解
	printf("Swap1:num1 = %d num2 = %d\n", num1, num2);
	Swap2(&num1, &num2);//传址调用,下面讲解
	printf("Swap2:num1 = %d num2 = %d\n", num1, num2);
	
	return 0;
}

Les paramètres des fonctions Swap1 et Swap2 ci-dessus x, y, px, pysont tous des paramètres formels . Les paramètres réels sont passés à Swap1 dans la fonction main num1et num2passés à la fonction Swap2 &num1.&num2


3. Paramètres de fonction


3.1 Paramètres réels (paramètres réels)

Le paramètre réel passé à la fonction est appelé 实参.

Les arguments peuvent être : 常量, 变量, 表达式, 函数等.

Quel que soit le type de quantité que sont les paramètres réels, ils doivent avoir des valeurs définies lors de l'appel de la fonction, afin que ces valeurs puissent être transférées aux paramètres formels
.

3.2 Paramètres formels (paramètres formels)

Le paramètre formel fait référence à la variable entre parenthèses après le nom de la fonction , car le paramètre formel n'est instancié ( unité de mémoire allouée) que lorsque la fonction est appelée
, elle est donc appelée 形式参数.

Les paramètres formels sont automatiquement détruits à la fin de l'appel de la fonction.

Par conséquent, les paramètres formels ne sont valides qu'au sein de la fonction.

3.3 Conclusion sur les paramètres

Ouvrez la fenêtre mémoire, et nous analysons les paramètres réels et les paramètres formels du code précédent.
insérez la description de l'image ici
Ici, nous pouvons voir que lorsque la fonction Swap1 est appelée, x et y ont leur propre espace, et ont exactement le même contenu que les paramètres réels.

On peut donc simplement penser que : 形参实例化之后其实相当于实参的一份临时拷贝.


4. Appel de fonction


4.1 Appel par valeur

Les paramètres formels et les paramètres réels de la fonction occupent respectivement 对形参的修改不会影响实参des blocs de mémoire différents .

4.2 Appel par référence

  • L'appel par adresse est un moyen d'appeler une fonction en passant l'adresse mémoire de la variable créée en dehors de la fonction au paramètre de la fonction .
  • Cette façon de passer des paramètres peut établir une véritable connexion entre la fonction et les variables extérieures à la fonction , c'est-à-dire que les variables extérieures à la fonction peuvent être directement manipulées
    à l'intérieur de la fonction
    .

(Ceci est une explication supplémentaire) Concernant le passage de paramètres des fonctions : Lorsque j'écrivais la structure (passage de paramètres de structure) , j'ai cliqué dessus 函数传参的时候,参数是需要压栈的, et vous pouvez l'étendre.

4.3 Exercices

1. Écrivez une fonction pour juger des nombres premiers dans la plage

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

//判断n是否为素数
int is_prime(int n)
{
    
    
	//试除法,有两种方法
	//2~n-1
	//2~sqrt(n)
	int j = 0;
	for (j = 2; j <= sqrt(n); j++)
	{
    
    
		if (n % j == 0)
			return 0;
	}
	return 1;
}

int main()
{
    
    
	//100-200之间的素数
	int i = 0;
	int cnt = 0;
	for (i = 100; i <= 200; i++)
	{
    
    
		//判断i是否为素数
		if (is_prime(i) == 1)
		{
    
    
			printf("%d ", i);
			cnt++;
		}
	}
	printf("\ncount = %d\n", cnt);

	return 0;
}

2. Écrivez une fonction pour juger l'année bissextile dans la plage de jugement

#include <stdio.h>

//int is_leap_year(int y)
//{
    
    
//	if (y % 4 == 0)
//	{
    
    
//		if (y % 100 != 0)
//		{
    
    
//			return 1;
//		}
//	}
//	if (y % 400 == 0)
//		return 1;
//
//	return 0;
//}

// 优化后
//int is_leap_year(int y)
//{
    
    
//	if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
//		return 1;
//	else
//		return 0;
//}

//再优化后
int is_leap_year(int y)
{
    
    
	return (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0));
}

int main()
{
    
    
	int y = 0;
	for (y = 1000; y <= 2000; y++)
	{
    
    
		//判断y是否为闰年
		//函数
		//是闰年,返回1
		//不是闰年,返回0
		if (is_leap_year(y) == 1)
		{
    
    
			printf("%d ", y);
		}
	}

	return 0;
}

3. Écrire une fonction pour réaliser la recherche binaire d'un tableau ordonné d'entiers

#include <stdio.h>

int main()
{
    
    
	int arr[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int reft = 0;
	int right = sz - 1;
	int mid = 0;
	int k = 0;//要查找的元素
	int flag = 0;//找不到
	printf("请输入您要查找的数字:");
	scanf("%d", &k);
	while (reft <= right)//即使是 reft==right 也有一个元素需要查找
	{
    
    
		mid = (reft + right) / 2;//每次二分查找都要求出中间元素的下标
		if ((arr[mid] < k))
		{
    
    
			reft = mid + 1;
		}
		else if (arr[mid] > k)
		{
    
    
			right = mid - 1;
		}
		else
		{
    
    
			flag = 1;//找到了
			break;
		}
	}
	if (1 == flag)
	{
    
    
		printf("找到了,下标是%d\n", mid);
	}
	else
	{
    
    
		printf("没找到\n");
	}
	return 0;
}

4. Écrivez une fonction qui augmente la valeur de num de 1 chaque fois que la fonction est appelée

#include <stdio.h>

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

int main()
{
    
    
	int num = 0;
	Use(&num);
	printf("%d\n", num);
	Use(&num);
	printf("%d\n", num);
	Use(&num);
	printf("%d\n", num);

	return 0;
}

5. Appel imbriqué et accès en chaîne aux fonctions


5.1 Appels imbriqués

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

Les fonctions peuvent être appelées de manière imbriquée, mais les définitions ne peuvent pas être imbriquées.

5.2 Accès à la chaîne

Utiliser la valeur de retour d'une fonction comme argument d'une autre fonction (deux exemples)

//例子1:
#include <stdio.h>
#include <string.h>

int main()
{
    
    
	int len = strlen("abc");

	printf("%d\n", len);
	printf("%d\n", strlen("abc"));//链式访问

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

//解析:所以打印的是4321

6. Déclaration et définition de la fonction


6.1 Déclaration de fonction

  1. Indiquez au compilateur comment une fonction est appelée , quels sont ses paramètres et quel est son type de retour . Mais qu'elle existe ou non, la déclaration de la fonction ne peut pas décider.
  2. La déclaration d'une fonction apparaît généralement avant l'utilisation de la fonction. 要满足先声明后使用.
  3. Les déclarations de fonction sont généralement placées dans des fichiers d'en-tête.

Le contenu de xxx.h : la déclaration de la fonction de placement

//方法1:
#ifndef __TEST_H__
#define __TEST_H__
//函数的声明
int Add(int x, int y);
#endif

//方法2:完全等价的且简单
#pragam once//这段代码的意思是 防止头文件重复,多次的应用
//后面进行函数的声明
extern int Add(int x, int y);

6.2 Définition de la fonction

La définition d'une fonction fait référence à la réalisation spécifique de la fonction, expliquant la réalisation fonctionnelle de la fonction.

Le contenu de xxx.c : l'implémentation de la fonction de placement

//函数Add的实现
int Add(int x, int y)
{
    
    
	return x + y;
}

6.3 Idée de conception de sous-module

Cette forme d'écriture de sous-fichiers présente de nombreux avantages dans les projets de grande envergure :

  1. Collaboration
  2. encapsulation et masquage

Supposons qu'un morceau de code soit très compliqué et que nous ne voulions pas l'écrire nous-mêmes, nous pouvons acheter le code d'autres personnes, mais ils ne vous vendront pas le code source !
Mais les gens vont compiler et vous vendre quelque chose, qui est une bibliothèque statique (fichier . lib) .

6.3.1 Comment compiler le fichier .lib (voir directement l'image)

insérez la description de l'image ici
Le contenu du fichier .lib est une information binaire , puis le fichier .lib et le fichier d'en-tête vous seront vendus, afin que vous puissiez non seulement protéger votre propre code, mais aussi vous vendre la fonction, afin d'atteindre emballer et cacher!

6.3.2 Comment ajouter une bibliothèque statique

insérez la description de l'image ici
Alors lancez-vous !


7. Fonction récursivité et itération


7.1 Qu'est-ce que la récursivité ?

La technique de programmation dans laquelle un programme s'appelle s'appelle 递归( recursion).

La récursivité est largement utilisée comme algorithme dans les langages de programmation.

Un processus ou une fonction a une méthode directe ou indirecte dans sa définition ou sa description . Il convertit généralement un problème important et complexe couche par couche en un problème à plus petite échelle similaire au problème d'origine à résoudre . La stratégie récursive ne nécessite qu'une petite quantité de Le programme peut décrire les multiples calculs répétés requis dans le processus de résolution de problèmes , ce qui réduit considérablement la quantité de code dans le programme .调用自身

La principale façon de penser à la récursivité est :把大事化小 .

7.2 Deux conditions nécessaires à la récursivité

  • Il y a des contraintes, et lorsque cette contrainte est satisfaite, la récursivité ne continuera pas.
  • Cette limite se rapproche de plus en plus après chaque appel récursif.

7.2.1 Exercice 1 : (faire des dessins pour expliquer)

Prend une valeur entière (non signée) et imprime ses bits dans l'ordre.
Par exemple :
entrée : 1234, sortie 1 2 3 4.

Code de référence:

#include <stdio.h>

void print(unsigned int n)
{
    
    
	if (n > 9)//这个就是限制条件
	{
    
    
		print(n / 10);
	}
	printf("%d ", n % 10);
}

int main()
{
    
    
	unsigned int num = 1234;
	print(num);

	return 0;
}

Explication du dessin :

1. Difficultés à surmonter :
insérez la description de l'image ici
2. Idées d'analyse :
insérez la description de l'image ici

3. Explication supplémentaire :
que se passe-t-il si vous ne l'ajoutez pas au codeif (n > 9)//这个限制条件 ?
insérez la description de l'image ici
À ce stade, il sera bloqué lors de l'exécution, puis une erreur sera signalée lors du débogage.
Lorsqu'une récursivité se poursuit indéfiniment, une erreur est appelée 栈溢出.
insérez la description de l'image ici
Concernant la création et la destruction des cadres de pile de fonctions, je vais écrire un article en [Langage C avancé] pour expliquer, s'il vous plaît attendez-le avec impatience!
Le moment venu, postez le lien directement ! !

7.2.2 Exercice 2 : (dessiner et expliquer)

Écrivez une fonction qui ne permet pas la création de variables temporaires pour trouver la longueur de la chaîne.

Code de référence:

#include <stdio.h>

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

int main()

{
    
    
	char arr[20] = "abcd";//数组名arr是数组首元素的地址 — char*
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

Explication du dessin :

1. Surmonter les difficultés :
insérez la description de l'image ici
2. Pensée analytique :
insérez la description de l'image ici
3. Explication supplémentaire :

Suggestion : fonction au moment du design 能不创建全局变量就不创建全局变量.

7.2.3 Quelques problèmes classiques de récursivité

À propos de cette partie, je vais écrire un blog séparé pour vous apprendre à le faire !
Donc je ne vais pas trop en présenter, je posterai le lien directement plus tard, s'il vous plaît attendez-le avec impatience!

  1. Problème des tours de Hanoï
  2. grenouille qui saute les escaliers

7.3 Itération

Tout d'abord, utilisons la récursivité des fonctions pour répondre aux questions suivantes !

7.3.1 Exercice 3 :

Trouver la factorielle de n. (le débordement n'est pas pris en compte)

Référence du code clé :

int factorial(int n)
{
    
    
	if (n <= 1)
		return 1;
	else
		return n * factorial(n - 1);
}

7.3.2 Exercice 4 :

Trouvez le nième nombre de Fibonacci. (le débordement n'est pas pris en compte)

Référence du code clé :

int fib(int n)
{
    
    
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}

Mais nous avons trouvé un problème :

  1. Lors de l'utilisation de la fonction fib, elle est particulièrement chronophage si l'on veut calculer le 50ème nombre de Fibonacci.
  2. En utilisant la fonction factorielle pour trouver la factorielle de 10000 (indépendamment de l'exactitude du résultat), le programme plantera.

pourquoi ?
Nous avons constaté que de nombreux calculs sont en fait répétés lors de l'appel de la fonction fib.

Si on modifie un peu le code :

int count = 0;//全局变量
int fib(int n)
{
    
    
	if (n == 3)//第三个斐波那契数
		count++;
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}

Enfin, nous produisons le nombre, qui est une très grande valeur.

Le problème est que:

  • Lors du débogage de la fonction factorielle, si vos paramètres sont relativement grands, une erreur sera signalée : stack overflow(栈溢出)telle information.
  • L'espace de pile alloué par le système au programme est limité, mais s'il y a une boucle infinie, ou (récursion morte), cela peut conduire à la création d'espace de pile tout le temps, et finalement l'espace de pile sera épuisé. phénomène est appelé débordement de pile.

Alors, comment résoudre les problèmes ci-dessus:

  1. Réécrire récursivement en non récursivement.
  2. Utilisez des objets statiques au lieu d'objets locaux non statiques. Dans la conception de fonctions récursives, des objets statiques peuvent être utilisés à la place d'objets locaux non statiques (c'est-à-dire des objets de pile), ce qui non seulement réduit la surcharge de génération et de libération d'objets non statiques pour chaque appel et retour récursifs, mais enregistre également l'état intermédiaire. d'appels récursifs et accessible par chaque couche appelante.

Par exemple, le code suivant l'implémente de manière non récursive :

//求n的阶乘
int factorial(int n)
{
    
    
	int result = 1;
	while (n > 1)
	{
    
    
		result *= n;
		n -= 1;
	}
	return result;
}
//求第n个斐波那契数
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;
}

7.3.1 Résumé

  1. Lors de la résolution d'un problème à la fois récursif et non récursif peut être utilisé , et il n'y a pas de problème évident , alors utilisez la récursivité
  2. Lorsque la résolution d'un problème écrit de manière récursive est très simple et qu'il n'y a pas de problème évident , utilisez la récursivité
  3. Lorsque la résolution d'un problème de manière récursive est facile à écrire , mais qu'il existe des problèmes évidents , la récursivité ne peut pas être utilisée.

Enfin, laissez-moi vous rappeler :

  1. De nombreux problèmes sont expliqués sous une forme récursive simplement parce qu'elle est plus claire qu'une forme non récursive.
  2. Mais les implémentations itératives de ces problèmes sont souvent plus efficaces que les implémentations récursives, bien que le code soit légèrement moins lisible.
  3. Lorsqu'un problème est trop complexe pour être implémenté de manière itérative, la simplicité de l'implémentation récursive peut compenser la surcharge d'exécution qu'elle entraîne.

fin de texte

Je suppose que tu aimes

Origine blog.csdn.net/Cbiltps/article/details/119814668
conseillé
Classement