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 :

((double *)2) equivalent à ((double *)0)


Sujet :

C

  1. #1
    Futur Membre du Club
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2007
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2007
    Messages : 8
    Points : 8
    Points
    8
    Par défaut ((double *)2) equivalent à ((double *)0)
    Salut,

    Si je fais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    double *j;
    j = (double *)2;
    Ce pointeur aura-t-il le même comportement qu'un pointeur nul ? Je constate qu'il est à l'origine des mêmes erreurs, comment s'en proteger puisque (double *)2 != NULL. A quoi sert le cast ici ?

    D'avance merci.

  2. #2
    Membre expert
    Avatar de Metalman
    Homme Profil pro
    Enseignant-Chercheur
    Inscrit en
    Juin 2005
    Messages
    1 049
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Enseignant-Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 049
    Points : 3 532
    Points
    3 532
    Par défaut
    Dans ce cas, tu essayes de dire que J doit contenir l'adresse 2... pas l'adresse pointant vers 2.

    Si tu souhaites allouer la valeur 2 à J, tu dois d'abord allouer de l'espace avec malloc, et mettre 2 dedans.

    j = (double*) 2; est évidemment différent de NULL.

    Tout dépend de ton code si le comportement semble être le même que si j contenait NULL.

    Pourrais-tu nous montrer le code qui ne fonctionne pas ?
    --
    Metalman !

    Attendez 5 mins après mes posts... les EDIT vont vite avec moi...
    Les flags de la vie : gcc -W -Wall -Werror -ansi -pedantic mes_sources.c
    gcc -Wall -Wextra -Werror -std=c99 -pedantic mes_sources.c
    (ANSI retire quelques fonctions comme strdup...)
    L'outil de la vie : valgrind --show-reachable=yes --leak-check=full ./mon_programme
    Et s'assurer que la logique est bonne "aussi" !

    Ma page Developpez.net

  3. #3
    Futur Membre du Club
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2007
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2007
    Messages : 8
    Points : 8
    Points
    8
    Par défaut
    Voilà ce que je veux faire, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    double *j;
    j = (double *)2;
    if(j != NULL)
        scanf("%lf", j);
    else
        ....

  4. #4
    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 Docteur Dewi Voir le message
    Ce pointeur aura-t-il le même comportement qu'un pointeur nul ? Je constate qu'il est à l'origine des mêmes erreurs,
    Si les erreurs en question sont en fait une segfault, ce n'est pas le fait que le pointeur soit NULL en soi qui pose problème, mais le fait qu'il référence une adresse invalide. Si tu essaies ensuite d'y lire ou écrire quelque chose, il est normal que cela plante.

    Et « 2 » est une adresse tout aussi invalide que NULL.

    A quoi sert le cast ici ?
    Un transtypage sert à demander au compilateur de convertir une valeur connue en son équivalent le plus proche dans un autre format. Par exemple, si tu convertis un short en long, soit ton nombre initial est non signé et le compilateur comblera le reste avec des zéros, soit le nombre est signé et là, le programme propagera le bit de signe.

    Or, techniquement, une adresse mémoire est bien un entier naturel binaire mais sémantiquement, ça n'a rien à voir : par exemple, il n'y a aucun sens à additionner deux adresses mémoire (mais il y en a à les soustraire). Comme on le disait dans un autre commentaire, ce n'est pas parce que, dans ta rue, tu habites au № 5 et un voisin au
    № 8 qu'à vous deux, vous habitez au № 13.

    Tu as tout-à-fait le droit d'affecter une adresse constante à un pointeur si tu sais ce que tu fais (c'est utile dans l'embarqué et avec les micro-contrôleurs) mais comme il ne s'agit pas de la même chose, la conversion n'est pas implicite. Elle le sera si tu utilises le compilateur en l'état mais si tu lui passes des options plus strictes, notamment pour respecter des normes, tu auras des messages d'avertissement si tu ne le fais pas.

    Citation Envoyé par Docteur Dewi Voir le message
    Voilà ce que je veux faire, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    double *j;
    j = (double *)2;
    if(j != NULL)
        scanf("%lf", j);
    else
        ....
    Et c'est censé faire quoi (à part planter) ? « scanf() » attend en paramètre un emplacement mémoire où il va pouvoir déposer la valeur que l'utilisateur a saisi. Cet emplacement est généralement celui d'une variable.

    Ici, ton code est syntaxiquement correct, mais il indique à scanf() de déposer la donnée à partir de l'adresse 0x00000002 en RAM, ce qui va donc planter.

  5. #5
    Futur Membre du Club
    Profil pro
    Étudiant
    Inscrit en
    Septembre 2007
    Messages
    8
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Septembre 2007
    Messages : 8
    Points : 8
    Points
    8
    Par défaut
    Je vois, merci beaucoup pour la réponse complète !

    au final faire : j = 2; ou j = (double *)2; cela revient au même (le warning en moins) ? A quoi sert le cast ?

    Je crois que c'est tellement tordu et nul comme exercice que je me suis mis à chercher trop loin.

  6. #6
    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 Docteur Dewi Voir le message
    au final faire : j = 2; ou j = (double *)2; cela revient au même (le warning en moins) ? A quoi sert le cast ?
    C'est expliqué au dessus : l'expression « 2 » en elle-même est considérée par le compilateur comme un nombre entier. Si tu écris « j = 2 », il considère avec raison que tu essaies d'affecter un nombre entier à un pointeur. Comme les adresses mémoire sont représentées par des entiers binaires, il le fait sans se plaindre puisqu'il peut faire directement l'affectation sans avoir à faire de conversion.

    Il en reste que ce n'est pas correct en soi parce que ça ne veut rien dire. Tu commences donc par transtyper explicitement ta valeur en « double * » pour dire que c'est bien ce que tu veux faire, d'une part, et laisser le compilateur faire des adaptations si c'est nécessaire.

    Un exemple plus parlant encore serait celui-ci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
        int         x;
        int       * i;
        double    * j;
     
        i = &x;
        j = i;
    La ligne 5 est correcte parce que i est un pointeur sur un entier et que je lui passe l'adresse de x, qui est un entier.
    La ligne 6, en revanche, l'est beaucoup moins : il s'agit de pointeurs et donc d'adresses mémoire dans les deux cas. Ces adresses ont toutes deux le même format donc il n'y a aucune conversion numérique à faire. Néanmoins, i est de type « int * » et j est du type « double * ». L'affectation provoquera forcément un avertissement puisque si on sait que c'est un « int » qui se trouve à une adresse donnée, ça ne peut pas être un « double ».

  7. #7
    Membre éclairé
    Avatar de Kirilenko
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2011
    Messages
    234
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 27
    Localisation : France, Gironde (Aquitaine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 234
    Points : 807
    Points
    807
    Par défaut
    Bonjour,

    Citation Envoyé par Docteur Dewi
    au final faire : j = 2; ou j = (double *)2; cela revient au même (le warning en moins) ? A quoi sert le cast ?
    Intuitivement, les deux formes sont effectivement identiques. Si on y regarde de plus près, cependant, ce n'est pas tout à fait la même chose. Histoire d'être sûrs qu'on parle bien de la même chose, considérons l'instruction suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double *j = (double *)2;
    Commençons par analyser la nécessité du transtypage, c'est-à-dire en quoi l'instruction suivante est incorrecte :

    Notre point de départ sera le passage de la norme sur l'opérateur d'affectation.

    Citation Envoyé par C11 (n1570), § 6.5.16.1 Simple assignment
    One of the following shall hold:
    — the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
    — the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
    — the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
    — the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
    — the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
    — the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.
    C'est long, mais, en regardant attentivement, on voit qu'aucune clause ne garantit l'affectation d'une constante entière à une opérande gauche de type pointeur. Il s'agit donc d'une violation de contrainte. L'implémentation (ici, le compilateur), se doit alors de produire un message de diagnostic dans un tel cas.

    Citation Envoyé par C11 (n1570), § 5.1.1.3 Diagnostics
    A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint, even if the behavior is also explicitly specified as undefined or implementation-defined.
    À titre d'exemple, gcc (4.4.5) et clang (2.8) sont standards sur ce point, en indiquant respectivement « initialization makes pointer from integer without a cast » et « incompatible integer to pointer conversion initializing 'double *' with an expression of type 'int' ». C'est pourquoi, pour obtenir une affectation correcte, on va essayer de forcer le typage de l'expression avec l'opérateur de trantypage. La seule contrainte de type qui le régit (mis à part l'interdiction de conversion d'un flottant vers un pointeur) est la suivante :

    Citation Envoyé par C11 (n1570), § 6.5.4 Cast operators
    Unless the type name specifies a void type, the type name shall specify atomic, qualified, or unqualified scalar type, and the operand shall have scalar type.
    Or, double * et int sont des types scalaires (une catégorie qui regroupe les types arithmétiques et les types pointeurs). L'affectation est également correcte en ce qui concerne la destination.

    Citation Envoyé par C11 (n1570), § 6.3.2.3 Pointers
    An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.)
    Le pointeur résultant est donc défini par l'implémentation. Évidemment, il serait une mauvaise idée de le déréférencer, car cela conduirait à un comportement indéterminé.

    Citation Envoyé par C11 (n1570), § 6.5.3.2 Address and indirection operators
    If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined.
    En résumé

    • Du point de vue du langage, j = 2 et j = (double *)2 sont différents.
    • Le transtypage permet d'éviter la violation de contrainte.
    • Le pointeur résultant d'une telle affectation est défini par l'implémentation.


    Voilà, c'était pour le petit passage « norme » de la journée.

    Bonne journée !
    Récursivité en C : épidémie ou hérésie ?

    "Pour être un saint dans l'Église de l'Emacs, il faut vivre une vie pure. Il faut se passer de tout logiciel propriétaire. Heureusement, être célibataire n'est pas obligé. C'est donc bien mieux que les autres églises" - Richard Stallman

Discussions similaires

  1. Double jointure avec double group by
    Par annebe dans le forum DB2
    Réponses: 0
    Dernier message: 28/01/2013, 14h52
  2. Réponses: 0
    Dernier message: 18/05/2011, 10h47
  3. problème de précision : double ou long double?
    Par pimousse3000 dans le forum C++
    Réponses: 3
    Dernier message: 24/04/2007, 19h13
  4. Réponses: 3
    Dernier message: 29/01/2007, 13h39
  5. réels aléatoires (double ou long double)
    Par koushkov dans le forum C++
    Réponses: 16
    Dernier message: 08/05/2006, 18h44

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