J'ai lu cette ligne dans un livre:
Il est prouvé impossible de construire un compilateur qui puisse réellement déterminer si une fonction C ++ changera ou non la valeur d'une variable particulière.
Le paragraphe parlait de la raison pour laquelle le compilateur est prudent lors de la vérification de la const-ness.
Pourquoi est-il impossible de construire un tel compilateur?
Le compilateur peut toujours vérifier si une variable est réaffectée, une fonction non-const y est appelée, ou si elle est passée en tant que paramètre non-const ...
c++
compiler-construction
Joueur de cricket
la source
la source
Réponses:
Pour la même raison que vous ne pouvez pas écrire un programme qui déterminera si un programme donné se terminera. C'est ce qu'on appelle le problème de l' arrêt , et c'est l'une de ces choses qui n'est pas calculable.
Pour être clair, vous pouvez écrire un compilateur qui peut déterminer qu'une fonction change la variable dans certains cas , mais vous ne pouvez pas en écrire un qui vous indique de manière fiable que la fonction changera ou ne changera pas la variable (ou s'arrêtera) pour toutes les fonctions possibles.
Voici un exemple simple:
Comment un compilateur peut-il déterminer, simplement en regardant ce code, s'il
foo
changera un joura
? Que ce soit le cas ou non dépend de conditions externes à la fonction, à savoir la mise en œuvre debar
. Il y a plus que cela à la preuve que le problème d'arrêt n'est pas calculable, mais il est déjà bien expliqué dans l'article Wikipédia lié (et dans tous les manuels de théorie du calcul), donc je n'essaierai pas de l'expliquer correctement ici.la source
Imaginez qu'un tel compilateur existe. Supposons également que pour plus de commodité, il fournisse une fonction de bibliothèque qui renvoie 1 si la fonction passée modifie une variable donnée et 0 lorsque la fonction ne le fait pas. Alors que doit imprimer ce programme?
la source
f
modifie la variable - pas si elle pourrait modifier la variable. Cette réponse est correcte.modifies_variable
depuis le source du compilateur, annulant totalement votre argument. (en supposant open-source, mais le point devrait être clair)Ne pas confondre "modifiera ou ne modifiera pas une variable étant donné que ces entrées" pour "a un chemin d'exécution qui modifie une variable."
La première est appelée détermination de prédicat opaque , et est trivialement impossible à décider - en dehors de la réduction du problème d'arrêt, vous pouvez simplement signaler que les entrées peuvent provenir d'une source inconnue (par exemple l'utilisateur). Cela est vrai pour tous les langages, pas seulement pour C ++.
Cette dernière instruction, cependant, peut être déterminée en examinant l'arbre d'analyse, ce que font tous les compilateurs d'optimisation. La raison pour laquelle ils le font est que les fonctions pures (et les fonctions référentiellement transparentes , pour une certaine définition de référentiellement transparentes ) ont toutes sortes d'optimisations intéressantes qui peuvent être appliquées, comme être facilement insérables ou avoir leurs valeurs déterminées au moment de la compilation; mais pour savoir si une fonction est pure, il faut savoir si elle peut jamais modifier une variable.
Donc, ce qui semble être une déclaration surprenante sur C ++ est en fait une déclaration triviale sur tous les langages.
la source
Je pense que le mot clé dans «si une fonction C ++ changera ou non la valeur d'une variable particulière» est «volonté». Il est certainement possible de construire un compilateur qui vérifie si une fonction C ++ est autorisée à changer la valeur d'une variable particulière, vous ne pouvez pas dire avec certitude que le changement va se produire:
la source
const
contrôles de qualité.Je ne pense pas qu'il soit nécessaire d'invoquer le problème d'arrêt pour expliquer que vous ne pouvez pas savoir algorithmiquement au moment de la compilation si une fonction donnée modifiera une certaine variable ou non.
Au lieu de cela, il suffit de souligner que le comportement d'une fonction dépend souvent des conditions d'exécution, que le compilateur ne peut pas connaître à l'avance. Par exemple
Comment le compilateur pourrait-il prédire avec certitude s'il
y
sera modifié?la source
Cela peut être fait et les compilateurs le font tout le temps pour certaines fonctions , c'est par exemple une optimisation triviale pour de simples accesseurs en ligne ou de nombreuses fonctions pures.
Ce qui est impossible, c'est de le savoir dans le cas général.
Chaque fois qu'il y a un appel système ou un appel de fonction provenant d'un autre module, ou un appel à une méthode potentiellement remplacée, tout peut arriver, y compris la prise de contrôle hostile de l'utilisation par un pirate d'un débordement de pile pour changer une variable non liée.
Cependant, vous devez utiliser const, éviter les globaux, préférer les références aux pointeurs, éviter de réutiliser des variables pour des tâches non liées, etc. qui faciliteront la vie du compilateur lors de l'exécution d'optimisations agressives.
la source
Il existe plusieurs moyens d'expliquer cela, dont l'un est le problème de l' arrêt :
Si j'écris un programme qui ressemble à ceci:
Est-ce que la valeur du
x
changement? Pour déterminer cela, vous devez d'abord déterminer si lado tons of complex stuff
pièce provoque le déclenchement de la condition - ou, plus élémentaire encore, si elle s'arrête. C'est quelque chose que le compilateur ne peut pas faire.la source
Vraiment surpris qu'il n'y ait pas de réponse qui utilise directement le problème d'arrêt! Il y a une réduction très simple de ce problème au problème de l'arrêt.
Imaginez que le compilateur puisse dire si une fonction a changé la valeur d'une variable ou non. Ensuite, il serait certainement en mesure de dire si la fonction suivante change la valeur de y ou non, en supposant que la valeur de x peut être suivie dans tous les appels dans le reste du programme:
Maintenant, pour tout programme que nous aimons, réécrivons-le comme suit:
Notez que, si, et seulement si, notre programme change la valeur de y, il se termine alors - foo () est la dernière chose qu'il fait avant de quitter. Cela signifie que nous avons résolu le problème en suspens!
Ce que la réduction ci-dessus nous montre, c'est que le problème de déterminer si la valeur d'une variable change est au moins aussi difficile que le problème d'arrêt. Le problème de l'arrêt est connu pour être incalculable, donc celui-ci doit l'être également.
la source
y
. Il me semble que lesfoo()
retours sont rapides, puis lesmain()
sorties. (De plus, vous appelezfoo()
sans argument ... cela fait partie de ma confusion.)Dès qu'une fonction appelle une autre fonction dont le compilateur ne "voit" pas la source, il doit soit supposer que la variable a été modifiée, soit les choses peuvent mal tourner plus loin. Par exemple, disons que nous avons ceci dans "foo.cpp":
et nous avons ceci dans "bar.cpp":
Comment le compilateur peut-il "savoir" que ce qui
x
ne change pas (ou qui est en train de changer, de manière plus appropriée) dansbar
?Je suis sûr que nous pouvons trouver quelque chose de plus complexe, si ce n'est pas assez complexe.
la source
const_cast
in foo, cela apporterait toujours desx
modifications - je serais en violation du contrat qui stipule que vous ne devez pas modifier lesconst
variables, mais puisque vous pouvez convertir n'importe quoi en "more const" et qu'ilconst_cast
existe, les concepteurs du langage avaient sûrement en tête l'idée qu'il y a parfois de bonnes raisons de croire que lesconst
valeurs peuvent avoir besoin de changer.Il est en général impossible au compilateur de déterminer si la variable sera modifiée, comme cela a été souligné.
Lors de la vérification de la const-ness, la question qui nous intéresse semble être de savoir si la variable peut être modifiée par une fonction. Même cela est difficile dans les langues qui prennent en charge les pointeurs. Vous ne pouvez pas contrôler ce que fait un autre code avec un pointeur, il pourrait même être lu à partir d'une source externe (bien que peu probable). Dans les langages qui restreignent l'accès à la mémoire, ces types de garanties peuvent être possibles et permettent une optimisation plus agressive que C ++.
la source
Pour rendre la question plus précise, je suggère que l'ensemble de contraintes suivant peut avoir été ce que l'auteur du livre a pu avoir à l'esprit:
Dans le contexte de la conception d'un compilateur, je pense que les hypothèses 1, 3, 4 ont un sens parfait du point de vue d'un rédacteur de compilateur dans le contexte de l'exactitude de la génération de code et / ou de l'optimisation du code. L'hypothèse 2 a du sens en l'absence du mot-clé volatile. Et ces hypothèses focalisent également suffisamment la question pour rendre le jugement d'une réponse proposée beaucoup plus définitif :-)
Compte tenu de ces hypothèses, l'une des principales raisons pour lesquelles la constance ne peut pas être supposée est due à l'aliasing des variables. Le compilateur ne peut pas savoir si une autre variable pointe vers la variable const. L'aliasing pourrait être dû à une autre fonction dans la même unité de compilation, auquel cas le compilateur pourrait regarder à travers les fonctions et utiliser un arbre d'appels pour déterminer statiquement qu'un aliasing pourrait se produire. Mais si l'alias est dû à une bibliothèque ou à un autre code étranger, alors le compilateur n'a aucun moyen de savoir à l'entrée de la fonction si les variables sont aliasées.
Vous pourriez soutenir que si une variable / argument est marqué const, il ne devrait pas être sujet à changement via l'aliasing, mais pour un rédacteur de compilateur, c'est assez risqué. Il peut même être risqué pour un programmeur humain de déclarer une variable const dans le cadre, par exemple, d'un grand projet où il ne connaît pas le comportement de l'ensemble du système, ou du système d'exploitation, ou d'une bibliothèque, pour vraiment connaître une variable gagnée '' t changer.
la source
Même si une variable est déclarée
const
, cela ne signifie pas qu'un code mal écrit peut l'écraser.production:
la source
a
etb
sont des variables de pile, etb[1]
se trouve être le même emplacement de mémoire quea
.const
si tout est étiquetéconst
. C'est parce que le comportement non défini fait partie de C / C ++. J'essayais de trouver une manière différente de répondre à sa question plutôt que de mentionner le problème de l'arrêt ou la contribution humaine externe.Pour développer mes commentaires, le texte de ce livre n'est pas clair, ce qui obscurcit le problème.
Comme je l'ai commenté, ce livre essaie de dire: "obtenons un nombre infini de singes pour écrire toutes les fonctions C ++ imaginables qui pourraient être écrites. Il y aura des cas où si nous choisissons une variable qui (une fonction particulière que les singes ont écrite) utilise, nous ne pouvons pas déterminer si la fonction changera cette variable. "
Bien sûr, pour certaines (voire plusieurs) fonctions dans une application donnée, cela peut être déterminé par le compilateur, et très facilement. Mais pas pour tous (ou forcément pour la plupart).
Cette fonction peut être facilement analysée:
"foo" ne modifie clairement pas "global". Cela ne modifie rien du tout, et un compilateur peut résoudre ce problème très facilement.
Cette fonction ne peut pas être analysée ainsi:
Puisque les actions de "foo" dépendent d'une valeur qui peut changer à l'exécution , il est manifestement impossible de déterminer au moment de la compilation si elle modifiera "global".
Tout ce concept est beaucoup plus simple à comprendre que les informaticiens ne le prétendent. Si la fonction peut faire quelque chose de différent en fonction des choses qui peuvent changer au moment de l'exécution, vous ne pouvez pas déterminer ce qu'elle fera jusqu'à ce qu'elle s'exécute, et chaque fois qu'elle s'exécute, elle peut faire quelque chose de différent. Que ce soit prouvé impossible ou non, c'est évidemment impossible.
la source