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 d'un novice sur les « infâmes » pointeurs


Sujet :

C

  1. #41
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Citation Envoyé par Al_th Voir le message
    Si DIR existe, tout va bien, mais si DIR n'existe pas et que ma variable contient une chaine vide par exemple, je détruit tout mes fichiers de mon répertoire courant sans m'en rendre compte ?
    Oui, et c'est même un grand classique… :-) C'est même plus grave encore : $DIR n'est pas définie mais le slash demeure, ce qui fait que tu vas détruire tout ce qui ce trouve au premier niveau de la racine de ton système de fichier.

    Dans ce cas précis, si tu es utilisateur normal, tu n'auras pas d'ennui. Si tu es root, tu entreras dans la grande famille des administrateurs UNIX « de la deuxième catégorie » […]. Puisque tu n'as pas passé l'option « -r », tu as encore une chance de t'en sortir (les répertoires, même vides, ne devraient pas être effacés, et il n'est censé se trouver que des répertoires à cet endroit).

    Par contre, parmi les meilleures citations d'administrateurs systèmes, on a le laconique :

    Code ShellPrompt : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    # rm -rf / tempdir
    rm: tempdir: not found
    #

  2. #42
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2009
    Messages
    91
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 91
    Points : 133
    Points
    133
    Par défaut
    C'est l'espace entre le / et tempdir qui va causer la perte de notre bien-aimé admin systeme ?

  3. #43
    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 Al_th Voir le message
    Attend, ca fait un petit peu peur ce que tu me dis

    J'utilise assez peu le shell unix, même si ca peut m'arriver.
    Qu'est ce qui se passe si, sans le même genre on test :
    -La présence d'un dossier $DIR
    -L'execution d'une commande rm $DIR/*

    Si DIR existe, tout va bien, mais si DIR n'existe pas et que ma variable contient une chaine vide par exemple, je détruit tout mes fichiers de mon répertoire courant sans m'en rendre compte ?
    Déjà il serait étonnant qu'une de tes variables ne soit pas remplie comme il faut (c'est la tienne non ?)
    Mais bon, on peut imaginer qu'elle sera saisie et dépend d'un contexte que tu ne maitrises pas. Ben ton souci peut se résoudre assez facilement avec la syntaxe suivante
    Code bash : Sélectionner tout - Visualiser dans une fenêtre à part
    test -d "$dir" && rm -rf "$dir"/*
    Surtout toujours bien spécifier les guillemets autour de la variable. Comme ça, même si elle est vide, le shell voit test -d "" et il a quand-même quelque chose à comparer. Parce que sinon, il voit test -d et te renvoie une erreur.

    Tu remarqueras que la spécificité que j'ai donnée hier ne s'applique pas à ton exemple. Tu n'allais quand-même pas écrire test -d "$dir/*" -a $(rm -f "$dir"/*) quand-même...


    Citation Envoyé par Al_th Voir le message
    C'est l'espace entre le / et tempdir qui va causer la perte de notre bien-aimé admin systeme ?
    Exact. Une fois il m'est arrivé de faire ma couille. J'avais l'habitude, sur ma sun, de purger /tmp sous root. Je tapais donc cd /tmp; rm -fr * .*. Tout se passait nickel.
    Puis, un jour, j'ai voulu aller plus vite et j'ai tapé rm -rf /tmp/* /tmp/.*. Et là, le système ne m'a pas rendu la main...
    Pendant qu'il bossait, je réfléchissais à ce qu'il pouvait bien être en train de faire. Et là, j'ai eu un flash. Et je me suis souvenu que dans /tmp/.*, il y avait /tmp/..
    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]

  4. #44
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2009
    Messages
    91
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2009
    Messages : 91
    Points : 133
    Points
    133
    Par défaut
    Tu remarqueras que la spécificité que j'ai donnée hier ne s'applique pas à ton exemple. Tu n'allais quand-même pas écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     test -d "$dir/*" -a $(rm -f "$dir"/*)
    quand-même...
    Je suis trop peu familiarisé avec le shell pour utiliser des commandes de ce genre.
    Personellement je ferais

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ls
    cd dir
    rm *
    cd ..
    rm -rd dir


    Exact. Une fois il m'est arrivé de faire ma couille. J'avais l'habitude, sur ma sun, de purger /tmp sous root. Je tapais donc cd /tmp; rm -fr * .*. Tout se passait nickel.
    Puis, un jour, j'ai voulu aller plus vite et j'ai tapé rm -rf /tmp/* /tmp/.*. Et là, le système ne m'a pas rendu la main...
    Pendant qu'il bossait, je réfléchissais à ce qu'il pouvait bien être en train de faire. Et là, j'ai eu un flash. Et je me suis souvenu que dans /tmp/.*, il y avait /tmp/..

  5. #45
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Exact. Une fois il m'est arrivé de faire ma couille. J'avais l'habitude, sur ma sun, de purger /tmp sous root. Je tapais donc cd /tmp; rm -fr * .*. Tout se passait nickel.
    Puis, un jour, j'ai voulu aller plus vite et j'ai tapé rm -rf /tmp/* /tmp/.*. Et là, le système ne m'a pas rendu la main...
    Pendant qu'il bossait, je réfléchissais à ce qu'il pouvait bien être en train de faire. Et là, j'ai eu un flash. Et je me suis souvenu que dans /tmp/.*, il y avait /tmp/..
    Mil-le-dieux... Je me souviens désormais pourquoi je rechigne tant à utiliser la commande rm.

    Au passage je pose la question la plus folle que je n'ai jamais eu jusqu'ci (ou presque) :

    [5] - Est-il possible de programmer en C en n'utilisant que des pointeurs et des constantes ?
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  6. #46
    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 VivienD Voir le message
    Mil-le-dieux... Je me souviens désormais pourquoi je rechigne tant à utiliser la commande rm.
    Quand j'ai débuté et que j'ai été lâché la première fois seul sur un Unix, j'avais créé ma propre commande "rm" que j'avais appelé "rm+". Elle transformait un effacement en déplacement dans un dossier de secours. Mais comme d'habitude, on ne fait jamais les conneries quand on débute. On est trop concentré et inquiet. C'est après, quand on prend de l'assurance mais qu'on n'a pas encore tous les bons réflexes que les pb arrivent...

    Citation Envoyé par VivienD Voir le message
    [5] - Est-il possible de programmer en C en n'utilisant que des pointeurs et des constantes ?
    Ca me fait penser à Georges Perec qui a écrit un bouquin entier (La Disparition) sans utiliser la lettre "e". Alors oui on peut le faire. Mais l'utilité d'une telle démarche...?

    PS: Georges Perec a quand-même triché car pour son livre, il a inventé des mots qui ne sont pas dans le dictionnaire...
    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]

  7. #47
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    L'utilité ? Je n'en vois qu'une : m'exercer à programmer avec des pointeurs. Certes, c'est barbare, mais ça me correspond bien.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  8. #48
    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 VivienD Voir le message
    L'utilité ? Je n'en vois qu'une : m'exercer à programmer avec des pointeurs. Certes, c'est barbare, mais ça me correspond bien.
    Je te dirais bien de te lancer mais l'expérience m'a montré que si on veut programmer avec motivation, il vaut mieux avoir un but réel que de partir un peu au hasard en se disant "je vais m'exercer".
    Tu veux t'entrainer avec des pointeurs ? Refais donc memcpy() (en essayant de prévoir le cas où on appelle ta fonction lorsque les pointeurs se chevauchent). Là c'est déjà un bon début...
    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. #49
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Je te dirais bien de te lancer mais l'expérience m'a montré que si on veut programmer avec motivation, il vaut mieux avoir un but réel que de partir un peu au hasard en se disant "je vais m'exercer".
    Tu veux t'entrainer avec des pointeurs ? Refais donc memcpy() (en essayant de prévoir le cas où on appelle ta fonction lorsque les pointeurs se chevauchent). Là c'est déjà un bon début...
    J'ai fait quelques recherches vu que cette fonction était inconnue au bataillon. J'ai trouvé que memcpy() copie les (size_t) n octets à partir de l'adresse pointée par le pointeur-source et les colle à partir de l'adresse pointée par le pointeur-destinataire. memcpy() renvoie le pointeur-destinataire lorsque l'opération s'est déroulée normalement ; mais quelle adresse renvoie la fonction dans le cas contraire ? NULL, peut-être ?
    En revanche je ne vois pas à quoi correspond le type void des deux pointeurs utilisés en tant que paramètres de la fonction.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  10. #50
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Hello,

    Citation Envoyé par VivienD Voir le message
    J'ai fait quelques recherches vu que cette fonction était inconnue au bataillon. J'ai trouvé que memcpy() copie les (size_t) n octets à partir de l'adresse pointée par le pointeur-source et les colle à partir de l'adresse pointée par le pointeur-destinataire. memcpy() renvoie le pointeur-destinataire lorsque l'opération s'est déroulée normalement ; mais quelle adresse renvoie la fonction dans le cas contraire ? NULL, peut-être ?
    La fonction renvoie toujours dest car elle ne peut pas échouer (sauf segfault, bien sûr, mais cela ne la regarde plus).

    En revanche je ne vois pas à quoi correspond le type void des deux pointeurs utilisés en tant que paramètres de la fonction.
    void signifie justement que le type en question n'est pas défini et/ou n'a pas de valeur. Une fonction void peut donc se passer de return, par exemple, et un pointeur void permet de dire que l'on passe une adresse en mémoire sans préciser ce qu'il y a derrière, soit parce qu'on ne le sait pas, soit parce que cela peut être n'importe quoi a priori, comme dans le cas de cette fonction. Un pointeur void peut donc recevoir l'adresse de n'importe quoi sans transtypage explicite, sauf peut-être celle d'une fonction si on compile dans un environnement trop strict, comme mis en lumière dans cette discussion.

    Vois aussi memmove().

  11. #51
    Expert éminent sénior
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Points : 13 926
    Points
    13 926
    Par défaut
    Est-il possible de programmer en C en n'utilisant que des pointeurs et des constantes ?
    Cela dépend comment on comprend la question. L'ambiguité vient du mot "pointeur" : désigne t-il ici "objet pointeur" (stockant une adresse d'objet) ou "adresse d'un objet" (parfois appelée constante de type pointeur) ?

    Dans le deuxième cas, cela signifie qu'on a pour accéder aux objets que leur adresse (et pas d'identificateur associé aux objets). Autrement dit tous les objets sont non nommés. Alors, la réponse est (probablement) non.
    Les adresses des objets devraient être stockées dans des objets pointeurs et si ceux-ci ne peuvent être repérés que par leur adresse, on a un cycle infernal. Il faut au moins exonérer une partie des objets pointeurs de cette contrainte. On aura donc au moins quelques objets pointeurs nommés. Les autres objets pourront être obtenus par allocation dynamique ou comme chaine littérale de caractères ou par l'intermédiaire des autres objets non nommés disponible en C99.
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  12. #52
    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 VivienD Voir le message
    memcpy() renvoie le pointeur-destinataire lorsque l'opération s'est déroulée normalement ; mais quelle adresse renvoie la fonction dans le cas contraire ? NULL, peut-être ?
    Lance-toi à programmer un ersatz de cette fonction. Tu découvriras probablement la réponse tout seul...

    Citation Envoyé par VivienD Voir le message
    En revanche je ne vois pas à quoi correspond le type void des deux pointeurs utilisés en tant que paramètres de la fonction.
    Un pointeur "void *" est un pointeur universel. On peut donc y mettre un pointeur de char, d'int, de float, de n'importe quoi (on l'appelle aussi "pointeur sur tout"). Ainsi tu peux utiliser cette fonction avec des pointeurs sur int, ou bien des pointeurs sur float, ou bien...
    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <string.h>
    int main()
    {
        int a=10;
        int b;
        float c=3.1416;
        float d;
     
        memcpy(&b, &a, sizeof(int));
        memcpy(&d, &c, sizeof(float));
    }
    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]

  13. #53
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    À diogene : En fait, tu m'as perdu dès la deuxième phrase où tu parles de l'ambiguïté du terme « pointeur ». Je vois que l'amateur que je suis a encore beaucoup de chemin à faire. Tant mieux ! car, sinon, ça ne serait pas intéressant.

    À Sve@r et obsidian : je vais m'atteler à l'étude des fonctions memcpy() et memmove() après mes examens vu que ça risque de me prendre un certain temps.

    D'autre part, j'ai fait une petite expérience dans cette pause dans mes révisions. J'ai créé un tableau bidimensionnel grâce aux pointeurs et à la fonction calloc(), mais je n'en suis pas tout à fait satisfait.
    Voici le code :
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    #define NB_ABSCISSES 100
    #define NB_ORDONNEES 100
     
    int main(int argc, char *argv[])
    {
        int i, j;
        int *arpAbscisse[NB_ABSCISSES] = {NULL};
        int **pTab = NULL;
     
        for(i = 0 ; i < NB_ABSCISSES ; i++)
            arpAbscisse[i] = calloc((size_t) NB_ORDONNEES, sizeof(int));
     
        pTab = &arpAbscisse[0];
     
        return 0;
    }
    Pourquoi ma satisfaction n'est-elle pas complète ? En fait, je pense qu'il y a un moyen de se départir du vecteur int *arpAbscisse[], mais je ne vois pas comment. Un peu d'aide, s'il vous plaît.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  14. #54
    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 VivienD Voir le message
    À Sve@r et obsidian : je vais m'atteler à l'étude des fonctions memcpy() et memmove() après mes examens vu que ça risque de me prendre un certain temps.
    Essaye plutôt (enfin c'est juste un avis) d'étudier à quoi elles servent puis, une fois que tu as bien l'idée en tête, de refaire la même chose à partir de rien.

    Citation Envoyé par VivienD Voir le message
    D'autre part, j'ai fait une petite expérience dans cette pause dans mes révisions. J'ai créé un tableau bidimensionnel grâce aux pointeurs et à la fonction calloc(), mais je n'en suis pas tout à fait satisfait.
    Voici le code :
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    #define NB_ABSCISSES 100
    #define NB_ORDONNEES 100
     
    int main(int argc, char *argv[])
    {
        int i, j;
        int *arpAbscisse[NB_ABSCISSES] = {NULL};
        int **pTab = NULL;
     
        for(i = 0 ; i < NB_ABSCISSES ; i++)
            arpAbscisse[i] = calloc((size_t) NB_ORDONNEES, sizeof(int));
     
        pTab = &arpAbscisse[0];
     
        return 0;
    }
    Pourquoi ma satisfaction n'est-elle pas complète ? En fait, je pense qu'il y a un moyen de se départir du vecteur int *arpAbscisse[], mais je ne vois pas comment. Un peu d'aide, s'il vous plaît.
    Tu peux pas. Un tableau 2D c'est un tableau contenant "n" tableaux 1D. Tu peux éviter calloc vu que tes tailles sont figées int arpAbscisse[NB_ABSCISSES][NB_ORDONNEES] mais tu peux pas éviter d'avoir soit un int arpAbscisse[][], soit un int *arpAbscisse[], soit un int **arpAbscisse. Et dans ce dernier cas, te faudra lui-aussi l'allouer avant de le remplir.

    En fait, dans le meilleur des cas, c'est pTab qui est inutile...
    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]

  15. #55
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Je vois. Ça risque donc d'être fastidieux si je souhaite passer à une dimension supérieure en utilisant cette syntaxe. Dommage.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  16. #56
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Avant toute chose, je trouve que la qualité rédactionnelle de ton code est déjà remarquable. On voit que tu t'y attaches avec sérieux.

    À Sve@r et obsidian : je vais m'atteler à l'étude des fonctions memcpy() et memmove() après mes examens vu que ça risque de me prendre un certain temps.
    C'est une bonne chose de ne pas s'embrouiller la tête avec des choses annexes lorsque l'on est en période d'examen. Cela dit, il n'y a aucun piège dans memcpu() et memmove(). La première copie juste un bloc de n octets (enfin : bytes) d'un endroit A à un endroit B. memmove() fait de même mais en prenant soin d'éviter tout écrasement si les emplacements se chevauchent. Le plus simple pour y arriver consistant à commencer la copie par la fin des données si le bloc destination se trouve après le bloc source.

    D'autre part, j'ai fait une petite expérience dans cette pause dans mes révisions. J'ai créé un tableau bidimensionnel grâce aux pointeurs et à la fonction calloc(), mais je n'en suis pas tout à fait satisfait. Pourquoi ma satisfaction n'est-elle pas complète ? En fait, je pense qu'il y a un moyen de se départir du vecteur int *arpAbscisse[], mais je ne vois pas comment. Un peu d'aide, s'il vous plaît.
    En fait, tu mets le doigt sur un détail subtil mais très fondamental avec les tableaux à plusieurs dimensions : un tableau multi-dimensionnel n'est pas un « tableau de tableaux ». Cela reste une zone de mémoire consécutive et d'un seul tenant, qui contient tous tes éléments.

    La question est légitime car on pourrait tout-à-fait s'attendre à ce qu'ajouter des crochets à la suite d'une expression quelconque provoque sa mise en parallèle, fût-ce déjà un tableau.

    Il n'y a pas non plus n pointeurs automatiquement déclarés, un pour chaque ligne. Là encore, seule la place pour les éléments est réservée en mémoire, et le nom du tableau seul renvoie l'adresse du début de cette zone, qui est donc forcément celle du premier élément, même à plusieurs dimensions. C'est important parce que dans le cas de malloc() successifs comme tu le fais, tu es assurée que chaque « ligne » est en un seul morceau, certes, rien ne te garantit que les différentes lignes soient consécutives en mémoire. Ça peut être un mal comme un bien, dans la mesure où allouer un très gros bloc d'un seul tenant peut être impossible si la mémoire est trop fragmentée, même si la mémoire totale disponible reste supérieure à la taille de ce bloc.

    La façon la plus sûre de déclarer dynamiquement un tableau multi-dimensionnel, donc, reste l'allocation d'une zone de mémoire correspondant à tous ses éléments :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        pTab = calloc ((size_t) NB_ORDONNEES * NB_ABSCISSES,sizeof (int));
    Mais si tu veux rester propre d'un point de vue sémantique, tu peux quand même allouer de la place pour « NB_ABSCISSES » tableaux de « NB_ORDONNEES » entiers :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        pTab = calloc ((size_t) NB_ORDONNEES,sizeof (int[NB_ORDONNEES));
    ATTENTION : Chose importante, tu n'as plus de liste de pointeurs indiquant le début de chaque ligne. Ton pointeur pTab ne peut donc plus être déclarée « int ** pTab; ».

    À la place, le plus exact d'un point de vue sémantique, toujours, est d'indiquer qu'il pointe un tableau d'entiers et dont les dimensions sont celles que tu as déclarées. Attention, c'est subtil, encore une fois : de la même façon qu'un pointeur « int * » sur un tableau fait « disparaître » la première dimension, il va falloir définir un « pointeur vers une ligne d'entiers » :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        int (* pTab) [NB_ORDONNEES] = NULL;
    EDIT : Je paraphrase encore Sve@r sur certains points, le temps de finir de rédiger mon commentaire. :-) Je pense qu'il ne m'en voudra pas.

  17. #57
    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 VivienD Voir le message
    Je vois. Ça risque donc d'être fastidieux si je souhaite passer à une dimension supérieure en utilisant cette syntaxe. Dommage.
    C'est pour ça que j'ai dit une fois que généralement, on allait rarement dépasser la 3D. Ca devient trop galère ensuite pour s'en sortir sans se ramasser la tronche en segfault (et encore, là c'est quand on a de la chance car quand on n'en a pas, le programme tourne, foireux mais il tourne jusqu'au jour où tu rajoutes un printf() à 40km plus bas et là il foire et toi, tu cherches pourquoi ça foire avec un printf et là tu galères...). Et que j'ai parlé aussi de structures remplaçant les dimensions...
    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
    typedef struct {
        int val;
    } t_point;
     
    typedef struct {
        t_point tabPt[NB_ABSCISSE];
    } t_lig;
     
    typedef struct {
        t_lig tabLig[NB_ORDONNEES];
    } t_matrice;
     
    int main()
    {
        int i, j;
        t_matrice mat;
     
        for (i=0; i < nb_ORDONNEES; i++)
        {
             for (j=0; j < NB_ABSCISSE; j++)
             {
                 mat.tabLig[i].tabPt[j].val=i * j;
             }
        }
     
        for (i=0; i < nb_ORDONNEES; i++)
        {
             for (j=0; j < NB_ABSCISSE; j++)
             {
                 printf("élément [%d][%=%d, ", i, j, mat.tabLig[i].tabPt[j].val);
             }
             printf("\n");
        }
        return 0;
    }

    De plus, ça peut paraitre idiot une structure pour juste une valeur mais ça permet plus tard de la faire évoluer facilement...


    Citation Envoyé par Obsidian Voir le message
    Le plus simple pour y arriver consistant à commencer la copie par la fin des données si le bloc destination se trouve après le bloc source.
    Argh fallait pas lui dire. Ca le faisait chercher un peu


    Citation Envoyé par Obsidian Voir le message
    Je paraphrase encore Sve@r sur certains points, le temps de finir de rédiger mon commentaire. :-) Je pense qu'il ne m'en voudra pas.
    Evidemment que non On s'éclate avec les pointeurs non ???
    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]

  18. #58
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Tu peux pas. Un tableau 2D c'est un tableau contenant "n" tableaux 1D. Tu peux éviter calloc vu que tes tailles sont figées int arpAbscisse[NB_ABSCISSES][NB_ORDONNEES] mais tu peux pas éviter d'avoir soit un int arpAbscisse[][], soit un int *arpAbscisse[], soit un int **arpAbscisse. Et dans ce dernier cas, te faudra lui-aussi l'allouer avant de le remplir.
    Ben… si ! Justement, c'est l'idée :

    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
    #include <stdlib.h>
    #include <stdio.h>
     
    #define ABSCISSES 11
    #define ORDONNEES 15
     
    int main (void)
    {
        int i,j;
        int (*ptr)[ORDONNEES] = NULL;
        int * ptr2 = NULL;
     
        ptr = calloc (ABSCISSES,sizeof (int[ORDONNEES));
     
        ptr2 = ptr;
        for (i=0;i<ABSCISSES*ORDONNEES;++i) *ptr2++=i;
     
        for (i=0;i<ABSCISSES;++i)
        for (j=0;j<ORDONNEES;++j) printf ("%d ",ptr[i][j]);
     
        free (ptr);
     
        puts ("");
        return 0;
    }

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    $ ./test
    0 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

    À la ligne 19, on voit que le pointeur « ptr », une fois initialisé correctement, s'utilise comme un tableau ordinaire à deux dimensions. En outre, je remplis ce tableau linéairement, à la ligne 16, et les valeurs sont restituées correctement et dans le bon ordre.

    On s'éclate avec les pointeurs non ???
    Oui. :-)

  19. #59
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    À obsidian :
    Citation Envoyé par Obsidian Voir le message
    Avant toute chose, je trouve que la qualité rédactionnelle de ton code est déjà remarquable.
    Je trouve que c'est plus pratique de trouver ces erreurs dans un code clairement rédigé, d'autant plus que je n'aime pas qu'un de mes programmes s'obstine à ne pas fonctionner. J'ai donc choisi mon camp.
    Citation Envoyé par Obsidian Voir le message
    En fait, tu mets le doigt sur un détail subtil mais très fondamental avec les tableaux à plusieurs dimensions
    Vu le nombre de fois qu'on me dit que je mets mon doigt sur un détail subtil ou épineux, je vais finir par croire que j'ai un don inné pour ça.

    En revanche, tu as utilisé plusieurs fois la déclaration int (* pTab) [NB_ORDONNEES] = NULL; mais je ne saisis la différence entre celle-ci et la déclaration int *pLigne[NB_ORDONNEES] = {NULL};.

    À Sve@r :
    Citation Envoyé par Sve@r Voir le message
    C'est pour ça que j'ai dit une fois que généralement, on allait rarement dépasser la 3D. Ca devient trop galère ensuite pour s'en sortir sans se ramasser la tronche en segfault (et encore, là c'est quand on a de la chance car quand on n'en a pas, le programme tourne, foireux mais il tourne jusqu'au jour où tu rajoutes un printf() à 40km plus bas et là il foire et toi, tu cherches pourquoi ça foire avec un printf et là tu galères...). Et que j'ai parlé aussi de structures remplaçant les dimensions...
    Sans vouloir te vexer je trouve ta technique avec les structures est un brin longue et lourde, même si c'est sûr qu'elle fonctionne.
    Citation Envoyé par Sve@r Voir le message
    Argh fallait pas lui dire. Ca le faisait chercher un peu
    Roh ! Vil faquin cachotier !
    Le pire dans tout ça, c'est que je pensais que c'était memmove() qui pouvait entraîner une perte de données par écrasement, et non l'inverse. Par contre, il faut, soit le faire exprès (auquel cas on utilisera memcpy()), soit manquer cruellement de chance pour que la zone-source et la zone destinataire se chevauchent.
    Citation Envoyé par Sve@r Voir le message
    On s'éclate avec les pointeurs non ???
    Si ce n'était pas le cas, j'aurais déjà déserté.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  20. #60
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Citation Envoyé par VivienD Voir le message
    Je trouve que c'est plus pratique de trouver ces erreurs dans un code clairement rédigé, d'autant plus que je n'aime pas qu'un de mes programmes s'obstine à ne pas fonctionner. J'ai donc choisi mon camp.
    Tu as choisi le bon !

    Vu le nombre de fois qu'on me dit que je mets mon doigt sur un détail subtil ou épineux, je vais finir par croire que j'ai un don inné pour ça.
    Si tu arrives à ne pas t'en dégoûter, c'est de cette façon que tu progresseras le plus vite.

    En revanche, tu as utilisé plusieurs fois la déclaration int (* pTab) [NB_ORDONNEES] = NULL; mais je ne saisis la différence entre celle-ci et la déclaration int *pLigne[NB_ORDONNEES] = {NULL};.
    — Le premier est un pointeur sur un tableau d'entiers ;
    — Le second est un tableau de pointeurs d'entiers.

    Ça se voit d'ailleurs aux accolades « { » et « } » que tu es obligé de mettre. Elles indiquent que tu initialises un tableau. Dans le premier cas, on déclare bien un pointeur seul, quoi qu'il pointe. C'est pour cela que je peux directement lui affecter NULL, sans autre artifice.

    À Sve@r :
    Sans vouloir te vexer je trouve ta technique avec les structures est un brin longue et lourde, même si c'est sûr qu'elle fonctionne.
    Cela dit, les structures sont extrêmement utilisées en C, pour des choses similaires. Le seul inconvénient à vectoriser des structures est l'éventuel padding que le compilateur peut ajouter et qu'il faut annuler explicitement le cas échéant, avec des directives qui ne sont pas toujours portables.

    Le pire dans tout ça, c'est que je pensais que c'était memmove() qui pouvait entraîner une perte de données par écrasement, et non l'inverse. Par contre, il faut, soit le faire exprès (auquel cas on utilisera memcpy()), soit manquer cruellement de chance pour que la zone-source et la zone destinataire se chevauchent.
    Ce n'est pas tout-à-fait vrai.

    D'abord on préfère avoir des programmes déterministes ! :-) Si il y a une chance même infime qu'un bug se produise, alors le problème doit être corrigé avant d'être mis en production. En plus, ces cas improbables lorsqu'on fonctionne en mono-tâche se révèlent redoutables une fois mis en parallèle dans un environnement multi-thread. Ils deviennent des races conditions, très difficiles à débuguer.

    Ensuite, les fonctions malloc() et calloc() ne te renverront jamais l'adresse d'un segment déjà occupé, c'est évident. Par contre, toi, tu peux avoir à déplacer des données au sein de ton propre bloc de mémoire.

    Le cas le plus simple est justement celui d'un tableau à une dimension que tu voudrais faire évoluer dynamiquement avec realloc(). Tant que tu ne fais qu'ajouter des éléments au bout du tableau, pas de problème. Mais à partir du moment où tu veux en insérer ou en supprimer, tu es obligé de recopier tous les éléments à partir d'une position n (et jusqu'à la fin) vers une position n+1 ou n-1, respectivement. Donc — par définition — tu vas écraser les données sources. L'important est que cela soit fait dans le bon ordre.

Discussions similaires

  1. Réponses: 4
    Dernier message: 31/01/2012, 17h36
  2. Question d'ordre général sur les macros sur excel
    Par tzehani dans le forum Macros et VBA Excel
    Réponses: 14
    Dernier message: 29/08/2007, 05h16
  3. [Portlet] Questions d'ordre général sur les portlets
    Par Chabin dans le forum Portails
    Réponses: 1
    Dernier message: 25/06/2007, 23h20
  4. Questions d'ordre general sur les design CSS
    Par Clorish dans le forum Mise en page CSS
    Réponses: 20
    Dernier message: 19/06/2007, 13h20
  5. question (peut-être idiote) sur les vues
    Par LadyArwen dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 26/03/2003, 10h35

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