web-dev-qa-db-fra.com

Méthode appropriée de conversion des types de pointeurs

Considérant le code suivant (et le fait que VirtualAlloc() renvoie un void* ):

BYTE* pbNext = reinterpret_cast<BYTE*>(
    VirtualAlloc(NULL, cbAlloc, MEM_COMMIT, PAGE_READWRITE));

pourquoi est-ce reinterpret_cast choisi au lieu de static_cast?

Je pensais que reinterpret_cast est OK pour par exemple caster des pointeurs vers et à partir de types entiers (comme par exemple DWORD_PTR), mais pour lancer à partir d'un void* à un BYTE*, n'est-ce pas static_cast D'ACCORD?

Existe-t-il des différences (subtiles?) Dans ce cas particulier, ou s'agit-il simplement de deux lancers de pointeurs valides?

La norme C++ a-t-elle une préférence pour ce cas, suggérant une voie au lieu de l'autre?

46
Mr.C64

Pour pointeurs convertibles en types fondamentaux les deux transtypages ont la même signification; vous avez donc raison que static_cast est correct.

Lors de la conversion entre certains types de pointeurs, il est possible que l'adresse mémoire spécifique contenue dans le pointeur doive changer .

C'est là que les deux modèles diffèrent. static_cast fera le réglage approprié. reinterpret_cast ne le sera pas.

Pour cette raison, c'est une bonne règle générale de static_cast entre les types de pointeurs, sauf si vous savez que reinterpret_cast est désiré.

38
Drew Dormann

Vous devez static_cast. Utilisez static_cast Dans les cas où vous annulez une conversion implicite.

Dans ce cas particulier, cependant, il n'y a pas de différence car vous effectuez une conversion à partir de void*. Mais en général, reinterpret_cast Entre deux pointeurs d'objet est défini comme étant (§5.2.10/7):

Un pointeur d'objet peut être explicitement converti en un pointeur d'objet d'un type différent. Lorsqu'une valeur v de type "pointeur vers T1" Est convertie en type "pointeur vers cv T2", le résultat est static_cast<cv T2*>(static_cast<cv void*>(v)) si T1 et T2 sont tous deux des types de mise en page standard et que les exigences d'alignement de T2 ne sont pas plus strictes que ceux de T1, ou si l'un ou l'autre type est void. Conversion d'une valeur de type "pointeur vers T1" En type "pointeur vers T2" (Où T1 Et T2 Sont des types d'objets et où l'alignement les exigences de T2 ne sont pas plus strictes que celles de T1) et revenir à son type d'origine donne la valeur du pointeur d'origine. Le résultat de toute autre conversion de pointeur de ce type n'est pas précisé.

Je souligne. Étant donné que T1 Pour vous est déjà void*, La conversion vers void* Dans reinterpret_cast Ne fait rien. Ce n'est pas vrai en général, c'est ce que Drew Dormann dit :

#include <iostream>

template <typename T>
void print_pointer(const volatile T* ptr)
{
    // this is needed by oversight in the standard
    std::cout << static_cast<void*>(const_cast<T*>(ptr)) << std::endl;
}

struct base_a {};
struct base_b {};
struct derived : base_a, base_b {};

int main()
{
    derived d;

    base_b* b = &d; // implicit cast

    // undo implicit cast with static_cast
    derived* x = static_cast<derived*>(b);

    // reinterpret the value with reinterpret_cast
    derived* y = reinterpret_cast<derived*>(b);

    print_pointer(&d);
    print_pointer(x);
    print_pointer(y);
}

Sortie:

00CBFD5B
00CBFD5B
00CBFD5C

(Notez que parce que y ne pointe pas réellement vers un derived, son utilisation est un comportement non défini.)

Ici, reinterpret_cast Propose une valeur différente car elle passe par void*. C'est pourquoi vous devez utiliser static_cast Quand vous le pouvez et reinterpret_cast Quand vous le devez.

23
GManNickG

En utilisant static_cast pour placer un pointeur vers et depuis void* est garanti pour conserver l'adresse.

reinterpret_cast d'autre part garantit que si vous convertissez le pointeur d'un type en un autre et que vous revenez au type d'origine, l'adresse est conservée.

Bien qu'avec la plupart des implémentations, vous obtiendrez les mêmes résultats en utilisant l'un de ces éléments, static_cast devrait être préféré.

Et avec C++11 Je m'en souviens, en utilisant reinterpret_cast pour void* a un comportement bien défini. Avant cela, ce comportement était interdit.

It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.

Proposition de résolution (août 2010):

Modifier le paragraphe 7 de 5.2.10 [expr.reinterpret.cast] comme suit:

Un pointeur d'objet peut être explicitement converti en un pointeur d'objet d'un type différent. Lorsqu'une valeur pr de type "pointeur vers T1" est convertie en type "pointeur vers cv T2", le résultat est static_cast (static_cast (v)) si T1 et T2 sont tous deux de type standard (3.9 [basic.types]) et que les exigences d'alignement de T2 ne sont pas plus strictes que celles de T1, ou si l'un ou l'autre type est nul.

La conversion d'une valeur de type "pointeur vers T1" en type "pointeur vers T2" (où T1 et T2 sont des types d'objet et où les exigences d'alignement de T2 ne sont pas plus strictes que celles de T1) et de retour à son type d'origine donne l'original valeur du pointeur. Le résultat de toute autre conversion de pointeur de ce type n'est pas spécifié.

Plus d'infos ici .

Merci à Jesse Good pour le lien.

8
Tuxdude