Survivre à la version de lancement donne un bon aperçu.
Les choses que j'ai rencontrées - la plupart sont déjà mentionnées
Initialisation des variables de loin la plus courante. Dans Visual Studio, les builds de débogage initialisent explicitement la mémoire allouée à des valeurs données, voir par ex. Valeurs de la mémoire ici. Ces valeurs sont généralement faciles à repérer, elles provoquent une erreur hors limites lorsqu'elles sont utilisées comme index ou une violation d'accès lorsqu'elles sont utilisées comme pointeur. Un booléen non initialisé est vrai, cependant, et peut causer des bogues de mémoire non initialisée passant inaperçus pendant des années.
Dans les versions où la mémoire n'est pas explicitement initialisée, elle conserve simplement le contenu qu'elle avait auparavant. Cela conduit à de "drôles de valeurs" et à des plantages "aléatoires", mais aussi souvent à des plantages déterministes qui nécessitent l'exécution d'une commande apparemment sans rapport avec la commande qui provoque le plantage. Cela est dû au fait que la première commande "configure" l'emplacement mémoire avec des valeurs spécifiques, et que lorsque les emplacements mémoire sont recyclés, la seconde commande les voit comme des initialisations. C'est plus courant avec les variables de pile non initialisées qu'avec le tas, mais le dernier cas m'est aussi arrivé.
L'initialisation de la mémoire brute peut également être différente dans une version publiée si vous démarrez à partir de Visual Studio (débogueur attaché) ou à partir de l'Explorateur. Cela crée des bogues dans les versions les plus "belles" qui n'apparaissent jamais dans le débogueur.
Optimisations valides viennent en second lieu dans mon expérience. La norme C++ permet de nombreuses optimisations qui peuvent surprendre mais qui sont tout à fait valables, par exemple lorsque deux pointeurs aliasent le même emplacement mémoire, l'ordre d'initialisation n'est pas pris en compte, ou lorsque plusieurs threads modifient les mêmes emplacements mémoire, et que l'on s'attend à ce que le thread B voie dans un certain ordre les modifications apportées par le thread A. Souvent, le compilateur est blâmé pour cela. Pas si vite, jeune yedi ! - voir ci-dessous
Timing Les versions ne sont pas seulement "plus rapides", pour diverses raisons (optimisations, fonctions de journalisation fournissant un point de synchronisation des threads, code de débogage comme les assertions non exécutées, etc.), le timing relatif entre les opérations change aussi considérablement. Le problème le plus courant découvert par ce biais est celui des conditions de course, mais aussi des blocages et de la simple exécution "dans un ordre différent" de code basé sur des messages, des temps ou des événements. Même s'il s'agit de problèmes de timing, ils peut être étonnamment stables d'une version à l'autre et d'une plate-forme à l'autre, avec des reproductions qui "fonctionnent toujours, sauf sur PC 23".
Octets de garde . Les constructions de débogage mettent souvent des octets de garde (plus nombreux) autour d'instances et d'allocations sélectionnées, pour se protéger contre les débordements d'index et parfois les sous-débordements. Dans les rares cas où le code s'appuie sur des offsets ou des tailles, par exemple pour sérialiser des structures brutes, ils sont différents.
Autres différences de code Certaines instructions - par exemple les assertions - n'ont aucune valeur dans les builds de version. Parfois, elles ont des effets secondaires différents. Ceci est courant avec les macros, comme dans la classique (avertissement : erreurs multiples)
#ifdef DEBUG
#define Log(x) cout << #x << x << "\n";
#else
#define Log(x)
#endif
if (foo)
Log(x)
if (bar)
Run();
Ce qui, dans un build de version, correspond à si (foo && bar) Ce type d'erreur est très très rare avec du code C/C++ normal, et des macros qui sont correctement écrites.
Bugs du compilateur Cela n'arrive vraiment jamais. Eh bien, cela arrive, mais il est préférable, pour la majeure partie de votre carrière, de supposer que ce n'est pas le cas. En dix ans de travail avec la VC6, j'en ai trouvé un où je suis toujours convaincu qu'il s'agit d'un bogue de compilateur non corrigé, par rapport à des dizaines de modèles (peut-être même des centaines d'instances) avec une compréhension insuffisante de l'écriture (alias la norme).
2 votes
Bien que j'aie essayé de répondre à votre question, cela n'aide peut-être pas beaucoup à résoudre le problème. Premièrement, obtenez un bon reproducteur. Ensuite, activez les informations de débogage dans le build de la version. Le crash est toujours là ? non --> peut être le timing ou l'initialisation. Oui --> utiliser un débogueur (distant).