Existe-t-il un cas où le fait de manquer un #include
casserait le logiciel au moment de l'exécution, alors que la construction continue?
En d'autres termes, est-il possible que
#include "some/code.h"
complexLogic();
cleverAlgorithms();
et
complexLogic();
cleverAlgorithms();
serait à la fois construire avec succès, mais se comporter différemment?
#include
d.#include <vld.h>
dans une position stratégique dans votre code. La suppression ou l'ajout de cet en-tête VLD ne "casse" pas le programme, mais il affecte considérablement le comportement d'exécution. J'ai vu VLD ralentir un programme au point qu'il est devenu inutilisable.Réponses:
Oui, c'est parfaitement possible. Je suis sûr qu'il existe de nombreuses façons, mais supposons que le fichier include contienne une définition de variable globale qui a appelé un constructeur. Dans le premier cas, le constructeur s'exécute et dans le second, il ne s'exécute pas.
Mettre une définition de variable globale dans un fichier d'en-tête est un mauvais style, mais c'est possible.
la source
<iostream>
dans la bibliothèque standard fait précisément cela; si une unité de traduction inclut<iostream>
alors l'std::ios_base::Init
objet statique sera construit au démarrage du programme, initialisant les flux de caractères,std::cout
etc., sinon ce ne sera pas le cas.Oui, c'est possible.
Tout ce qui concerne
#include
s se produit au moment de la compilation. Mais au moment de la compilation, les choses peuvent changer le comportement à l'exécution, bien sûr:some/code.h
:puis
Avec le
#include
, la résolution de surcharge trouve le plus appropriéfoo(int)
et imprime donc à la1
place de2
. De plus, puisqueFOO
est défini, il imprime en outreFOO
.Ce ne sont que deux exemples (non liés) qui me sont venus à l'esprit immédiatement, et je suis sûr qu'il y en a beaucoup plus.
la source
Juste pour souligner le cas trivial, les directives de précompilateur:
Et alors
C'est pathologique, peut-être, mais un cas connexe s'est produit:
Ça a l'air inoffensif. Essaie d'appeler
std::max
. Cependant, windows.h définit max commeSi tel était le cas
std::max
, ce serait un appel de fonction normal qui évalue f () une fois et g () une fois. Mais avec windows.h là-dedans, il évalue maintenant f () ou g () deux fois: une fois pendant la comparaison et une fois pour obtenir la valeur de retour. Si f () ou g () n'était pas idempotent, cela peut provoquer des problèmes. Par exemple, si l'un d'eux se trouve être un compteur qui renvoie un nombre différent à chaque fois ...la source
using namespace std;
et utilisezstd::max(f(),g());
, le compilateur va attraper le problème (avec un message obscur, mais au moins pointant vers le site d'appel).Il est possible de manquer une spécialisation de modèle.
la source
Incompatibilité binaire, accès à un membre ou pire encore, appel d'une fonction de la mauvaise classe:
Une fonction l'utilise, et c'est ok:
Apporter une autre version de la classe:
En utilisant les fonctions principales, la deuxième définition modifie la définition de classe. Cela conduit à une incompatibilité binaire et se bloque simplement lors de l'exécution. Et corrigez le problème en supprimant le premier include dans main.cpp:
Aucune des variantes ne génère une erreur de temps de compilation ou de liaison.
La situation vice versa, l'ajout d'une inclusion corrige le crash:
Ces situations sont encore plus difficiles à résoudre lors de la correction de bogues dans une ancienne version du programme ou lors de l'utilisation d'une bibliothèque externe / dll / objet partagé. C'est pourquoi il faut parfois respecter les règles de compatibilité descendante binaire.
la source
Je tiens à souligner que le problème existe également en C.
Vous pouvez dire au compilateur qu'une fonction utilise une convention d'appel. Si vous ne le faites pas, le compilateur devra deviner qu'il utilise celui par défaut, contrairement à C ++ où le compilateur peut refuser de le compiler.
Par exemple,
main.c
foo.c
Sous Linux sur x86-64, ma sortie est
Si vous omettez le prototype ici, le compilateur suppose que vous avez
Et la convention pour les listes d'arguments non spécifiées exige que cela
float
soit convertidouble
pour être passé. Donc, bien que j'aie donné1.0f
, le compilateur le convertit1.0d
pour le transmettrefoo
. Et selon le supplément du processeur d'architecture AMD64 de l'interface binaire d'application System V, ledouble
obtient est transmis dans les 64 bits les moins significatifs dexmm0
. Maisfoo
attend un flottant, et il le lit à partir des 32 bits les moins significatifs dexmm0
, et obtient 0.la source