71 votes

Les valeurs des pointeurs sont différentes mais elles se comparent de manière égale. Pourquoi ?

Un court exemple produit un résultat bizarre !

#include <iostream>

using namespace std;

struct A { int a; };    
struct B { int b; };
struct C : A, B
{
    int c;
};

int main()
{
    C* c = new C;
    B* b = c;

    cout << "The address of b is 0x" << hex << b << endl;
    cout << "The address of c is 0x" << hex << c << endl;

    if (b == c)
    {
        cout << "b is equal to c" << endl;
    }
    else
    {
        cout << "b is not equal to c" << endl;
    }
}

La sortie devrait être la suivante :

The address of b is 0x003E9A9C
The address of c is 0x003E9A98
b is equal to c

Ce qui me fait me demander :

0x003E9A9C n'est pas égal à 0x003E9A98, mais le résultat est "b est égal à c".

87voto

Mike Seymour Points 130519

A C contient deux sous-objets, de type A y B . De toute évidence, ces objets doivent avoir des adresses différentes, puisque deux objets distincts ne peuvent pas avoir la même adresse ; ainsi, au maximum l'un d'entre eux peut avoir la même adresse que l'objet C objet. C'est pourquoi l'impression des pointeurs donne des valeurs différentes.

La comparaison des pointeurs ne consiste pas simplement à comparer leurs valeurs numériques. Seuls les pointeurs du même type peuvent être comparés, donc le premier doit être converti pour correspondre à l'autre. Dans ce cas, c est converti en B* . C'est exactement la même conversion que celle utilisée pour initialiser b en premier lieu : il ajuste la valeur du pointeur de manière à ce qu'il pointe vers le fichier B plutôt que le sous-objet C et les deux pointeurs sont maintenant égaux.

75voto

Arne Mertz Points 13966

La disposition de la mémoire d'un objet de type C ressemblera à quelque chose comme ça :

|   <---- C ---->   |
|-A: a-|-B: b-|- c -|
0      4      8     12

J'ai ajouté le décalage en octets de l'adresse de l'objet (dans une plateforme comme la vôtre avec sizeof(int) = 4).

Dans votre main, vous avez deux pointeurs, je vais les renommer en pb y pc pour plus de clarté. pc pointe vers le début de l'objet C entier, alors que pb pointe vers le début du sous-objet B :

   |   <---- C ---->   |
   |-A: a-|-B: b-|- c -|
   0      4      8     12
pc-^   pb-^

C'est la raison pour laquelle leurs valeurs sont différentes. 3E9A98+4 est 3E9A9C, en hexadécimal.

Si vous comparez maintenant ces deux pointeurs, le compilateur verra une comparaison entre un B* et un C* qui sont de types différents. Il doit donc appliquer une conversion implicite, s'il en existe une. pb ne peut être converti en un C* mais l'inverse est possible - il convertit pc en un B* . Cette conversion donnera un pointeur qui pointe sur le sous-objet B de où que ce soit. pc pointe vers - il s'agit de la même conversion implicite utilisée lorsque vous avez défini l'option B* pb = pc; . Le résultat est égal à pb évidemment :

   |   <---- C ---->   |
   |-A: a-|-B: b-|- c -|
   0      4      8     12
pc-^   pb-^
   (B*)pc-^

Ainsi, en comparant les deux pointeurs, le compilateur compare en fait les pointeurs convertis, qui sont égaux.

8voto

luk32 Points 5567

Je sais qu'il existe une réponse, mais celle-ci sera peut-être plus directe et étayée par un exemple.

Il y a une conversion implicite de C* a B* en c l'opérande ici if (b == c)

Si vous utilisez ce code :

#include <iostream>

using namespace std;

struct A { int a; };    
struct B { int b; };
struct C : A, B
{
    int c;
};

int main()
{
    C* c = new C;
    B* b = c;

    cout << "The address of b is 0x" << hex << b << endl;
    cout << "The address of c is 0x" << hex << c << endl;
    cout << "The address of (B*)c is 0x" << hex << (B*)c << endl;

    if (b == c)
    {
        cout << "b is equal to c" << endl;
    }
    else
    {
        cout << "b is not equal to c" << endl;
    }
}

Vous obtenez :

The address of b is 0x0x88f900c
The address of c is 0x0x88f9008
The address of (B*)c is 0x0x88f900c
b is equal to c

Así que c casté à B* a la même adresse que b . Comme prévu.

7voto

Nobilis Points 3511

Si je peux ajouter à l'excellente réponse de Mike, si tu les mets en tant que void* alors vous obtiendrez le comportement attendu :

if ((void*)(b) == (void*)(c))
    ^^^^^^^       ^^^^^^^

imprime

b is not equal to c

Faire quelque chose de similaire en C (le langage) irritait le compilateur en raison des différents types de pointeurs comparés.

J'ai eu :

warning: comparison of distinct pointer types lacks a cast [enabled by default]

2voto

Kaz Points 18072

En informatique (ou, plutôt, nous devrions dire en mathématiques), il peut y avoir de nombreuses notions d'égalité. Toute relation qui est symétrique, réflexive et transitive peut être employée comme égalité.

Dans votre programme, vous examinez deux notions d'égalité quelque peu différentes : l'identité d'implémentation au niveau du bit (deux pointeurs étant exactement à la même adresse) contre un autre type d'égalité basé sur l'identité d'objet, qui permet à deux vues sur le même objet, par le biais de références de type statique différent, d'être correctement considérées comme faisant référence au même objet.

Ces vues typées différemment utilisent des pointeurs qui n'ont pas la même valeur d'adresse, car ils s'accrochent à différentes parties de l'objet. Le compilateur le sait et génère donc le code correct pour la comparaison d'égalité qui tient compte de ce décalage.

C'est la structure des objets induite par l'héritage qui rend nécessaire l'existence de ces décalages. Lorsqu'il y a plusieurs bases (grâce à l'héritage multiple), une seule de ces bases peut être à l'adresse basse de l'objet, de sorte que le pointeur sur la partie de base est le même que le pointeur sur l'objet dérivé. Les autres parties de la base se trouvent ailleurs dans l'objet.

Ainsi, la comparaison naïve et bit à bit des pointeurs ne donnerait pas les bons résultats selon la vision orientée objet de l'objet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X