IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Comprendre les pointeurs


Sujet :

C

  1. #21
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Hulkabyl Voir le message
    mis a part la syntaxe qui diffère entre tableau et pointeur, qu'est que le programmeur gagne en utilisant les pointeur d'un point de vue optimal?
    Salut
    J'arrive en retard. Dommage, j'aime beaucoup les pointeurs.

    Bon, LittleWhite a très bien expliqué la nécessité, lorsque l'on désire faire modifier une variable par une fonction, de devoir passer l'adresse de cette variable à ladite fonction. La fonction recevant alors une adresse en paramètre devra la stocker dans un pointeur. Donc en fait, une première réponse à "pourquoi un pointeur" serait "chaque fois qu'on a une adresse à stocker"

    Maintenant, si on veut développer certaines de leurs utilités, parlons des tableaux et plus précisément, du traitement de ses éléments.

    Considère le tableau suivant: char nom[]="Jean-Paul" (petit rappel: une chaine est un tableau de caractères se terminant par le caractère spécial '\0').
    Imagine que tu désires l'afficher caractère par caractère. Tu pourras faire un truc ressemblant à ceci
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int i;
    for (i=0; nom[i] != '\0'; i++)
        printf("caractère %d: %c\n", i, nom[i]);

    Ce code fonctionne et, dans 99% des cas, contentera le programmeur lambda qui veut afficher son tableau.

    Toutefois il présente un petit inconvénient: il accède deux fois à nom[i]. Or, cet accès anodin se traduit, au niveau assembleur, par une suite d'opérations contenant un positionnement au début du tableau puis un décalage de "i" positions (tu remarques que je n'emploie pas le mot "caractère"). Ce décalage se décompose lui-même en plusieurs petites opérations qui intègrent la taille d'un élément (ici un octet) etc.
    Bref tout une multitudes d'opérations qui sont, ici, faites deux fois.

    Et c'est là que le pointeur entre en jeu. Il permet de mémoriser la position courante afin de ne pas avoir à la recalculer à chaque fois.
    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int i;
    char *pt;
    for (i=0, pt=nom; *pt != '\0'; i++, pt++)
        printf("caractère %d: %c\n", i, *pt);
    A la différence du code précédent c'est que pt est positionné en début de tableau. Puis il s'incrémente en parallèle avec i et ainsi, pointe exactement sur nom[i]. Mais l'accès à sa valeur ne nécessite plus toute la gamme d'opérations sus nommées ce qui optimise ainsi le temps d'accès à nom[i]

    Bon, ne nous leurrons pas. Ici, sur un petit tableau de caractères, l'optimisation est minime. Mais ce mécanisme peut aussi s'appliquer sur un élément plus complexe et donc plus gourmand: le tableau de structures.

    Imagine la structure suivante
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    struct s_personne {
        char nom[20];
        char prenom[20];
        int age;
    };
     
    struct s_personne personne[]={
        {"Eluard", "Paul", 38},
        {"Valjean", "Jean", 28},
        {"Rimbaud", "Arthur", 48},
        {"Ange gardien", "Joséphine", 28},
        {"", "", 0},
    };

    Tout comme dans l'exemple précédent, on peut l'afficher de cette façon
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int i;
    for (i=0; personne[i].age != 0; i++)
        printf("personne %d: nom [%s], prenom [%s], age [%d]\n", i, personne[i].nom, personne[i].prenom, personne[i].age);
    sauf que ici, l'accès à "personne[i]" est bien plus gourmand que tout à l'heure (un décalage dans un tableau de structures c'est mortel pour un code surtout qu'ici je fais ce décalage 4 fois !!!).

    On peut donc appliquer la même méthode
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int i;
    struct s_personne *pt;
    for (i=0, pt=personne; pt->age != 0; i++, pt++)
        printf("personne %d: nom [%s], prenom [%s], age [%d]\n", i, pt->nom, pt->prenom, pt->age);

    Ceci démontre déjà une première possibilité intéressante des pointeurs. LittlelWhite l'a un peu évoqué en parlant de tab[n] qui pouvait s'écrire *(tab+n) mais sans aller assez loin dans la raison d'une telle écriture. Accessoirement tu remarqueras la subtilité d'intégrer, dans mon tableau, une valeur neutre permettant de marquer la fin pour que mon code puisse la repérer (exactement comme le '\0' marque la fin d'une chaine).

    Il y en a une seconde qui l'est encore plus: la possibilité de créer des fonctions génériques => des fonctions qui ont un comportement théorique et généraliste qui peut s'appliquer sur des éléments que toi (programmeur), ne possède pas encore.
    Tu as parlé tout à l'heure de la somme des n premiers entiers (facile). Mais comment ferais-tu si tu voulais la somme des n premiers carrés ? la somme des n premiers nombres de Fibonnaci ? La somme des n premiers nombres de la suite de Syracuse ?

    Soit tu codes toutes ces sommes de façon distinctes (qui devront évoluer à chaque nouveau besoin), soit tu passes par une fonction générique "somme des n premiers [quelque chose]" en laissant la possibilité de remplir "quelque chose" plus tard. Et ça, ça se fait à travers un... pointeur de fonction !!!

    Ca veut dire que ta fonction "somme_universelle" devra alors être capable d'appeler une fonction "X" qui aura à charge de traiter l'entier pour renvoyer son carré, son cube, etc que la fonction "somme_universelle" pourra récupérer. Et cela ne peut se faire que si la fonction "somme_universelle" connait la fonctionX. Et elle ne peut la connaitre que par son adresse, adresse qu'elle stockera dans un "pointeur de fonction".

    Mais cela impose une seule règle: chacune des différentes fonctions "X" devra toujours être définie de la même façon (même nombre de paramètres, même type de retour). Ici c'est facile, chaque fonction X recevra un nombre entier, le travaillera et renverra un autre entier.
    Donc, chaque fonction X sera définie ainsi: int fonctionX(int).
    Ce qui amènera la conclusion suivante: un pointeur sur cette fonction X sera alors défini ainsi int (*ptF)(int) signifiant "ptF" est un pointeur sur une fonction recevant un entier, ainsi "*ptF" est une fonction utilisable recevant un entier.

    Et la fonction générique "sommeUniverselle" recevra deux paramètres
    • la hauteur des n premiers nombres à traiter
    • le pointeur de la fonction qu'elle devra appeler pour chaque entier


    Ce qui donnera le code suivant
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int somme_generique(int n, int (*ptF)(int))
    {
    	int res;				// Résultat de la somme
    	int i;					// Indice de boucle
     
    	 // Traitement des n premiers entiers
    	for (res=0, i=0; i < n; i++)
    	{
    		// Chaque nombre "i" sera traité par la fonction X et ce qu'elle renvoi sera incrémenté
    		res+=(*ptF)(i);
    	}
     
    	// Le calcul est fini, renvoi du résultat
    	return res;
    }

    Maintenant, si tu veux la somme des n premiers entiers, te suffit de créer la fonction "entier" renvoyant la valeur entière d'un nombre entier (lui-même quoi)
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int entier(int n)
    {
    	return n;
    }

    Puis d'appeler ta fonction magique en lui passant l'adresse de cette fonction "entier" en paramètre (l'adresse d'une fonction c'est son nom tel quel sans parenthèses mais tu as le droit si tu veux de mettre "&" devant si ça te parle mieux)
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main()
    {
    	printf("La somme de 10 premiers entiers est %d\n", somme_generique(10, entier   /* ou bien &entier si tu préfères */));
    }

    En recevant l'adresse de la fonction "entier", la fonction "somme_generique" pourra exécuter son code et récupérer ce qu'elle renvoie.

    Admettons que tu veuilles la somme des n premiers carrés, te suffit de définir la fonction renvoyant le carré d'un nombre
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int carre(int n)
    {
    	return n * n;
    }
     
    int main()
    {
    	printf("La somme de 10 premiers carrés est %d\n", somme_generique(10, carre));
    }

    Tu veux la somme des n premiers Fibonacci, te suffit juste de définir la fonction renvoyant la valeur de Fibonacci pour un nombre "n" (aussi complexe soit-elle)
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    int fibonacci(int n)
    {
    	int calc[3]={1, 1};
    	int i;
     
    	if (n < 2) return calc[n];
     
    	for (i=2; i <= n; i++)
    	{
    		calc[2]=calc[0]+calc[1];
    		calc[0]=calc[1];
    		calc[1]=calc[2];
    	}
    	return calc[2];
    }
     
    int main()
    {
    	printf("La somme de 10 premiers nombres de la suite de Fibonacci est %d\n", somme_generique(10, fibonacci));
    }

    Et on peut, summum du summum, faire du traitement en rafales avec des tableaux de structures intégant la fonction à appeler, son nom tel qu'il doit s'afficher et le nombre d'éléments à traiter

    Exemple (code complet, compilable et fonctionnel)
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    #include <stdio.h>
     
    typedef struct {
    	char *nom;
    	int (*ptF)(int);
    	int n;
    } t_traitement;
     
    int entier(int n)
    {
    	return n;
    }
     
    int carre(int n)
    {
    	return n*n;
    }
     
    int cube(int n)
    {
    	return n * carre(n);			// Juste pour la frime genre "je me la pête en montrant qu'on peut en plus réutiliser une fonction existante" parce que dans la vraie vie j'écrirais plutôt n*n*n (plus rapide)...
    }
     
    int fibonacci(int n)
    {
    	int calc[3]={1, 1};
    	int i;
     
    	if (n < 2) return calc[n];
     
    	for (i=2; i <= n; i++)
    	{
    		calc[2]=calc[0] + calc[1];
    		calc[0]=calc[1];
    		calc[1]=calc[2];
    	}
    	return calc[2];
    }
     
    int somme_generique(int n, int (*ptF)(int))
    {
    	int res;				// Résultat de la somme
    	int i;					// Indice de boucle
     
    	 // Traitement des n premiers entiers
    	for (res=0, i=0; i < n; i++)
    	{
    		// Chaque nombre "i" sera traité par la fonction X et ce qu'elle renvoi sera incrémenté
    		res+=(*ptF)(i);
    	}
     
    	// Le calcul est fini, renvoi du résultat
    	return res;
    }
     
    int main()
    {
    	t_traitement trt[]={
    		{"entier", entier, 10},
    		{"carré", carre, 15},
    		{"cube", cube, 20},
    		{"suite de Fibonacci", fibonacci, 30},
    		{NULL, NULL, 0},
    	};
    	t_traitement *pt;
     
    	for (pt=trt; pt->nom != NULL; pt++)
    		printf("Somme des %d premiers %s: %d\n", pt->n, pt->nom, somme_generique(pt->n, pt->ptF));
    }
    Tu veux rajouter une fonction dont tu veux la somme de ses valeurs ? Te suffit de programmer la fonction et de rajouter sa ligne dans le tableau "trt" et c'est tout.

    C'était la seconde et je pense aussi intéressante utilité des pointeurs surtout dans ce que tu as demandé, à savoir "gain de temps" et "optimisation"...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  2. #22
    Membre régulier Avatar de ekieki
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Avril 2014
    Messages
    34
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 65
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Enseignant Chercheur

    Informations forums :
    Inscription : Avril 2014
    Messages : 34
    Points : 103
    Points
    103
    Par défaut
    Citation Envoyé par Hulkabyl Voir le message
    il n'est pas si différent que l'exemple cité par Bktero



    La première fonction que j'ai apprise dans ma vie en informatique c'est la somme des n premiers entier en scheme , et qaund j'ai vraiment envie de comprendre quelque chose, je reviens a elle toujours.

    voici donc la fonction som qui calcule la somme des n premier entier jusqu'a n. Exemple: som(5) = 15. (...)
    Manque de chance, tu oublies, à la fin de la fonction, de retourner la valeur calculée. Au fait, la somme, ça se calcule direct
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int somme(int n) {
      return n*(n+1) / 2;
    }

Discussions similaires

  1. Je n'arrive pas à comprendre les "pointeurs"
    Par andrianiaina dans le forum Débuter
    Réponses: 7
    Dernier message: 26/03/2012, 14h35
  2. Réponses: 4
    Dernier message: 13/08/2004, 18h39
  3. [TTreeView] Problème avec les pointeurs d'objet
    Par BlackWood dans le forum Composants VCL
    Réponses: 2
    Dernier message: 02/07/2004, 14h31
  4. pointeurs, toujours les pointeurs :/
    Par giviz dans le forum C
    Réponses: 16
    Dernier message: 08/10/2003, 15h02
  5. Pb de débutant sur les pointeurs!!!
    Par benji17c dans le forum C
    Réponses: 6
    Dernier message: 30/09/2003, 17h50

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo