Tutoriel avancé sur le pointeur de niveau nounou - [langage C]

Dans le blog précédent, j'ai brièvement présenté les connaissances de base sur les pointeurs, la taille des pointeurs, les opérations, les pointeurs secondaires, etc. Ensuite, je vous emmènerai comprendre les pointeurs en profondeur, afin que chacun ait une bonne compréhension des pointeurs.


Table des matières

pointeur de caractère

tableau de pointeurs

 pointeur de tableau

Nom du tableau VS& nom du tableau

Comment utiliser les pointeurs de tableau

pointeur de fonction

tableau de pointeurs de fonction

pointeur vers un tableau de pointeurs de fonctions


pointeur de caractère

Dans le type de pointeur, nous avons vu un type de pointeur char*, qui est un pointeur de caractère, et le type objet pointé par ce type de pointeur est un caractère ;

Utilisation générale :

#include<stdio.h>

int main(void)
{
    char ch = 'w';
    char *pc = &ch;//*pc就是字符指针
    *pc = 'w';
    return 0;
}

Nous pouvons mieux comprendre le pointeur de caractère à travers un exemple : lorsqu'il n'est pas pratique pour nous d'utiliser ch pour modifier la valeur, nous pouvons utiliser le pointeur de caractère pour modifier indirectement le contenu, ce qui revient à tuer des gens avec un couteau.

Mais il existe une utilisation spéciale des pointeurs de caractères : vous pouvez utiliser des pointeurs de caractères pour créer directement une chaîne constante, telle que char*p = "abcd" ; à l'origine, nous utilisions un tableau de caractères pour créer une chaîne, mais des pointeurs de caractères peuvent également être créés Chaîne ; mais il y a une petite différence entre les deux : la chaîne pointée par *p est stockée dans la zone de code, tandis que le tableau de caractères est stocké dans la zone de pile.

int main(void)
{
	char arr[] = "abcdef";
	const char* p = "abcdef";//常量字符串
	printf("%s\n", p);
	printf("%c\n", *p);

	return 0;
}

 Lorsque nous utilisons le pointeur de caractère p pour imprimer la chaîne entière et déréférencer p, le résultat imprimé est le suivant :

 De cela, nous pouvons conclure que le pointeur de caractère p pointe sur l'adresse du premier élément de la chaîne, et non sur la chaîne entière.

À travers un exemple typique, nous pouvons voir la différence entre l'utilisation d'un tableau pour créer une chaîne et l'utilisation d'un pointeur de caractère pour créer une chaîne :

#include <stdio.h>
int main()
{
	char str1[] = "hello world.";
	char str2[] = "hello world.";
	const char* str3 = "hello world.";
	const char* str4 = "hello world.";
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");

	return 0;
}

À quoi devrait ressembler la sortie de la série de programmes ci-dessus ?

Ici, str3 et str4 pointent vers la même chaîne constante. C/C++ stocke les chaînes constantes dans une zone mémoire séparée. Lorsque plusieurs pointeurs pointent sur la même chaîne, ils pointent en fait sur le même bloc de mémoire. Mais lorsque vous utilisez la même chaîne constante pour initialiser différents tableaux, différents blocs de mémoire seront ouverts. Donc str1 et str2 sont différents, et str3 et str4 sont identiques.

C'est tout ce qu'il y a à savoir sur les pointeurs de caractères.


tableau de pointeurs

Qu'est-ce qu'un tableau de pointeurs ? On peut commencer par quelques analogies. Tableau d'entiers : un tableau qui stocke des entiers, tableau de caractères : un tableau qui stocke des caractères ; le tableau de pointeurs, comme son nom l'indique, est un tableau qui stocke des pointeurs

int* arr1[10] ; // Tableau de pointeurs d'entiers

char *arr2[4] ; // Tableau de pointeurs de caractères de premier niveau

char **arr3[5];//Tableau de pointeurs de caractères secondaires

On les appelle des tableaux de pointeurs.

 La fonction la plus couramment utilisée du tableau de pointeurs consiste à simuler un tableau à deux dimensions :

int main(void)
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	int* arr[] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

 Placez trois tableaux unidimensionnels du même type dans le tableau de pointeurs et utilisez le tableau de pointeurs pour accéder à la simulation d'un tableau bidimensionnel. Étant donné que le nom du tableau est l'adresse du premier élément du tableau, s'il existe plusieurs tableaux, nous pouvons placer plusieurs tableaux dans le tableau de pointeurs pour le traitement et imprimer ces tableaux un par un. Le résultat de l'exécution du programme est le suivant :

C'est du contenu pour le tableau de pointeurs. 


 pointeur de tableau

Les pointeurs de tableau sont des pointeurs ? Ou un tableau ?

La réponse est : pointeurs.

Nous connaissons tous les pointeurs entiers : int * pint ; des pointeurs qui peuvent pointer vers des données entières. Pointeur à virgule flottante : float * pf ; Un pointeur qui peut pointer vers des données à virgule flottante. Le pointeur de tableau doit être : un pointeur pouvant pointer vers un tableau.

Alors, lequel dans le code suivant est un pointeur de tableau ?

int* p1[10] ;

entier (*p2)[10] ;

Que sont p1 et p2 respectivement ?

Réponse : p1 est un tableau de pointeurs, p2 est un pointeur de tableau

Étant donné que la priorité de [] est supérieure à *, p2 est d'abord combiné avec *, indiquant que p2 est une variable de pointeur, puis il pointe vers un tableau de 10 entiers. Donc p2 est un pointeur pointant sur un tableau, appelé pointeur de tableau.


 

Nom du tableau VS& nom du tableau

Pour le tableau int arr[10];, que représente arr et que représente &arr ?

Nous savons que arr est le nom du tableau, représentant l'adresse du premier élément du tableau. &arr est-il superflu ? Regardons d'abord un morceau de code:

#include <stdio.h>
int main()
{
 int arr[10] = {0};
 printf("%p\n", arr);
 printf("%p\n", &arr);
 return 0;
}

 On peut voir que l'adresse imprimée par le nom du tableau et le nom du &array est la même, mais n'y a-t-il vraiment aucune différence entre arr et &arr ? La réponse est qu'il y a une différence.

arr est de type int* et &arr est de type int(*)[10] ; 

#include <stdio.h>
int main()
{
 int arr[10] = { 0 };
 printf("arr = %p\n", arr);
 printf("&arr= %p\n", &arr);
 printf("arr+1 = %p\n", arr+1);
 printf("&arr+1= %p\n", &arr+1);
 return 0;
}

 Selon le code ci-dessus, nous avons constaté qu'en fait, &arr et arr ont la même valeur, mais la signification devrait être différente.

En fait : &arr représente l'adresse de tout le tableau, pas l'adresse du premier élément du tableau. Le type de données de &arr est int (*)[10], qui est un type de pointeur de tableau et non int*. Et array +1 saute la taille de l'ensemble du tableau, donc la différence entre &arr+1 et arr+1 est de 40.


Comment utiliser les pointeurs de tableau

Comment utiliser le pointeur de tableau ?

Le pointeur de tableau pointe vers le tableau et l'adresse du tableau doit être stockée dans le pointeur de tableau, ce qui est généralement plus pratique à utiliser sur un tableau à deux dimensions.

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
void print_arr2(int (*arr)[5], int row, int col)
{
 int i = 0;
 for(i=0; i<row; i++)
 {
 for(j=0; j<col; j++)
 {
 printf("%d ", arr[i][j]);
 }
 printf("\n");
 }
}
int main()
{
 int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
 print_arr1(arr, 3, 5);
 print_arr2(arr, 3, 5);
 return 0;
}

 Nous avons utilisé respectivement la réception de tableaux bidimensionnels et la réception de pointeurs de tableau, qui peuvent tous deux réaliser le passage de paramètres de tableaux bidimensionnels, mais ce sont en fait des pointeurs par essence.

Pour le nom de tableau arr dans le programme ci-dessus, il représente l'adresse du premier élément, mais le premier élément du tableau à deux dimensions est la première ligne du tableau à deux dimensions, donc le arr passé ici est en fait équivalent au adresse de la première ligne, qui est unidimensionnelle L'adresse du tableau peut être reçue avec un pointeur de tableau.


pointeur de fonction

Comme son nom l'indique, un pointeur vers une fonction est un pointeur de fonction.

La fonction a-t-elle une adresse ? Si tel est le cas, nous pouvons certainement utiliser des pointeurs pour pointer vers l'adresse de la fonction. Regardons d'abord un morceau de code:

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}

Résultat de sortie :  deux adresses sont générées, toutes deux étant des adresses de la fonction de test, ce qui prouve que la fonction a une adresse, il doit donc y avoir un pointeur de fonction. Et nous savons que le nom de la fonction et le nom de la fonction & dans le pointeur de fonction ont la même signification, ici nous pouvons le distinguer du tableau.

Alors, quel type de pointeur devrions-nous utiliser pour recevoir le pointeur de fonction ? Voici comment nous pouvons écrire de manière analogue aux pointeurs de tableau :

Lorsque la fonction définie a été déterminée, nous utiliserons la valeur de retour et les paramètres de la fonction personnalisée pour imiter le pointeur de fonction. Par exemple:

entier ajouter(int x, int y)

{

        retourner x+y ;

}

//Définir int (*pf)(int, int) = &add; ou int (*pf)(int, int) = add;

annuler le test()

{

       .......

}

//Définir void(*pd)() = &test ou void(*pd)() = test;

Lorsque nous appelons une fonction, nous pouvons utiliser la méthode la plus primitive, écrire directement la fonction pour l'appeler ou utiliser un pointeur de fonction pour appeler la fonction.

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

int main(void)
{
	int (*pf)(int,int) = add;
	int r = add(3, 5);
	int m = pf(4, 5);
	printf("%d %d\n", r, m);

	return 0;
}

Ce programme utilise deux méthodes pour appeler la fonction d'ajout, qui peuvent toutes deux être appelées avec succès. Le résultat est le suivant : il  s'agit du pointeur de fonction, que nous utilisons généralement dans la fonction de rappel. Si vous ne connaissez pas la fonction de rappel, vous pouvez visiter la page d'accueil de l'auteur, qui contient une explication de la fonction de rappel. ! ! ! 


tableau de pointeurs de fonction

Un tableau est un espace de stockage pour stocker le même type de données. Le tableau de pointeurs est expliqué ci-dessus. Ensuite, l'adresse de la fonction doit être stockée dans un tableau. Ensuite, ce tableau est appelé un tableau de pointeurs de fonction. Comment définir un tableau de pointeurs de fonctions ?

//modèle

entier (*parr1[10])();

Parr1 est d'abord combiné avec [], indiquant que parr1 est un tableau, quel est le contenu du tableau ? est un pointeur de fonction de type int (*)().

Objet du tableau de pointeurs de fonction : table de transfert  

Lorsque nous programmons, nous devons définir certaines fonctions du même type, et lorsque la fonction principale appelle chaque fonction, les opérations de base sont à peu près les mêmes, si nous écrivons du code, il y aura un contenu redondant et une logique peu claire . À ce stade, nous utilisons un tableau de pointeurs de fonction, ce qui peut améliorer la lisibilité du code.

L'implémentation du calculateur peut alors se faire à l'aide d'un tableau de pointeurs de fonctions.

#include <stdio.h>
int add(int a, int b)
{
 return a + b;
}
int sub(int a, int b)
{
 return a - b;
}
int mul(int a, int b)
{
 return a*b;
}
int div(int a, int b)
{
 return a / b;
}
int main()
{
 int x, y;
 int input = 1;
int ret = 0;
 int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
 while (input)
 {
 printf( "*************************\n" );
 printf( " 1:add 2:sub \n" );
 printf( " 3:mul 4:div \n" );
 printf( "*************************\n" );
 printf( "请选择:" );
 scanf( "%d", &input);
 if ((input <= 4 && input >= 1))
 {
 printf( "输入操作数:" );
 scanf( "%d %d", &x, &y);
 ret = (*p[input])(x, y);
 }
 else
 printf( "输入有误\n" );
 printf( "ret = %d\n", ret);
 }
 return 0;
}

C'est plus clair que d'utiliser un langage de casse et rend le code concis et clair.


pointeur vers un tableau de pointeurs de fonctions

Un pointeur vers un tableau de pointeurs de fonction est un pointeur pointant vers un tableau, et les éléments du tableau sont tous des pointeurs de fonction ; alors comment le définir ?

Pour éviter toute confusion, je fais la distinction entre les deux définitions de module ci-dessus et les pointeurs vers des tableaux de pointeurs de fonction :

void test(const char* str)
{
 printf("%s\n", str);
}
int main()
{
 //函数指针pfun
 void (*pfun)(const char*) = test;
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
 return 0;
}

En fait, on peut ajouter des tableaux et des pointeurs pour continuer à poser, ce qui va devenir de plus en plus compliqué, mais si cela vous intéresse, vous pouvez continuer à explorer, je n'en dirai pas plus ici.

Ce qui précède est l'intégralité du contenu de Pointer Advanced. Merci d'avoir regardé. J'espère que vous pourrez souligner mes lacunes et mes erreurs. Je continuerai à m'améliorer avec votre soutien ! !

Je suppose que tu aimes

Origine blog.csdn.net/m0_74755811/article/details/131666246
conseillé
Classement