44 votes

Pourquoi les sorties de printf et de std::cout sont-elles différentes ?

J'ai essayé le code suivant de C++. Cependant, les sorties de printf y std::cout sont différentes. Pourquoi ?

struct Foo
{
    int a;
    int b;
    int c;
};

int main()
{
    printf("%d\n", &Foo::c);  // The output is 8
    std::cout << &Foo::c << "\n"; // The output is 1
}

25 votes

main.cpp:22:27: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int Foo::*’ [-Wformat=]

0 votes

@Angew -- Je répondais à un commentaire qui a maintenant été supprimé. Il disait que Foo::c n'avait pas été initialisé.

78voto

geza Points 13730

printf("%d\n", &Foo::c) il s'agit d'un comportement non défini, car &Foo::c n'est pas un entier, mais un pointeur vers un membre (mais, en fait, il est habituel que le compilateur stocke le pointeur vers un membre de données en tant que décalage, et en tant que 8 est le décalage de Foo::c , 8 est imprimé).

std::cout << &Foo::c : ceci imprime la valeur &Foo::c . Comme iostream n'a pas de pointeur vers l'imprimante membre, il choisit la plus proche : il la convertit en bool et l'imprime en tant que nombre entier. Comme &Foo::c converti en bool es true , 1 est imprimé.

21voto

StoryTeller Points 6139

La sortie est différente car le comportement de votre printf est indéfinie.

Un pointeur sur le membre (comme celui produit par &Foo::c ) n'est pas un nombre entier. Le site printf s'attend à un nombre entier, puisque vous lui avez demandé de le faire avec la fonction %d spécificateur.

Vous pouvez le modifier en ajoutant un cast à bool comme ceci :

printf("%d\n", (bool)&Foo::c)

Un pointeur vers un membre peut être converti en un bool (ce que vous faites avec le cast), et la fonction bool puis subit une promotion intégrale vers un int du fait qu'il s'agit d'un argument variadique intégral d'une fonction variadique.

En parlant de la conversion à bool c'est exactement la conversion qui est appliquée implicitement en essayant d'appeler std::ostream 's operator<< . Puisqu'il n'existe pas de surcharge de l'opérateur qui supporte les pointeurs vers les membres, la résolution de surcharge en sélectionne une autre qui est appelable après avoir converti implicitement &Foo::c en un booléen.

5voto

Lorehead Points 953

En plus de la réponse plus littérale sur la raison pour laquelle le compilateur a interprété votre code de la façon dont il l'a fait : vous semblez avoir un problème XY Vous essayez de formater un pointeur vers un membre comme un entier, ce qui suggère fortement que vous vouliez faire quelque chose de différent.

Si ce que vous vouliez était un int valeur stockée dans .c vous devez soit créer une instance Foo some_foo; et prendre some_foo.c ou bien vous devez déclarer Foo::c a static membre, donc il y a un sans ambiguïté Foo::c dans toute la classe. Ne prenez pas l'adresse dans ce cas.

Si ce que vous vouliez, c'était de prendre une adresse de la .c membre d'une Foo vous devez faire comme ci-dessus pour que Foo::c es static et se réfère à une variable spécifique, ou bien déclarer une instance et prendre son nom. .c membre, puis prenez l'adresse. La bonne printf() pour un pointeur d'objet est %p et pour imprimer une représentation de pointeur d'objet avec <iostream> et le convertir en void* :

printf( "%p\n", &some_foo.c );
std::cout << static_cast<void*>{&some_foo.c} << '\n';

Si ce que vous voulez est le décalage de Foo::c au sein de la classe Foo vous voulez que le offsetof() macro dans <stddef.h> . Puisque sa valeur de retour est size_t qui n'est pas de la même taille que int sur les plates-formes 64 bits, vous devez soit convertir le résultat explicitement, soit passer le code d'accès à la base de données. printf() el z spécificateur de type :

#include <stddef.h>

/* ... */

  constexpr size_t offset_c = offsetof( Foo, c );
  printf( "%zu\n", offset_c );
  cout << offset_c << '\n';

Quoi que vous essayiez de faire, si votre compilateur ne vous a pas averti de l'incompatibilité de type, vous devriez activer davantage d'avertissements. Ceci est particulièrement vrai pour quelqu'un qui code par essais et erreurs jusqu'à ce que le programme se compile.

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