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 :

Questions en vrac sur le C.


Sujet :

C

  1. #1
    Membre expérimenté
    Avatar de Luke spywoker
    Homme Profil pro
    Etudiant informatique autodidacte
    Inscrit en
    Juin 2010
    Messages
    1 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant informatique autodidacte

    Informations forums :
    Inscription : Juin 2010
    Messages : 1 077
    Points : 1 742
    Points
    1 742
    Par défaut Questions en vrac sur le C.
    Salut les C,

    je me suis fait une petite liste de questions que j'aimerai poser plus ou moins importantes.

    1. Compilation avec option d'optimisation.

    Quand on ajoute l'option -O2 a la ligne de compilation que ce passe-t-il ?

    Le compilateur essaie de produire du code optimisé si je me trompe pas.

    Mais en quels termes ce code est optimisé ?

    Est-ce dangereux ? Car si l'on compile avec l'option -O2 le programme aura-t-il plus de chance de crasher ou de mal se comporter ?

    2. Variables globales.

    Je sais qu'il faut les éviter mais dès fois c'est nécessaire:

    je pense a une application GTK+3 ou l'on ne peut passer qu'un seule pointeur en argument aux callback et c'est donc pratique d'avoir une variable globale comme celle-ci:

    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
     
    #include <gtk/gtk.h>
     
    typedef struct {
     
      GtkWidget *button ;
      GtkWidget *label   ;
     
      // etc..
    } GUI ;
     
    GUI pgui ;
     
    GUI gui = &pgui ;
     
    int main(int argc, char *argv[]) { return 0 ;  }
    Mais le problème vient a la compilation car si je veut faire une compilation en passant par des fichiers objets (*.o) cela m'est impossible pour toute fonction qui utilise la variable globale, impossible d'inclure un en-tête et compiler un fichier objet global_vars.c ne fonctionne pas...

    3. qualificateur const.

    Il ma été dit qu'il été inutile de déclarer un argument de fonction comme constant car il est passer par copie ???

    4. return nécessaire.

    Il m'a également dit qu'il était inutile de mettre un return a la fin d'une fonction renvoyant void ???

    5. valeur de retour dans un #define.

    Est il possible de faire retourner une valeur dans une définition ?

    Je pense a quelque chose comme cela:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    #define GET_NUM ({ do { return 1 ; } while(0) ; })
    Personnellement je n'y arrive pas.

    6. exec()

    Est-il possible de récupérer la stdout, stderr d'un appel a la famille exec(...) ou autre fonction exécutant une commande dans le terminal ?

    Merci pour vos réponses éclairées.
    Pour faire tes armes:
    Use du présent pour construire ton futur sinon use de ce que tu as appris auparavant.
    Et sois toujours bien armé avant de te lancer.
    Le hasard ne sourit qu'aux gens préparés...
    Site: Website programmation international (www.open-source-projects.net)
    Site: Website imagerie 3D (www.3dreaming-imaging.net)
    Testez aux moins pendant une semaine l'éditeur avec terminaux intégrées it-edit Vous l'adopterai sûrement !
    FUN is HARD WORK !!!

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    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 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par Luke spywoker Voir le message
    1. Compilation avec option d'optimisation.

    Quand on ajoute l'option -O2 a la ligne de compilation que ce passe-t-il ?

    Le compilateur essaie de produire du code optimisé si je me trompe pas.

    Mais en quels termes ce code est optimisé ?

    Est-ce dangereux ? Car si l'on compile avec l'option -O2 le programme aura-t-il plus de chance de crasher ou de mal se comporter ?
    En ce qui concerne l'optimisation je sais pas trop. Je pense qu'il essaye de mettre les indices de boucles en "register" ou remplacer les crochets des tableaux par des pointeurs. Mais en tout cas, le code restera fiable.

    Citation Envoyé par Luke spywoker Voir le message
    2. Variables globales.

    Je sais qu'il faut les éviter mais dès fois c'est nécessaire:
    Exact. C'est pour ça que ça existe.
    En fait, le vrai problèmes des globales c'est que dans un gros code improprement écrit, on peut les perdre (ne plus savoir qui les utilise) ou les oublier et recréer une variable locale du même nom (ce qui masquera alors la globale et plantera le code là où elle est utilisée).
    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int age;
     
    void fonction()
    {
         ... gros truc ...
         printf("Age=%d\n", age);
    }

    Ensuite un autre codeur arrive et rajoute un truc à lui
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void fonction()
    {
         ...autre truc...
         int age;
         ...fin autre truc...
         ... gros truc ...
         printf("Age=%d\n", age);
    }
    Et là ça ne fonctionne plus. Oui c'est vrai, l'autre codeur avait qu'à lire mais bon, on est tous humains.

    Une des façons de se "protéger" contre le danger est de déclarer en extern les globales utilisées par une fonction. La variable "extern" locale à une fonction référencera alors la variable globale.
    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int age;
     
    void fonction()
    {
         extern int age;   // Ca référence la globale et au moins tout le monde verra du premier coup qu'il n'a pas le droit de rajouter de variable nommée "age"
         ... gros truc ...
         printf("Age=%d\n", age);
    }
    Ainsi, on verra tout de suite si la globale est utilisée ou pas.

    Citation Envoyé par Luke spywoker Voir le message
    Mais le problème vient a la compilation car si je veut faire une compilation en passant par des fichiers objets (*.o) cela m'est impossible pour toute fonction qui utilise la variable globale, impossible d'inclure un en-tête et compiler un fichier objet global_vars.c ne fonctionne pas...
    Te suffit de définir ta globale dans le source qui contient le main et tu la déclares en tant qu'extern dans un ".h" qui sera inclus par tous tes sources (même le main, ce n'est pas grave).

    Exemple
    Fichier "toto.h"

    Fichier "main.c"
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include "toto.h"   // Ca redéclarera "age" une seconde fois, mais en "extern" ce n'est pas grave. La déclaration ne fait qu'indiquer l'existence.
     
    int age;                            // Ici ça défnit "age". Or c'est à la définition qu'est créé la mémoire nécessaire. 
     
    int main()
    {
        ...
    }

    Fichier "autre_source.c"
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #include "toto.h"    // Ici aussi, déclaration de l'existence de cette variable "age". La jointure finale se fera lors de l'édition des liens
     
    void fonction()
    {
         extern int age;   // Ne pas changer une bonne habitude
         ... gros truc ...
         printf("Age=%d\n", age);
    }

    Compilation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    cc -c autre_source.c
    cc -c main.c
    cc main.o autre_source.o -o exec
    Citation Envoyé par Luke spywoker Voir le message
    3. qualificateur const.

    Il ma été dit qu'il été inutile de déclarer un argument de fonction comme constant car il est passer par copie ???
    Moui. Tu peux avoir quand-même envie de verrouiller cet argument pour être sûr que personne ne le modifiera par "accident". Si par exemple tu écris une fonction d'affichage de pages à l'écran avec comme argument le nb de lignes de l'écran, ce nb de lignes doit rester invariant tout le long du travail de la fonction.

    Tu peux aussi avoir des pointeurs en argument, et vouloir protéger non pas le pointeur mais le pointé...
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int mylen(const char *chaine)
    {
    	int l=0;
    	while (*chaine++) l++;
    	return l;
    }
    Ici, le "const" ne s'applique pas au pointeur "chaine" (qui d'ailleurs est incrémenté dans la fonction) mais aux caractères pointés par ce pointeur. Si j'écris par exemple *chaine='x', le compilo va me jeter...

    Citation Envoyé par Luke spywoker Voir le message
    4. return nécessaire.

    Il m'a également dit qu'il était inutile de mettre un return a la fin d'une fonction renvoyant void ???
    Bien entendu. Ca ne sert que quand tu veux quitter une fonction "void" avant sa fin naturelle. D'ailleurs une fonction "void" ne "renvoie pas" void, elle est void !!!

    Citation Envoyé par Luke spywoker Voir le message
    5. valeur de retour dans un #define.

    Est il possible de faire retourner une valeur dans une définition ?

    Je pense a quelque chose comme cela:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    #define GET_NUM ({ do { return 1 ; } while(0) ; })
    Personnellement je n'y arrive pas.
    Normal, un #define constitue un remplacement de code. Chaque fois que le préprocesseur verra GET_NUM il écrira à la place ({ do { return 1 ; } while(0) ; }). Or le "return" fait que ta macro te fait quitter la fonction dans laquelle elle se trouve.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    #define GET_NUM(x)     (x)
    int main()
    {
        print("%d\n", GET_NUM(5));
    }


    Citation Envoyé par Luke spywoker Voir le message
    6. exec()

    Est-il possible de récupérer la stdout, stderr d'un appel a la famille exec(...) ou autre fonction exécutant une commande dans le terminal ?
    Oui. Il te faut d'abord créer un pipe mémoire. Puis ensuite forker ton processus afin de générer un fils. Ensuite, dans le fils, demander un dup2 de stdout sur le coté [1] du pipe pendant que le père va lire le coté [0]. Ensuite dans le fils tu fais ton exec.
    Le processus fils sera alors remplacé par le code de l'exec mais tout ce que ce code écrit dans stdout ira dans le coté [1] du pipe. Et le père, en lisant le coté [0], récupèrera ce qui a été écrit

    Exemple qui lit un "ls -l" et qui le convertit en majuscules

    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
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    void min2maj(char *chaine)   // Ici pas de "const" car on modifie le pointeur et le pointé
    {
    	while (*chaine)
    	{
    		*chaine=toupper(*chaine);
    		chaine++;
    	}
    }
    	
    int main()
    {
    	int p[2];
    	char buf[1024+1];
    	int sz_read;
    
    	pipe(p);
    	switch (fork())
    	{
    		case 0: // Fils
    			close(p[0]);
    			dup2(p[1], STDOUT_FILENO);
    			execl("/bin/ls", "/bin/ls", "-l", 0);
    			exit(0);
    		default: // Père
    			close(p[1]);
    			while ((sz_read=read(p[0], buf, 1024)) > 0)
    			{
    				buf[sz_read]=0;  // Je convertis le buffer en chaine
    				min2maj(buf);
    				write(STDOUT_FILENO, buf, sz_read);
    			}
    			close(p[0]);
    	}
    }

    Avec stderr le principe est le même avec un second pipe dédié. Et si tu veux mixer stdout et stderr dans un seul flux, je pense qu'un dup2 de stderr sur stdout avant de dup2 stdout sur le pipe devrait le faire (non testé)
    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
    Expert éminent sénior
    Avatar de Mat.M
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2006
    Messages
    8 360
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Novembre 2006
    Messages : 8 360
    Points : 20 378
    Points
    20 378
    Par défaut
    Citation Envoyé par Luke spywoker Voir le message
    Mais en quels termes ce code est optimisé ?
    le code peut être optimisé en taille ou en performances d'exécution...
    si l'optimisation est en exécution , pas dit que la taille du code en assembleur soit réduite pour autant car le compilateur peut rajouter des instructions supplémentaires.
    Maintenant si tu veux t'en rendre compte,il faut jouer avec les options d'optimisation et examiner le code assembleur généré.

    Citation Envoyé par Luke spywoker Voir le message
    Est-ce dangereux ? Car si l'on compile avec l'option -O2 le programme aura-t-il plus de chance de crasher ou de mal se comporter ?
    le fait d'utiliser des options d'optimisation ne garantit pas que le code s'exécute sans plantages...
    tout dépend du runtime C qui est lié à l'édition des liens avec le linker.
    Selon les runtimes C il est possible qu'il y ait des routines de contrôles d'opérations à savoir un pointeur mal alloué ou qui dépasse les limites d'un tableau.



    Ensuite pour les variables globales oui il faut les éviter et les rassembler dans des structures.

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 629
    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 629
    Points : 10 554
    Points
    10 554
    Par défaut
    Citation Envoyé par Luke spywoker Voir le message
    3. qualificateur const.

    Il ma été dit qu'il été inutile de déclarer un argument de fonction comme constant car il est passer par copie ???
    C'est logique

    Le passage de paramètre en C est par copie (on copie l'objet). Donc que l'on modifie la copie ou qu'on rende la copie constante, l'objet original ne sera pas modifié (*).
    D'ailleurs c'est pour cela qu'on passe des pointeurs (soit un pointeur soit l'adresse d'un objet): pour modifier le pointé
    Et même plus, si on veut modifier le pointeur lui-même, soit on passe un fameux double pointeur soit on retourne le nouveau pointeur.


    * -> Les débutants l'ont expérimenté plus d'1 fois

    Citation Envoyé par Luke spywoker Voir le message
    4. return nécessaire.

    Il m'a également dit qu'il était inutile de mettre un return a la fin d'une fonction renvoyant void ???
    Oui

    Dans les langages comme le Pascal/ Delphi on y a une distinction entre une fonction (un traitement qui retourne un résultat) et une procédure (un traitement sans résultat)

    En C/ C++, on n'a pas cette notion. Mais pour avoir des procédures, on crée des fonctions qui retournent rien (void). Donc à moins de vouloir quitter une fonction avant sa fin, pour une fonction qui retourne void, cela est inutile de le préciser à la toute fin.

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    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 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par foetus Voir le message
    Le passage de paramètre en C est par copie (on copie l'objet). Donc que l'on modifie la copie ou qu'on rende la copie constante, l'objet original ne sera pas modifié (*).
    D'ailleurs c'est pour cela qu'on passe des pointeurs (soit un pointeur soit l'adresse d'un objet): pour modifier le pointé
    Et même plus, si on veut modifier le pointeur lui-même, soit on passe un fameux double pointeur soit on retourne le nouveau pointeur.
    Alors je t'aime bien... mais là c'est du grand n'importe quoi !!!
    Déjà, comme je l'ai dit, on peut très bien vouloir garantir la constance de la valeur reçue. Parce que cette valeur a une signification mais que la fonction ne doit pas la modifier. Donc on utilisera "const".
    Ensuite, je ne vois pas en quoi le fait de renvoyer le nouveau pointeur va modifier le pointeur reçu en paramètre. Je vois bien que tu penses à un truc comme ça
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    char *fct(char *pt)
    {
        char *new;
        ... (travail)...
        return new
    }
     
    int main()
    {
        char *x;
        x=fct(x);
    }
    Mais là, la fonction n'a absolument pas modifié le pointeur reçu. C'est son appelant qui utlise le pointeur récupéré pour écraser le pointeur d'origine.

    Et enfin (détail) quand on veut qu'une fonction modifie une variable de type "<type>", alors on passe l'adresse de cette variable à la fonction qui la stockera dans un <type> *pt. Et cette simple description suffit à tous les cas et n'a pas besoin de précision supplémentaire. Si par exemple la variable en question est déjà un pointeur, alors "type" contiendra une étoile dans sa description et donc la variable "pt" aura deux étoiles. Pas besoin alors d'apporter de précision supplémentaire laissant sous-entendre que modifier un pointeur passé en paramètre est un cas différent de modifier une variable. Ni en quoi le double pointeur serait "fameux" (donc en comparaison le simple et le triple sont ordinaires ???)
    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]

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 629
    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 629
    Points : 10 554
    Points
    10 554
    Par défaut
    Tu t'es fait plaisir en jouant avec les mots ... et je ne devrais pas te répondre parce que tu pourras me reprendre à chaque fois.

    Je pense qu'il y a deux façons de voir le passage en C.
    1) Comme des matriochkas (les poupées russes), où seule celle la plus externe n'est pas modifiable
    1. Passage type param -> param non modifiable
    2. Passage type* param -> param non modifiable, (*param) modifiable
    3. Passage type** param -> param non modifiable, (*param) modifiable, (*(*param)) modifiable
    4. Passage type*** param -> param non modifiable, (*param) modifiable, (*(*param)) modifiable, (*(*(*param))) modifiable

    ainsi de suite

    2) Ou alors:
    1. Passage type param -> constant
    2. Passage type* param -> modification
    3. Passage type** param -> création et modification

    D'où le fameux double pointeur , puisqu'il apporte la signification de "création". C'est un coup à prendre.


    Sinon, effectivement tu as raison le retour du pointeur n'a rien à voir avec le passage de paramètre (avec ce fil).
    Mais j'aurai dû préciser que c'était une alternative pour la création d'un pointeur ... en rouge surligné gras police 15-20


    Sinon, j'ai vérifié et le const est le même qu'en C++. On peut le placer à 2 endroits pour vouloir soit le pointé soit le pointeur soit les 2 constants.
    C'est une question préférée des recruteurs qui ne connaissent rien d'ailleurs const char* const p; ou bien char const* const p;.

  7. #7
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 518
    Points
    41 518
    Par défaut
    À noter que, pour des raisons qui m'échappent, un compilateur C standard* ne permet pas la conversion implicite de truc ** en truc const * const *, alors que C++ corrige cette lacune.

    *Le compilateur C intégré à Microsoft Visual C++ l'accepte.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  8. #8
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    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 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par foetus Voir le message
    2) Ou alors:
    1. Passage type param -> constant
    2. Passage type* param -> modification
    3. Passage type** param -> création et modification

    D'où le fameux double pointeur , puisqu'il apporte la signification de "création". C'est un coup à prendre.
    Ok, m'a fallu du temps mais j'ai compris.
    Je pense que tu veux parler du cas des listes chainées, où la création d'un noeud est dévolu à une fonction. Exemple (on considèrera que le noeud créé se met toujours en tête de la liste)
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    typedef struct s_noeud {
    	char nom[100];
    	struct s_noeud *next;
    } t_noeud;
     
    void createNoeud(t_noeud **first, char *nom)
    {
    	t_noeud *new;
    	new=malloc(sizeof(t_noeud));
    	strcpy(new->nom, nom);
    	new->next=(*first);
    	(*first)=new;
    }
     
    void affiche(t_noeud *first)
    {
    	while (first)
    	{
    		printf("Nom: %s\n", first->nom);
    		first=first->next;
    	}
    }
     
    int main(int argc, char *argv[])
    {
    	t_noeud *first=NULL;
    	while (*++argv)
    		createNoeud(&first, *argv);
    	affiche(first);
    }

    Donc effectivement, ce que je traduis par "modification du pointeur reçu pour lui passer l'adresse de la zone allouée par malloc" toi tu le traduis par "création de pointeur". Ok, je veux bien admettre qu'on peut le voir comme cela. Toutefois je vais te proposer deux autres façons de faire

    1) la fonction de création ne modifie pas le pointeur mais en renvoie un
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    typedef struct s_noeud {
    	char nom[100];
    	struct s_noeud *next;
    } t_noeud;
     
    t_noeud* createNoeud(t_noeud *first, char *nom)
    {
    	t_noeud *new;
    	new=malloc(sizeof(t_noeud));
    	strcpy(new->nom, nom);
    	new->next=first;
    	return new;
    }
     
    void affiche(t_noeud *first)
    {
    	while (first)
    	{
    		printf("Nom: %s\n", first->nom);
    		first=first->next;
    	}
    }
     
    int main(int argc, char *argv[])
    {
    	t_noeud *first=NULL;
    	while (*++argv)
    		first=createNoeud(first, *argv);
    	affiche(first);
    }
    Cette solution qui évite le double étoile apporte l'avantage de ne pas manipuler de (*truc) ni d'écrire de (*truc)->qqchose qui peut se révéler vite illisible. Je ne sais plus qui a écrit dans sa signature "une variable en moins c'est un bug en moins, un pointeur en moins c'est une montagne de bugs en moins". Là, avec des double étoiles, ce sont des montagnes² de bugs...
    Mais en contrepartie on perd l'indépendance de la fonction avec son appelant qui doit, pour que tout fonctionne sans accroc, bien veiller à écraser son "first" avec celui qu'il reçoit de la fonction de création.

    2) C'est pourquoi j'aimerais te proposer cette seconde solution où on encapsule le premier pointeur dans une structure dédiée à la liste elle-même...
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    typedef struct s_noeud {
    	char nom[100];
    	struct s_noeud *next;
    } t_noeud;
     
    typedef struct {
    	t_noeud *first;
    } t_liste;
     
    void createNoeud(t_liste *l, char *nom)
    {
    	t_noeud *new;
    	new=malloc(sizeof(t_noeud));
    	strcpy(new->nom, nom);
    	new->next=l->first;
    	l->first=new;
    }
     
    void affiche(t_liste *l)
    {
    	t_noeud *n;
    	for (n=l->first; n; n=n->next)
    		printf("Nom: %s\n", n->nom);
    }
     
    int main(int argc, char *argv[])
    {
    	t_liste liste;
    	liste.first=NULL;
    	while (*++argv)
    		createNoeud(&liste, *argv);
    	affiche(&liste);
    }
    Et là on atteint une nouvelle étape. Non seulement on garde l'indépendance (la fonction de création se suffit à elle-même pour faire évoluer la liste) ; non seulement on évite ces doubles étoiles assez lourdes à force ; mais surtout on ouvre la voie à d'autres possibilités futures. Comme par exemple pouvoir facilement traiter des tableaux de liste (la solution n° 1 aurait nécessité d'écrire t_noeud *first[n] alors qu'ici on aura un élégant t_liste liste[n]). Ou alors rajouter facilement des éléments de gestion comme le nb de noeuds ou autre (une seule modif dans la structure "t_liste" et toutes les fonctions y ont automatiquement accès).
    Bref je pense qu'on peut toujours éviter ces doubles étoiles qui, en fait, généralement me crispent...
    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]

  9. #9
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Et là on atteint une nouvelle étape. Non seulement on garde l'indépendance (la fonction de création se suffit à elle-même pour faire évoluer la liste) ; non seulement on évite ces doubles étoiles assez lourdes à force ; mais surtout on ouvre la voie à d'autres possibilités futures. Comme par exemple pouvoir facilement traiter des tableaux de liste (la solution n° 1 aurait nécessité d'écrire t_noeud *first[n] alors qu'ici on aura un élégant t_liste liste[n]). Ou alors rajouter facilement des éléments de gestion comme le nb de noeuds ou autre (une seule modif dans la structure "t_liste" et toutes les fonctions y ont automatiquement accès).
    Bref je pense qu'on peut toujours éviter ces doubles étoiles qui, en fait, généralement me crispent...
    Si le but est de cacher le double pointeur un simple typedef fait l'affaire, pas besoin de le cacher dans une struct:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef t_noeud* p_noeud;
    typedef p_noeud* pp_noeud;
     
    void createNoeud(pp_noeud first, char *nom);
    Mais il est important de savoir ce qu'on manipule et ce que ça implique, un double pointeur est bien plus parlant : il apporte des informations supplémentaires pour la compréhension du code.
    Citation Envoyé par foetus Voir le message
    2) Ou alors:
    1. Passage type param -> constant
    2. Passage type* param -> modification
    3. Passage type** param -> création et modification

    D'où le fameux double pointeur , puisqu'il apporte la signification de "création". C'est un coup à prendre.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    typedef struct Foo_ { } Foo;
     
    void foo(Foo f); // Foo est petit, on peut se permettre de le copier, accès en lecture seulement
    void foo(Foo const* f); // Foo est gros on veut pas le copier, mais accès en lecture seulement
    void foo(Foo *f); // On va modifier le contenu de f, accès en lecture / écriture
    void foo(Foo **f); // On va modifier sur quoi pointe f (possiblement créer un nouvel objet), on peut aussi modifier le contenu de f
    Sans le double pointeur, c'est des informations qui se retrouveraient dans la doc, ou qui demanderaient une lecture plus approfondie du code.

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    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 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Iradrille Voir le message
    Si le but est de cacher le double pointeur un simple typedef fait l'affaire, pas besoin de le cacher dans une struct
    Tu fais erreur, je n'ai jamais prôné de masquer/cacher le double pointeur. Au contraire, de partout j'ai toujours dit sur ce forum que cacher un pointeur (et donc fatalement un double pointeur) était la pire des choses à faire.
    Ma structure n'a pas pour but de cacher le double pointeur mais d'encapsuler un objet (concrètement une liste) dans un outil permettant de la manipuler (une structure) et ce, dans le but de pouvoir la manipuler justement plus facilement. J'ai d'ailleurs souvent dit à ce sujet que c'est comme pour une chemise (le vêtement): on peut la tenir par le col mais c'est plus aisé si on la met sur un cintre.

    Et accessoirement le fait de déporter "premier élément" dans la liste fait que le double pointeur disparait. Ce n'était pas le but mais ça devient une conséquence par ailleurs agréable.

    Citation Envoyé par Iradrille Voir le message
    Mais il est important de savoir ce qu'on manipule et ce que ça implique, un double pointeur est bien plus parlant : il apporte des informations supplémentaires pour la compréhension du code.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    typedef struct Foo_ { } Foo;
     
    void foo(Foo f); // Foo est petit, on peut se permettre de le copier, accès en lecture seulement
    void foo(Foo const* f); // Foo est gros on veut pas le copier, mais accès en lecture seulement
    void foo(Foo *f); // On va modifier le contenu de f, accès en lecture / écriture
    void foo(Foo **f); // On va modifier sur quoi pointe f (possiblement créer un nouvel objet), on peut aussi modifier le contenu de f
    Sans le double pointeur, c'est des informations qui se retrouveraient dans la doc, ou qui demanderaient une lecture plus approfondie du code.
    Mouais. J'ai quand-même un gros doute sur le fait qu'un double pointeur est plus parlant qu'une structure. Surtout que faudrait aussi savoir ce que tu entends par "petit" ou "gros". Mais même sans ça je trouve quand-même super pratique (ou super évoilutif) d'avoir un objet dédié à la liste. Ca facilite lévolution. Demain tu veux rajouter le nb d'éléménts, te suffiit de modifier mon dernier code ainsi

    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
    typedef struct s_noeud {
    	char nom[100];
    	struct s_noeud *next;
    } t_noeud;
     
    typedef struct {
    	t_noeud *first;
    	size_t nb_elem;
    } t_liste;
     
    void createNoeud(t_liste *l, char *nom)
    {
    	...
    	l->nb_elem++;
    }
     
    int main(int argc, char *argv[])
    {
    	t_liste liste;
    	liste.first=NULL;
    	liste.nb_elem=0;
    	...
    }
    Idem si tu veux rajouter un second pointeur pour faire du double-sens ou autre...
    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]

  11. #11
    Expert confirmé
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Points : 4 442
    Points
    4 442
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Mouais. J'ai quand-même un gros doute sur le fait qu'un double pointeur est plus parlant qu'une structure. Surtout que faudrait aussi savoir ce que tu entends par "petit" ou "gros". Mais même sans ça je trouve quand-même super pratique (ou super évoilutif) d'avoir un objet dédié à la liste. Ca facilite lévolution. Demain tu veux rajouter le nb d'éléménts, te suffiit de modifier mon dernier code ainsi
    [...]
    Idem si tu veux rajouter un second pointeur pour faire du double-sens ou autre...
    "Petit" : qu'on peut se permettre de copier : <= ~8 octets; "Gros" : qu'on préfère manipuler via un pointeur pour éviter des copies trop chères.

    Si le but n'est pas de cacher le pointeur, tout va bien (et oui, ici une structure a du sens ).

    Mais je comprenais ton post comme : "en cas de double pointeur, le cacher dans une structure est la solution par défaut / habituelle". D'où ma réaction : un double pointeur n'est pas forcément quelque chose à cacher, ça peut apporter des informations.

  12. #12
    Membre expérimenté
    Avatar de Luke spywoker
    Homme Profil pro
    Etudiant informatique autodidacte
    Inscrit en
    Juin 2010
    Messages
    1 077
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Etudiant informatique autodidacte

    Informations forums :
    Inscription : Juin 2010
    Messages : 1 077
    Points : 1 742
    Points
    1 742
    Par défaut
    Merci pour vos réponses, je ne sais pas quoi dire d'autre, car je n'ai rien a ajouter.

    A part peut-être je me posait la question:

    A quoi sert la commande "strip" exactement:

    Cela enlève la table des symboles je suppose, mais comme je ne suis pas assez calé en matière de format d'exécutable, je ne sais a quoi cela correspond.

    Par contre je sais qu'il ne faut pas stripper une library, sûrement pour les points d'entrées qui sont multiples dans ce cas.

    Cela rétrécis la taille de l'exécutable mais on peut plus le debugger après...

    Merci pour vos réponses éclairées.
    Pour faire tes armes:
    Use du présent pour construire ton futur sinon use de ce que tu as appris auparavant.
    Et sois toujours bien armé avant de te lancer.
    Le hasard ne sourit qu'aux gens préparés...
    Site: Website programmation international (www.open-source-projects.net)
    Site: Website imagerie 3D (www.3dreaming-imaging.net)
    Testez aux moins pendant une semaine l'éditeur avec terminaux intégrées it-edit Vous l'adopterai sûrement !
    FUN is HARD WORK !!!

  13. #13
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 684
    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 684
    Points : 30 973
    Points
    30 973
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Luke spywoker Voir le message
    A quoi sert la commande "strip" exactement:
    Cela enlève la table des symboles je suppose, mais comme je ne suis pas assez calé en matière de format d'exécutable, je ne sais a quoi cela correspond.
    Ca supprime d'un exécutable les éléments qui ne sont pas strictement nécessaire à son exécution. Que sont ces éléments exactement, ça je ne sais pas trop mais la table des symboles en fait effectivement partie.
    Il en résulte un exécutable plus petit et (éventuellement) plus performant.
    Mais il ne faut pas oublier que cette commande (Unix) a été écrite à l'époque où l'espace disque était faible et convoité tandis que, parallèlement, les accès étaient lents. Son utilité aujourd'hui semble peut-être moins primordiale.

    Citation Envoyé par Luke spywoker Voir le message
    mais on peut plus le debugger après...
    Exact. L'outil "gdb" qui utilise la table des symboles ne peut plus traiter le programme.

    Citation Envoyé par Luke spywoker Voir le message
    Merci pour vos réponses éclairées.
    Effectivement, elles le sont toutes
    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]

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [debutant]Question toute bete sur le messages
    Par flogreg dans le forum Servlets/JSP
    Réponses: 18
    Dernier message: 09/09/2004, 09h07
  2. Question d'algorithmique sur HeapSort
    Par didier2604 dans le forum Algorithmes et structures de données
    Réponses: 2
    Dernier message: 02/09/2004, 11h17
  3. question de débutant sur les objets
    Par boucher_emilie dans le forum ASP
    Réponses: 3
    Dernier message: 06/08/2004, 10h51
  4. [Débutant]Quelques questions de principe sur l'API win32
    Par silver_dragoon dans le forum Windows
    Réponses: 4
    Dernier message: 19/03/2004, 18h38
  5. [LG]J'ai honte : question de cours sur les paramètres
    Par letibdesneiges dans le forum Langage
    Réponses: 14
    Dernier message: 17/01/2004, 13h57

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