Embora seja verdade que o comportamento é bem definido - é não verdade que compiladores pode "otimizar para const", no sentido que você quer dizer.
Ou seja, não é permitido que um compilador assuma que apenas porque um parâmetro é a const T* ptr
, a memória apontada por ptr
não será alterada por outro ponteiro. Os ponteiros nem precisam ser iguais. A const
é uma obrigação, não uma garantia - uma obrigação por você (= a função) não fazer alterações por meio desse ponteiro.
Para realmente ter essa garantia, você precisa marcar o ponteiro com a restrict
palavra - chave. Portanto, se você compilar essas duas funções:
int foo(const int* x, int* y) {
int result = *x;
(*y)++;
return result + *x;
}
int bar(const int* x, int* restrict y) {
int result = *x;
(*y)++;
return result + *x;
}
a foo()
função deve ler duas vezes x
, enquanto bar()
só precisa ler uma vez:
foo:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, DWORD PTR [rdi] # second read
ret
bar:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, eax # no second read
ret
Veja isso ao vivo GodBolt.
restrict
é apenas uma palavra-chave em C (desde C99); infelizmente, ele não foi introduzido no C ++ até agora (pelo baixo motivo que é mais complicado introduzi-lo no C ++). Muitos compiladores suportam isso, no entanto, como __restrict
.
Conclusão: O compilador deve oferecer suporte ao seu caso de uso "esotérico" ao compilar f()
e não terá nenhum problema com ele.
Consulte esta postagem sobre casos de uso para restrict
.
const
não é "uma obrigação sua (= a função) de não fazer alterações através desse ponteiro". O padrão C permite que a função seja removidaconst
por meio de uma conversão e, em seguida, modifique o objeto através do resultado. Essencialmente,const
é apenas um aviso e uma conveniência para o programador para ajudar a evitar a modificação inadvertida de um objeto.memcpy
estrcpy
é declarado comrestrict
argumentos, enquantomemmove
não é - apenas o último permite sobreposição entre os blocos de memória.Isso está bem definido (em C ++, não há mais certeza em C), com e sem o
const
qualificador.A primeira coisa a procurar é a regra estrita de aliasing 1 . Se
src
edst
aponta para o mesmo objeto:char*
echar const*
não são compatíveis.char*
echar const*
são semelhantes.Em relação ao
const
qualificador, você pode argumentar que, desde quedst == src
sua função efetivamente modifique ossrc
pontos,src
não deve ser qualificado comoconst
. Não é assim queconst
funciona. Dois casos precisam ser considerados:const
comochar const data[42];
modificado (direta ou indiretamente), leva a um comportamento indefinido.const
objeto é definido, como emchar const* pdata = data;
, é possível modificar o objeto subjacente, desde que não tenha sido definido comoconst
2 (consulte 1.). Portanto, o seguinte está bem definido:1) Qual é a regra estrita de aliasing?
2) É
const_cast
seguro?fonte
char*
echar const*
não são compatíveis._Generic((char *) 0, const char *: 1, default: 0))
avalia como zero.const
objeto é definida” está incorreta. Você quer dizer que quando uma referência ou ponteiro para um tipoconst
qualificado é definido, isso não significa que o objeto para o qual ele está apontado não pode ser modificado (por vários meios). (Se o ponteiro aponta para um objeto, isso significa que ele é realmente por definição, portanto o comportamento de tentar modificá-lo não é definido.)const
const
language-lawyer
. Exatidão é um valor que eu aprecio, mas também sei que ele vem com mais complexidade. Aqui, decidi optar por frases simples e fáceis de entender, porque acredito que isso é o que o OP queria. Se você pensa o contrário, responda, estarei entre os primeiros a votar. De qualquer forma, obrigado pelo seu comentário.Isso está bem definido em C. As regras estritas de alias não se aplicam ao
char
tipo, nem a dois ponteiros do mesmo tipo.Não sei ao certo o que você quer dizer com "otimizar para
const
". Meu compilador (GCC 8.3.0 x86-64) gera exatamente o mesmo código para os dois casos. Se você adicionar orestrict
especificador aos ponteiros, o código gerado será um pouco melhor, mas isso não funcionará no seu caso, os ponteiros sendo os mesmos.(C11 §6.5, 7)
Nesse caso (sem
restrict
), você sempre terá121
como resultado.fonte