Résultat inattendu lorsque l’élément store C ++ dans std :: vector à partir de la valeur de retour de la fonction

Lorsque la fonction implique une réallocation, j’ai trouvé que certains compilateurs pouvaient enregistrer l’adresse avant l’appel de fonction. Il conduit la valeur de retour stockée dans l’adresse non valide.

Il y a un exemple pour expliquer le comportement dans la description ci-dessus.

#include  #include  using namespace std; vector A; int func() { A.push_back(3); A.push_back(4); return 5; } int main() { A.reserve(2); A.push_back(0); A.push_back(1); A[1] = func(); printf("%d\n", A[1]); return 0; } 

Il existe un compilateur C ++ commun et le résultat du test est le suivant.

  • GCC (GNU Comstackr Collection): Erreur d’exécution ou sortie 1
  • Clang: sortie 5
  • VC ++: sortie 5

Est-ce un comportement indéfini?

Le comportement est indéfini dans toutes les versions de C ++ antérieures à C ++ 17. La raison simple est que les deux côtés de l’opérateur d’affectation peuvent être évalués dans n’importe quel ordre:

  • En supposant que A[1] est évalué en premier, vous obtenez un int& référant au deuxième élément de A à ce point.
  • Ensuite, le func() est évalué, ce qui permet de réaffecter le stockage pour le vecteur, en laissant la référence int& récupérée précédemment.
  • Enfin, l’affectation est effectuée en écrivant sur un stockage non alloué. Étant donné que la mémoire cache des allocateurs standard, le système d’exploitation ne détecte souvent pas cette erreur.

Seulement en C ++ 17, la règle spéciale 20 pour la cession a été faite:

Dans chaque expression d’affectation simple E1 = E2 et chaque expression d’affectation de composé E1 @ = E2, chaque calcul de valeur et effet secondaire d’E2 est séquencé avant chaque calcul de valeur et effet secondaire de E1

Avec C ++ 17, A[1] doit être évalué après l’appel à func() , qui fournit alors un comportement défini et fiable.

Si vous vérifiez la documentation , sous “Invalidation de l’iterator”, vous verrez que push_back() peut invalider chaque iterator s’il change de capacité, puisqu’il devrait réallouer la mémoire. Rappelez-vous que, pour un std::vector , un pointeur est également un iterator valide. Parce que push_back() peut ou non réallouer, et que vous n’avez aucun moyen de savoir si cela se produira, le comportement est indéfini.