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 :

Paramètres de fonction


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    autre
    Inscrit en
    Juillet 2020
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Juillet 2020
    Messages : 14
    Par défaut Paramètres de fonction
    Bonjour à tous,
    Je souhaiterais créer une fonction avec un nombre variable d'argument, sans utiliser la bibliothèque stdarg. D'après les recherches que j'ai effectuées, il suffirait de récupérer l'adresse de la première variable passée en argument, puis augmenter la valeur du pointeur sur ce paramètre de la taille des paramètres passés en arguments. Donc en gros, faire ça :
    Code : 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
     
    #include <stdio.h>
     
    void f (int n, ...)
    {       //En partant du principe que n indique le nombre de paramètre passés en arguments
    	int *liste = &n+sizeof(int);
    	for(int i = 0; i<n; i++)
    	{
    		liste+=1;  //On récupère l'adresse de l'argument suivant
    		char carAct = *((char*) liste);
    		putchar(carAct);  //On affiche l'argument
    	}
    	putchar('\n');
    }
     
    int main (void)
    {
       f ((int)4, 'a', 'b', 'c', 'z');
       f ((int)7, 'x', 'v', 'a', 'm', 'e', 'a');
     
       return 0;
    }
    Et donc là, je devrais avoir en retour :
    abcz
    xvamea
    Alors qu'en fait, j'obtient ça :
    �a
    xva

    En modifiant le code de cette manière :
    Code : 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
    #include <stdio.h>
     
    void f (int n, ...)
    {       //En partant du principe que n indique le nombre de paramètre passés en arguments
    	int *liste = &n+sizeof(int);
    	for(int i = 0; i<n; i++)
    	{
    		liste+=1;  //On récupère l'adresse de l'argument suivant
    		char carAct = *((char*) liste);
    		printf("%i ", carAct);  //On affiche l'argument
    	}
    	putchar('\n');
    }
     
    int main (void)
    {
       f ((int)4, 'a', 'b', 'c', 'z');
       f ((int)7, 'x', 'v', 'a', 'm', 'e', 'a');
     
       return 0;
    }
    J'obtient :
    -1 0 97 0
    0 4 120 0 118 0 97

    Ce qui me semble malgrès tout assez étonnant...
    Notez qu'au final, si je remplace par :
    Code : 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
     
    void f (int n, ...)
    {       //En partant du principe que n indique le nombre de paramètre passés en arguments
    	int *liste = &n+sizeof(int)+1;
    	for(int i = 0; i<n; i++)
    	{
    		liste+=2;  //On récupère l'adresse de l'argument suivant
    		char carAct = *((char*) liste);
    		printf("%i ", carAct);  //On affiche l'argument
    	}
    	putchar('\n');
    }
     
    int main (void)
    {
       f ((int)4, 'a', 'b', 'c', 'z');
       f ((int)7, 'x', 'v', 'a', 'm', 'e', 'a');
     
       return 0;
    }
    Tout marche correctement tant que je ne dépasse pas les 5 arguments optionnels (donc la deuxième ne marche pas), et ce, quel que soit la taille des paramètres optionnels (à la légère différence qu'avec les long, il faut remplacer le sizeof int de la ligne 3 par sizeof long).
    Et c'est d'ailleurs ce qui me chiffonne surtout :
    Comment, peut on récupérer les valeurs d'adresses séparées de deux octets, alors que celles-ci en font 4 ou 8 (int ou long sur mon pc 64 bits)
    Donc si vous avez des idées sur ce qui cloche, je suis preneur. Merci !
    NB : le + 5 sert à compenser le fait que je crée des variables locales. Si il y avait 3 pointeurs locaux, j'aurais mis 13 (3*4+1).

  2. #2
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    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 835
    Billets dans le blog
    1
    Par défaut
    Bonjour

    Déjà petite erreur sur ton premier code, ce n'est pas "7" mais "6" (enfin si j'ai bien compris vu qu'il n'y a que 6 arguments). Enfin bon c'est pas ça qui empèche de trouver. Accessoirement le cast nuit plus qu'il n'aide (si tu ne le mets pas, il y a alors cast implicite par rapport au paramètre "n" de la fonction). Et quand on essaye de faire ce genre de tests, on évite au maximum tout ce qui peut-être flou comme la taille d'un int en essayant de forcer au maximum avec "short" ou "long".
    Ce qui m'amène d'ailleurs à une seconde remarque: si je remplace "int n" par "unsigned int n" ça "marchotte" encore (je veux dire que ça donne comme toi) mais si je mets "unsigned short" ou "unsigned long" là ça ne marche plus du tout. Ce qui laisse penser que ta règle "il suffirait de récupérer l'adresse de la première variable passée en argument, puis augmenter la valeur du pointeur sur ce paramètre de la taille des paramètres passés en arguments" n'est pas si vraie que ça.

    De là, moi je me suis demandé "y a-t-il vraiment une règle qui permette de trouver les emplacements de ces arguments ?"

    J'ai donc tapé 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
    16
    17
    18
    19
    20
    21
    22
    23
    #include <stdio.h>
     
    void f (unsigned long n, ...)
    {       //En partant du principe que n indique le nombre de paramètre passés en arguments
    	char *liste = (char*)&n;
    	char but[]="abc";
    	unsigned long cpt;
    	printf("liste=%p\n", liste);
    	for (cpt=0; cpt < 16*n && *liste != 'z' ; cpt++, liste++) {
    		char *pt;
    		for (pt=but; *pt != '\0'; pt++)
    			if (*liste == *pt) printf("liste=%p (%c), diff=%lu(%lu)\n", liste, *liste, liste-(char*)&n, cpt);
    	}
    	putchar('\n');
    }
     
    int main (void)
    {
       f (4, 'a', 'b', 'c', 'z');
       f (7, 'a', 'b', 'c', 'a', 'b', 'c', 'z');
     
       return 0;
    }

    Et le résultat
    Code : 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
    liste=0x7ffdabc081f8
    liste=0x7ffdabc08204 (a), diff=12(12)
    liste=0x7ffdabc08205 (b), diff=13(13)
    liste=0x7ffdabc08206 (c), diff=14(14)
    liste=0x7ffdabc08228 (a), diff=48(48)
    liste=0x7ffdabc08230 (b), diff=56(56)
     
    liste=0x7ffdabc081e8
    liste=0x7ffdabc081f4 (a), diff=12(12)
    liste=0x7ffdabc081f5 (b), diff=13(13)
    liste=0x7ffdabc081f6 (c), diff=14(14)
    liste=0x7ffdabc08218 (a), diff=48(48)
    liste=0x7ffdabc08220 (b), diff=56(56)
    liste=0x7ffdabc08228 (c), diff=64(64)
    liste=0x7ffdabc08230 (a), diff=72(72)
    liste=0x7ffdabc08238 (b), diff=80(80)
    Là un premier constat: au second appel, il aurait dû trouver un second cycle "abc" immédiatement après le premier et non pas 32 cases plus loin.

    Ensuite, je remplace char but[]="abc" par char *but="abc" (le tableau devient l'adresse d'une chaine statique dans le tas). A priori cela ne devrait rien changer. En fait j'ai en réalité tapé ça par habitude d'économiser quand je peux, ne pensant pas que cela ferait une différence. Toutefois cela en a fait une
    Le résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    liste=0x7ffc6daaf788
    liste=0x7ffc6daaf7b8 (a), diff=48(48)
    liste=0x7ffc6daaf7c0 (b), diff=56(56)
     
    liste=0x7ffc6daaf778
    liste=0x7ffc6daaf7a8 (a), diff=48(48)
    liste=0x7ffc6daaf7b0 (b), diff=56(56)
    liste=0x7ffc6daaf7b8 (c), diff=64(64)
    liste=0x7ffc6daaf7c0 (a), diff=72(72)
    liste=0x7ffc6daaf7c8 (b), diff=80(80)
    Donc là, il ne trouve plus du tout le 'c' du premier appel, ni le second 'c' du second appel. En fait, j'ai l'impression que le "abc" qu'il trouve en positions 48, 56 et 64 sont le "abc" de mon "but".

    Maintenant je remplace unsigned long n par unsigned short n et pareil pour cpt (le "but" est resté "char*"). Le résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    liste=0x7ffdce5735bc
    liste=0x7ffdce5735e8 (a), diff=44(44)
    liste=0x7ffdce5735f0 (b), diff=52(52)
    liste=0x7ffdce5735f8 (c), diff=60(60)
     
    liste=0x7ffdce5735ac
    liste=0x7ffdce5735d8 (a), diff=44(44)
    liste=0x7ffdce5735e0 (b), diff=52(52)
    liste=0x7ffdce5735e8 (c), diff=60(60)
    liste=0x7ffdce5735f0 (a), diff=68(68)
    liste=0x7ffdce5735f8 (b), diff=76(76)
    Ensuite je compile (j'ai oublié de dire que je suis avec gcc 8.3) avec l'option "-O2" qui est une demande d'optimisation. Et le résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    liste=0x7ffcc770e86c
     
    liste=0x7ffcc770e85c
    liste=0x7ffcc770e8a0 (c), diff=68(68)
    Donc voilà. Tout ceci pour dire qu'il y a tant de paramètres qui jouent (état de la pile entre autre), qu'on peut difficilement en tirer une conclusion aussi simple que "adresse du premier argument puis décalage de l'âge de ma grand-mère". Surtout que rien ne dit que le décalage ne se fait pas vers l'arrière

    Citation Envoyé par ViveLasm Voir le message
    sans utiliser la bibliothèque stdarg
    Quelque chose me dit que si quelqu'un s'est fait ch... à la développer, ce n'était pas pour rien
    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]

  3. #3
    Membre averti
    Homme Profil pro
    autre
    Inscrit en
    Juillet 2020
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Juillet 2020
    Messages : 14
    Par défaut
    Cc,
    Merci de ta réponse.
    Bon, j'ai un peu chercher sur comment faire, et en regardant la structure des fonctions traditionnelles, j'en suis arrivé à ça :
    Code : 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
     
    #include <stdio.h>
     
     //Fonction pour afficher 5 long, puis un nombre indéterminé de caractères
     /*Attention : ne fonctionne pas avec optimisation : trop de modification dans la structure du programme (et surtout des fonctions à nombre
     variable de paramètres) : manifestement, aucune sur les grosses fonctions à nombre invariable d'arguments*/
    void f (int n, ...)
    {       //En partant du principe que n indique le nombre de paramètre passés en arguments
    		/*Le passage d'argument se fait de la même manière que si l'on utilise des int (5 arguments suivent le premier, puis l'on saute pour
    		accéder aux suivants). De plus, il faut incrémenter liste à chaque fois de 2, et d'abord incrémenter l'adresse de liste de 9 (deux int 
    		pour liste, 7 pour la sauvegarde de la pile, enfin je suppose)*/
    		/*Plus explicitement, le saut d'adresse se fait par tranches de 4 : liste+1 => liste+4. Ensuite, passé les 5 premiers arguments, il faut 
    		faire un saut de 36 (je ne sais pas pour quoi) pour récupérer les arguments suivants. Ensuite, continuer à sauter de 2 en 2. Enfin, si
    		l'on met des arguments suplémentaires dans la fonction, il faut incrémenter liste (lors du premier saut et du deuxième) de la taille
    		de ces arguments. Exemple : 2 int* = 4 int => liste+4*/
    	int* RSP;
    	int* RBP;
    	asm("mov %%rbp, %%rbx"
    	:"=b" (RBP));
    	asm("mov %%rsp, %%rax"
    	:"=a" (RSP));
    	printf("%p\n", RBP);
    	printf("%p\n", RSP);
    	// NB : RSP : pointeur de pile, RBP : pointeur de base de la pile => Utile pour le debug
    	int *liste = &n+9+4;
    	for(int i = 0; i<n && i<5; i++)
    	{
    		liste+=2;  //On récupère l'adresse de l'argument suivant
    		long carAct = *((long*) liste);
    		printf("%li ", carAct);  //On affiche l'argument
    	}
    	liste+=40;
    	for(int i = 5; i<n; i++)
    	{
    		liste+=2;
    		char carAct = *((char*) liste);
    		printf("%c ", carAct);  //On affiche l'argument
    	}
    	putchar('\n');
    }
     
    int main (void)
    {
    	f ((int)7, 'a', 'b', 'c', 'z', 'a', 'a', 'b');
    	f ((int)7, 123456, 123456, 123456, 123456, 123456, 'c', 'z');
     
       return 0;
    }
    Ça marche bien tant qu'il n'y a pas d'optimisation. Je n'ai pas réussi à changer le code en cas d'optimisation, car la structure des fonctions traditionnelles ne changent pas... (sauf que le sp n’existe plus). Ensuite, je suis d'accord sur le fait que les bibliothèques ne sont pas là pour rien, mais il y a des fois où tu es contraint de t'en passer (Programmation d'os, embarqué...).
    Ensuite, normalement, il y a des normes entourant le passage d'argument aux fonctions en C. Mais malheureusement je n'ai rien trouvé dessus... Donc si tu as un peu de doc à me proposer je suis partant

  4. #4
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    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 835
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ViveLasm Voir le message
    Ensuite, je suis d'accord sur le fait que les bibliothèques ne sont pas là pour rien, mais il y a des fois où tu es contraint de t'en passer (Programmation d'os, embarqué...).
    Moui, effectivement je suis d'accord, aucune situation n'est pareille à une autre. Mais dans ce cas, peut-être alors essayer de rester dans les classiques (fonction standard, nb de paramètres connus, etc)
    Ou alors utiliser le système argc/argv. Ta fonction recevant alors un tableau et le nb de ses éléments. M'est avis que ça restera toujours plus sain que ce... clafoutis

    Citation Envoyé par ViveLasm Voir le message
    Donc si tu as un peu de doc à me proposer je suis partant
    Désolé si j'avais eu je t'aurais donné.
    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]

  5. #5
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 769
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 769
    Par défaut
    Citation Envoyé par ViveLasm Voir le message
    Ensuite, normalement, il y a des normes entourant le passage d'argument aux fonctions en C. Mais malheureusement je n'ai rien trouvé dessus... Donc si tu as un peu de doc à me proposer je suis partant
    x86 calling conventions, lien wiki en anglais

  6. #6
    Membre averti
    Homme Profil pro
    autre
    Inscrit en
    Juillet 2020
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : autre

    Informations forums :
    Inscription : Juillet 2020
    Messages : 14
    Par défaut
    Ok merci beaucoup à tout les deux !

  7. #7
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    18 260
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 18 260
    Par défaut
    Je souhaiterais créer une fonction avec un nombre variable d'argument
    Il faut que tu regardes du coté des fonctions variadiques, comme printf.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

Discussions similaires

  1. Réponses: 10
    Dernier message: 04/01/2006, 16h57
  2. liste énumérée de const. perso comme paramètre de fonction
    Par batou22003 dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 13/12/2005, 20h37
  3. Paramètres de fonction : pointeurs ou valeurs ?
    Par Xandar dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 30/11/2005, 16h50
  4. Retrouver les valeurs des paramètres des fonctions d'une DLL
    Par Bernard Martineau dans le forum Langage
    Réponses: 6
    Dernier message: 08/11/2005, 10h42
  5. Paramètre de fonction
    Par Reynald dans le forum Débuter
    Réponses: 6
    Dernier message: 05/01/2005, 21h48

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