A const correção dá ao compilador mais espaço para otimização?

Eu sei que isso melhora a legibilidade e torna o programa menos propenso a erros, mas quanto ele melhora o desempenho?

E em uma nota lateral, qual é a principal diferença entre uma referência e um ponteiro const ? Eu diria que eles estão armazenados na memory de forma diferente, mas como assim?

[Edit: OK, então esta pergunta é mais sutil do que eu pensava no começo.]

Declarar um ponteiro para const ou referência de const nunca ajuda qualquer compilador a otimizar qualquer coisa. (Embora veja a atualização na parte inferior desta resposta.)

A declaração const indica apenas como um identificador será usado dentro do escopo de sua declaração; Não diz que o object subjacente não pode mudar.

Exemplo:

 int foo(const int *p) { int x = *p; bar(x); x = *p; return x; } 

O compilador não pode assumir que *p não é modificado pela chamada para bar() , porque p poderia ser (por exemplo) um ponteiro para um int global e uma bar() poderia modificá-lo.

Se o compilador sabe o suficiente sobre o chamador de foo() e o conteúdo de bar() que pode provar que bar() não modifica *p , então ele também pode executar essa prova sem a declaração const .

Mas isso é verdade em geral. Porque const só tem um efeito dentro do escopo da declaração, o compilador já pode ver como você está tratando o ponteiro ou referência dentro desse escopo; já sabe que você não está modificando o object subjacente.

Então, em suma, tudo o que const faz nesse contexto é evitar que você cometa erros. Ele não diz ao compilador nada que já não saiba e, portanto, é irrelevante para a otimização.

E quanto às funções que chamam foo() ? Gostar:

 int x = 37; foo(&x); printf("%d\n", x); 

O compilador pode provar que isso imprime 37, já que foo() toma um const int * ?

Não. Mesmo que foo() leve um ponteiro para const, ele pode lançar a constância longe e modificar o int. (Esse não é um comportamento indefinido.) Aqui, novamente, o compilador não pode fazer nenhuma suposição em geral; e se souber o suficiente sobre foo() para fazer tal otimização, saberá que mesmo sem o const .

A única vez que o const pode permitir otimizações é um caso como este:

 const int x = 37; foo(&x); printf("%d\n", x); 

Aqui, modificar x através de qualquer mecanismo (por exemplo, tomando um ponteiro para ele e descartando o const ) é invocar o comportamento indefinido. Portanto, o compilador está livre para assumir que você não faz isso e pode propagar a constante 37 para o printf (). Esse tipo de otimização é legal para qualquer object declarado const . (Na prática, uma variável local para a qual você nunca terá uma referência não será beneficiada, porque o compilador já pode ver se você a modifica dentro de seu escopo).

Para responder à sua pergunta de “nota lateral”, (a) um ponteiro const é um ponteiro; e (b) um ponteiro const pode ser igual a NULL. Você está certo de que a representação interna (isto é, um endereço) é provavelmente a mesma.

[atualizar]

Como Christoph aponta nos comentários, minha resposta está incompleta porque não menciona restrict .

A Seção 6.7.3.1 (4) da norma C99 diz:

Durante cada execução de B, seja L qualquer valor que tenha & L baseado em P. Se L é usado para acessar o valor do object X que ele designa, e X também é modificado (por qualquer meio), então os seguintes requisitos se aplicam : T não deve ser conscanvasdo. …

(Aqui B é um bloco básico sobre o qual P, um restrito-ponteiro para T, está no escopo.)

Então, se uma function C foo() é declarada assim:

 foo(const int * restrict p) 

… então o compilador pode assumir que nenhuma modificação em *p ocorre durante o tempo de vida de p – isto é, durante a execução de foo() – porque senão o Behavior seria indefinido.

Portanto, em princípio, combinar restrict com um ponteiro para const poderia habilitar ambas as otimizações que são descartadas acima. Algum compilador realmente implementa essa otimização? (GCC 4.5.2, pelo menos, não.)

Observe que restrict existe somente em C, não em C ++ (nem mesmo em C ++ 0x), exceto como uma extensão específica do compilador.

No topo da minha cabeça, posso pensar em dois casos em que a const adequada permite otimizações adicionais (nos casos em que a análise de todo o programa não está disponível):

 const int foo = 42; bar(&foo); printf("%i", foo); 

Aqui, o compilador sabe imprimir 42 sem ter que examinar o corpo de bar() (que pode não ser visível na unidade de tradução curent) porque todas as modificações em foo são ilegais ( isto é o mesmo que o exemplo de Nemo ).

No entanto, isso também é possível sem marcar foo como const declarando bar() como

 extern void bar(const int *restrict p); 

Em muitos casos, o programador realmente deseja restrict pointers para const e não os simples pointers para const como parâmetros de function, pois apenas os primeiros fazem qualquer garantia sobre a mutabilidade dos objects apontados.

Quanto à segunda parte da sua pergunta: Para todos os propósitos práticos, uma referência C ++ pode ser considerada como um ponteiro constante (não um ponteiro para um valor constante!) Com indireção automática – não é “seguro” ou “mais rápido” do que um ponteiro, apenas mais conveniente.

Há dois problemas com const em C ++ (no que diz respeito à otimização):

  • const_cast
  • mutable

const_cast significa que mesmo que você passe um object por referência const ou ponteiro const, a function pode lançar a constância longe e modificar o object (permitido se o object não for const para começar).

mutable significa que, mesmo que um object seja const , algumas de suas partes podem ser modificadas (comportamento de armazenamento em cache). Além disso, os objects apontados para (em vez de serem de propriedade) podem ser modificados em methods const , mesmo quando eles logicamente fazem parte do estado do object. E finalmente as variables ​​globais podem ser modificadas também …

const está aqui para ajudar o desenvolvedor a detectar erros lógicos antecipadamente.

const-correctness geralmente não ajuda no desempenho; a maioria dos compiladores nem se preocupa em rastrear constness além do frontend. Marcar variables ​​como const pode ajudar, dependendo da situação.

Referências e pointers são armazenados exatamente da mesma maneira na memory.

Isso realmente depende do compilador / plataforma (pode ajudar na otimização de algum compilador que ainda não tenha sido escrito ou em alguma plataforma que você nunca usa). Os padrões C e C ++ não dizem nada sobre desempenho além de dar requisitos de complexidade para algumas funções.

Ponteiros e referências a const geralmente não ajudam na otimização, já que a qualificação const pode legalmente ser descartada em algumas situações, e é possível que o object possa ser modificado por uma referência diferente de não-const. Por outro lado, declarar um object como const pode ser útil, pois garante que o object não pode ser modificado (mesmo quando passado para funções que o compilador não conhece a definição). Isso permite que o compilador armazene o object const na memory somente leitura ou armazene em cache seu valor em um local centralizado, reduzindo a necessidade de cópias e verificando se há modificações.

Ponteiros e referências são geralmente implementados da mesma maneira, mas novamente isso é totalmente dependente da plataforma. Se você está realmente interessado, então você deve olhar o código de máquina gerado para sua plataforma e compilador em seu programa (se de fato você estiver usando um compilador que gere código de máquina).

Uma coisa é, se você declarar uma variável global const, pode ser possível colocá-la na parte somente leitura de uma biblioteca ou executável e, portanto, compartilhá-la entre vários processos com um mmap somente leitura. Isso pode ser um grande ganho de memory no Linux, pelo menos se você tiver muitos dados declarados em variables ​​globais.

Outra situação, se você declarar um inteiro global constante, ou float ou enum, o compilador poderá colocar a constante inline em vez de usar uma referência de variável. Isso é um pouco mais rápido, embora eu não seja um especialista em compiladores.

As referências são apenas indicadores abaixo, em termos de implementação.

Pode ajudar um pouco o desempenho, mas somente se você estiver acessando o object diretamente por meio de sua declaração. parameters de referência e tal não podem ser otimizados, pois pode haver outros caminhos para um object não declarado originalmente const eo compilador geralmente não pode dizer se o object que você está referenciando foi realmente declarado const ou não a menos que seja a declaração que você está usando.

Se você estiver usando uma declaração const, o compilador saberá que corpos de funções compilados externamente, etc. não podem modificá-lo, portanto, você obtém um benefício lá. E é claro que coisas como const int’s são propagadas em tempo de compilation, então essa é uma grande vitória (comparado a apenas um int).

Referências e pointers são armazenados exatamente da mesma forma, eles apenas se comportam de maneira diferente sintaticamente. As referências são basicamente renomeações e, portanto, são relativamente seguras, enquanto os pointers podem apontar várias coisas diferentes e, portanto, são mais poderosas e propensas a erros.

Eu acho que o ponteiro const seria arquitetonicamente idêntico à referência, então o código da máquina e a eficiência seriam os mesmos. a diferença real é syntax – as referências são uma syntax mais limpa, mais fácil de ler e, como você não precisa do maquinário extra fornecido por um ponteiro, uma referência seria estilisticamente preferida.