Eu li esta linha em um livro:
É provavelmente impossível construir um compilador que possa realmente determinar se uma função C ++ mudará ou não o valor de uma determinada variável.
O parágrafo estava falando sobre por que o compilador é conservador ao verificar a constância.
Por que é impossível construir um compilador assim?
O compilador sempre pode verificar se uma variável é reatribuída, uma função não const está sendo invocada nela ou se está sendo passada como um parâmetro não const ...
c++
compiler-construction
Jogador de críquete
fonte
fonte
Respostas:
Pela mesma razão que você não pode escrever um programa que irá determinar se um determinado programa será encerrado. Isso é conhecido como o problema da parada e é uma daquelas coisas que não são computáveis.
Para ser claro, você pode escrever um compilador que pode determinar se uma função muda a variável em alguns casos , mas você não pode escrever um que diga com segurança que a função irá ou não alterar a variável (ou interromper) para todas as funções possíveis.
Aqui está um exemplo fácil:
Como um compilador pode determinar, apenas olhando para aquele código, se
foo
alguma vez mudaráa
? Se faz ou não depende de condições externas à função, nomeadamente a implementação debar
. Há mais do que isso para provar que o problema da parada não é computável, mas já está bem explicado no artigo da Wikipedia (e em todos os livros de teoria da computação), então não tentarei explicá-lo corretamente aqui.fonte
Imagine que tal compilador existe. Vamos supor também que, por conveniência, ele fornece uma função de biblioteca que retorna 1 se a função passada modificar uma determinada variável e 0 quando a função não modifica. Então o que este programa deve imprimir?
fonte
f
modifica a variável - não se poderia modificar a variável. Esta resposta está correta.modifies_variable
fonte do compilador, anulando totalmente o seu argumento. (assumindo código aberto, mas o ponto deve ser claro)Não confunda "irá ou não modificar uma variável dada essas entradas" para "tem um caminho de execução que modifica uma variável."
O primeiro é chamado de determinação de predicado opaco e é trivialmente impossível de decidir - além da redução do problema de parada, você pode apenas apontar que as entradas podem vir de uma fonte desconhecida (por exemplo, o usuário). Isso é verdade para todas as linguagens, não apenas C ++.
A última instrução, entretanto, pode ser determinada olhando a árvore de análise, que é algo que todos os compiladores de otimização fazem. A razão disso é que funções puras (e funções referencialmente transparentes , para alguma definição de referencialmente transparente ) têm todos os tipos de ótimas otimizações que podem ser aplicadas, como serem facilmente embutidas ou ter seus valores determinados em tempo de compilação; mas para saber se uma função é pura, precisamos saber se ela pode modificar uma variável.
Portanto, o que parece ser uma declaração surpreendente sobre C ++ é, na verdade, uma declaração trivial sobre todas as linguagens.
fonte
Acho que a palavra-chave em "se uma função C ++ irá ou não alterar o valor de uma variável específica" é "irá". Certamente é possível construir um compilador que verifica se uma função C ++ tem permissão para alterar o valor de uma variável específica, você não pode dizer com certeza se a mudança vai acontecer:
fonte
const
verificações de -ness.Não acho que seja necessário invocar o problema da parada para explicar que você não pode saber algoritmicamente em tempo de compilação se uma determinada função modificará uma determinada variável ou não.
Em vez disso, é suficiente apontar que o comportamento de uma função geralmente depende de condições de tempo de execução, as quais o compilador não pode saber com antecedência. Por exemplo
Como o compilador poderia prever com certeza se
y
será modificado?fonte
Isso pode ser feito e os compiladores estão fazendo isso o tempo todo para algumas funções ; esta é, por exemplo, uma otimização trivial para acessadores embutidos simples ou muitas funções puras.
O que é impossível é saber no caso geral.
Sempre que houver uma chamada de sistema ou uma chamada de função vinda de outro módulo, ou uma chamada para um método potencialmente sobrescrito, qualquer coisa pode acontecer, incluindo o controle hostil do uso de algum hacker de estouro de pilha para alterar uma variável não relacionada.
No entanto, você deve usar const, evitar globais, preferir referências a ponteiros, evitar reutilizar variáveis para tarefas não relacionadas, etc., o que tornará a vida do compilador mais fácil ao realizar otimizações agressivas.
fonte
Existem várias maneiras de explicar isso, uma das quais é o problema da parada :
Se eu escrever um programa parecido com este:
O valor da
x
mudança? Para determinar isso, primeiro você teria que determinar se ado tons of complex stuff
peça causa o disparo da condição - ou ainda mais básico, se ela para. Isso é algo que o compilador não pode fazer.fonte
Realmente surpreso que não haja uma resposta para usar o problema da parada diretamente! Há uma redução muito direta desse problema para o problema da parada.
Imagine que o compilador pudesse dizer se uma função alterou ou não o valor de uma variável. Então, certamente seria capaz de dizer se a função a seguir altera o valor de y ou não, assumindo que o valor de x pode ser rastreado em todas as chamadas ao longo do restante do programa:
Agora, para qualquer programa de que gostamos, vamos reescrevê-lo como:
Observe que, se, e somente se, nosso programa alterar o valor de y, ele então termina - foo () é a última coisa que ele faz antes de sair. Isso significa que resolvemos o problema da parada!
O que a redução acima nos mostra é que o problema de determinar se o valor de uma variável muda é pelo menos tão difícil quanto o problema da parada. O problema da parada é conhecido por ser incomputável, então este também deve ser.
fonte
y
. Parece-me quefoo()
volta rapidamente e depoismain()
sai. (Além disso, você está ligandofoo()
sem uma discussão ... isso é parte da minha confusão.)Assim que uma função chama outra função da qual o compilador não "vê" a origem, ele deve assumir que a variável foi alterada ou as coisas podem dar errado mais adiante. Por exemplo, digamos que temos isso em "foo.cpp":
e temos isso em "bar.cpp":
Como o compilador pode "saber" que
x
não está mudando (ou ESTÁ mudando, mais apropriadamente)bar
?Tenho certeza de que podemos chegar a algo mais complexo, se isso não for complexo o suficiente.
fonte
const_cast
em foo, ainda faria ax
mudança - eu estaria violando o contrato que diz que você não deve alterar asconst
variáveis, mas uma vez que você pode converter qualquer coisa para "mais const", econst_cast
existe, os designers da linguagem certamente tinham a ideia de que às vezes há boas razões para acreditar que osconst
valores podem precisar de mudança.Em geral, é impossível para o compilador determinar se a variável será alterada, como já foi apontado.
Ao verificar a constância, a questão de interesse parece ser se a variável pode ser alterada por uma função. Mesmo isso é difícil em linguagens que suportam ponteiros. Você não pode controlar o que outro código faz com um ponteiro, ele pode até ser lido de uma fonte externa (embora improvável). Em linguagens que restringem o acesso à memória, esses tipos de garantias podem ser possíveis e permitem uma otimização mais agressiva do que o C ++.
fonte
Para tornar a pergunta mais específica, sugiro que o seguinte conjunto de restrições pode ter sido o que o autor do livro pode ter tido em mente:
No contexto do projeto do compilador, acho que as suposições 1,3,4 fazem sentido na visão de um redator do compilador no contexto de correção de geração de código e / ou otimização de código. A suposição 2 faz sentido na ausência da palavra-chave volátil. E essas suposições também focam a questão o suficiente para fazer o julgamento de uma resposta proposta muito mais definitiva :-)
Dadas essas suposições, uma razão principal pela qual a constância não pode ser assumida é devido ao aliasing de variável. O compilador não pode saber se outra variável aponta para a variável const. O aliasing pode ser devido a outra função na mesma unidade de compilação, caso em que o compilador pode examinar as funções e usar uma árvore de chamada para determinar estaticamente que o aliasing pode ocorrer. Mas se o aliasing é devido a uma biblioteca ou outro código estrangeiro, o compilador não tem como saber na entrada da função se as variáveis têm alias.
Você pode argumentar que, se uma variável / argumento for marcada como const, ela não deve estar sujeita a alterações por meio de apelidos, mas para um redator de compilador isso é muito arriscado. Pode até ser arriscado para um programador humano declarar uma variável const como parte de, digamos, um grande projeto onde ele não conhece o comportamento de todo o sistema, ou do sistema operacional, ou de uma biblioteca, para realmente conhecer uma variável. t mudar.
fonte
Mesmo se uma variável for declarada
const
, não significa que algum código mal escrito possa sobrescrevê-la.resultado:
fonte
a
eb
são variáveis de pilha e, porb[1]
acaso, estão no mesmo local de memória quea
.const
se tudo está rotuladoconst
. É porque o comportamento indefinido faz parte do C / C ++. Eu estava tentando encontrar uma maneira diferente de responder à sua pergunta, em vez de mencionar o problema da parada ou intervenção humana externa.Para expandir meus comentários, o texto desse livro não é claro, o que ofusca a questão.
Como comentei, esse livro está tentando dizer: "vamos obter um número infinito de macacos para escrever todas as funções C ++ concebíveis que possam ser escritas. Haverá casos em que se escolhermos uma variável que (alguma função particular que os macacos escreveram) usa, não podemos descobrir se a função irá alterar essa variável. "
É claro que, para algumas (até mesmo muitas) funções em qualquer aplicativo, isso pode ser determinado pelo compilador e muito facilmente. Mas não para todos (ou necessariamente para a maioria).
Esta função pode ser facilmente analisada:
"foo" claramente não modifica "global". Ele não modifica absolutamente nada, e um compilador pode resolver isso facilmente.
Esta função não pode ser analisada desta forma:
Uma vez que as ações de "foo" dependem de um valor que pode mudar em tempo de execução , ele patentemente não pode ser determinado em tempo de compilação se irá modificar "global".
Todo esse conceito é muito mais simples de entender do que os cientistas da computação fazem parecer. Se a função pode fazer algo diferente com base em coisas que podem mudar no tempo de execução, então você não pode descobrir o que ela fará até que seja executada, e cada vez que for executada, pode fazer algo diferente. Independentemente de ser impossível ou não, é obviamente impossível.
fonte