Conversion en void ** sur différents compilateurs

9

J'ai exécuté le code suivant via différents compilateurs:

int main()
{
    float **a;
    void **b;
    b = a;
}

D'après ce que j'ai pu rassembler, il nevoid ** s'agit pas d' un pointeur générique, ce qui signifie que toute conversion à partir d'un autre pointeur ne devrait pas compiler ou au moins lancer un avertissement. Cependant, voici mes résultats (tous effectués sous Windows):

  • gcc - Lance un avertissement, comme prévu.
  • g ++ - Lance une erreur, comme prévu (cela est dû au typage moins permissif de C ++, non?)
  • MSVC (cl.exe) - Ne lance aucun avertissement, même avec / Wall spécifié.

Ma question est: est-ce que je manque quelque chose sur le tout et y a-t-il une raison spécifique pour laquelle MSVC ne produit pas d'avertissement? MSVC fait produire un avertissement lors de la conversion à partir de void ** à float **.

Autre chose à noter: si je remplace a = bpar la conversion explicite a = (void **)b, aucun des compilateurs ne lance d'avertissement. Je pensais que ce devrait être un casting invalide, alors pourquoi n'y aurait-il pas d'avertissement?

La raison pour laquelle je pose cette question est parce que je commençais à apprendre CUDA et dans le guide de programmation officiel ( https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#device-memory ) le code suivant peut être trouvé:

// Allocate vectors in device memory
float* d_A;
cudaMalloc(&d_A, size);

qui devrait effectuer une conversion implicite en void **for &d_A, car le premier argument de cudaMallocest de type void **. Un code similaire peut être trouvé dans toute la documentation. Est-ce juste un travail bâclé de la fin de NVIDIA ou est-ce que je manque encore une fois quelque chose? Depuis nvccutilise MSVC, le code compile sans avertissements.

CaptainProton42
la source
3
Erreurs des 3 postés en direct: godbolt.org/z/GQWMNo
Richard Critten
3
Les erreurs de code pour moi avec MSVC. Quelle version utilisez-vous? Mais oui, ce void**n'est pas un pointeur générique. Seulement void*.
NathanOliver
Merci pour la réponse rapide! 19.24.28315 pour x64 apparemment? Je n'ai jamais vraiment utilisé MSVC auparavant.
CaptainProton42
2
(void**)est une distribution de style c explicite. Il dit au compilateur de ne pas regarder de près ce que vous faites et de vous faire confiance. Il s'agit d'un remplacement explicite du type de système de sécurité et les compilateurs sont tenus d'accepter pratiquement n'importe quelle sorte de conversion. Les moulages de style C doivent être évités, ils sont beaucoup trop puissants. Utilisez des castings C ++ comme ceux static_castqui se plaindront si vous essayez de faire quelque chose qui n'a pas de sens.
François Andrieux
@RichardCritten langue incorrecte - aucune erreur godbolt.org/z/RmFpgN C ++ cuda nécessite des transtypages explicites comme d'habitude.
P__J__

Réponses:

4

Suis-je en train de manquer quelque chose et est-ce qu'il y a une raison particulière pour laquelle MSVC ne produit pas d'avertissement? MSVC produit un avertissement lors de la conversion de void ** en float **

Cette affectation sans transtypage est une violation de contrainte, donc un compilateur conforme standard affichera un avertissement ou une erreur. Cependant, MSVC n'est pas une implémentation C entièrement conforme.

Autre chose à noter: si je remplace a = b par la conversion explicite a = (void **) b, aucun des compilateurs ne lance d'avertissement. Je pensais que ce devrait être un casting invalide, alors pourquoi n'y aurait-il pas d'avertissement?

Les conversions de pointeurs via un casting sont autorisées dans certaines situations. La norme C dit ce qui suit dans la section 6.3.2.3p7:

Un pointeur vers un type d'objet peut être converti en pointeur vers un type d'objet différent. Si le pointeur résultant n'est pas correctement aligné pour le type référencé, le comportement n'est pas défini. Sinon, une fois reconverti, le résultat doit être égal au pointeur d'origine. Lorsqu'un pointeur vers un objet est converti en pointeur vers un type de caractère, le résultat pointe vers l'octet adressé le plus bas de l'objet. Des incréments successifs du résultat, jusqu'à la taille de l'objet, fournissent des pointeurs vers les octets restants de l'objet.

Vous pouvez donc convertir entre les types de pointeurs à condition qu'il n'y ait pas de problèmes d'alignement et que vous ne reconvertissiez que (sauf si la cible est a char *).

float* d_A;
cudaMalloc(&d_A, size);

...

Est-ce juste un travail bâclé de la fin de NVIDIA ou est-ce que je manque encore une fois quelque chose?

Vraisemblablement, cette fonction déréférence le pointeur donné et écrit l'adresse d'une certaine mémoire allouée. Cela voudrait dire qu'il essaie d'écrire sur un float *comme s'il s'agissait d'un void *. Ce n'est pas la même chose que la conversion typique de / vers a void *. À strictement parler, cela ressemble à un comportement indéfini, bien que cela "fonctionne" car les processeurs x86 modernes (lorsqu'ils ne sont pas en mode réel) utilisent la même représentation pour tous les types de pointeurs.

dbush
la source
@dbush Très instructif, merci! Je comprends pourquoi cela fonctionne si cela fonctionne. Pourtant, la plupart des compilateurs ne lanceraient-ils pas un avertissement ou même une erreur parce qu'ils &d_An'ont pas le type requis?
CaptainProton42
3
Faites attention à la façon dont vous interprétez cela, il peut y avoir et il y a des astuces de modèle entre si vous compilez CUDA avec un compilateur C ++
talonmies