Não estou falando de ponteiros para valores const, mas de ponteiros const.
Estou aprendendo C e C ++ além das coisas básicas e até hoje percebi que ponteiros são passados por valor para funções, o que faz sentido. Isso significa que dentro de uma função eu posso fazer o ponteiro copiado apontar para algum outro valor sem afetar o ponteiro original do chamador.
Então, qual é o sentido de ter um cabeçalho de função que diz:
void foo(int* const ptr);
Dentro dessa função, você não pode fazer ptr apontar para outra coisa porque é const e você não deseja que seja modificada, mas uma função como esta:
void foo(int* ptr);
Faz o trabalho tão bem! porque o ponteiro é copiado de qualquer maneira e o ponteiro no chamador não é afetado, mesmo se você modificar a cópia. Então, qual é a vantagem da const?
const
parâmetro.const
garantias de correção. Isso apenas nos faz ter mais certeza de que nosso código está indubitavelmente correto.Respostas:
const
é uma ferramenta que você deve usar na busca de um conceito C ++ muito importante:Mesmo que isso não mude a funcionalidade, adicionar
const
gera um erro de compilador quando você está fazendo coisas que não pretendia. Imagine o seguinte erro de digitação:Se você usar
int* const
, isso geraria um erro do compilador porque você está alterando o valor paraptr
. Adicionar restrições via sintaxe é uma coisa boa em geral. Apenas não leve muito longe - o exemplo que você deu é um caso em que a maioria das pessoas não se incomoda em usarconst
.fonte
Eu faço questão de usar apenas
const
argumentos porque isso permite mais verificações do compilador: se eu acidentalmente redesignar um valor de argumento dentro da função, o compilador me morderá.Eu raramente reutilizo variáveis, é mais fácil criar novas variáveis para armazenar novos valores, portanto, essencialmente, todas as minhas declarações de variáveis são
const
(exceto em alguns casos, como variáveis de loop, ondeconst
o código não funcionaria).Observe que isso só faz sentido na definição de uma função. Não pertence à declaração , que é o que o usuário vê. E o usuário não se importa se eu uso
const
parâmetros dentro da função.Exemplo:
Observe como o argumento e a variável local são
const
. Nem é necessário, mas com funções ainda um pouco maiores, isso me salvou repetidamente de cometer erros.fonte
+1
de outroconst
fanático avaliado. No entanto, prefiro que meus compiladores latam para mim. Eu cometi muitos erros e sofreria muito se eles mordessem.const
estratégia "a menos que haja uma boa razão". Porém, existem algumas boas exceções, por exemplo, Copy and swapconst
o argumento e, de preferência, comentar o motivo. Esse pequeno trabalho extra não justifica marcar todos os argumentos como não-const
padrão e abrir-se a todos os erros potenciais que isso cria.Sua pergunta aborda algo mais geral: os argumentos da função devem ser const?
Os argumentos de constância de valor (como seu ponteiro) são um detalhe de implementação e não fazem parte da declaração da função. Isso significa que sua função é sempre esta:
Cabe inteiramente ao implementador da função se ela deseja usar a variável de argumento de escopo de funções de maneira mutável ou constante:
Portanto, siga a regra simples para nunca colocar
const
a declaração (cabeçalho) e coloque-a na definição (implementação) se você não quiser ou precisar modificar a variável.fonte
const
, a declaração e a (parte do protótipo) da definição geralmente serão idênticas.const
na declaração. Depende inteiramente de você se você deseja adicionar o qualificador na implementação.const
a declaração, mas não a definição, faz sentido. Parece uma falha na linguagem que este é o único caso em que fazer a declaração e a definição não idênticas faz sentido. (É quase única falha de C, é claro.)O qualificador const de nível superior é descartado nas declarações; portanto, as declarações na pergunta declaram exatamente a mesma função. Por outro lado, na definição (implementação), o compilador verificará que, se você marcar o ponteiro como const, ele não será modificado dentro do corpo da função.
fonte
int* t ptr
é um erro de sintaxe. Sem isso, os dois são idênticos para fins de sobrecarga.Você está certo, para o interlocutor não faz absolutamente nenhuma diferença. Mas, para o autor da função, pode ser uma rede de segurança "ok, eu preciso ter certeza de que não entendi a coisa errada". Não é muito útil, mas também não é inútil.
É basicamente o mesmo que ter um
int const the_answer = 42
em seu programa.fonte
const int
eint const
são equivalentes, enquantoconst int*
eint* const
têm dois significados diferentes!int const
part; Estou colocando o tipo antes de const (parece natural) há algum tempo e estou ciente das diferenças. Este artigo pode ser útil. Eu mesmo tinha razões ligeiramente diferentes para mudar para esse estilo.Há muito na
const
palavra-chave, é bastante complexa. Geralmente, adicionar muitas const ao seu programa é considerado uma boa prática de programação, pesquise na web por "const correctness" e você encontrará muitas informações sobre isso.A palavra-chave const é um chamado "qualificador de tipo", outros são
volatile
erestrict
. Pelo menos volátil segue as mesmas regras (confusas) que const.Primeiro, a palavra-chave const serve a dois propósitos. O mais óbvio é proteger dados (e ponteiros) contra uso indevido intencional ou acidental, tornando-os somente leitura. Qualquer tentativa de modificar uma variável const será identificada pelo compilador no momento da compilação.
Mas também existe outro objetivo em qualquer sistema com memória somente leitura, a saber, garantir que uma determinada variável seja alocada dentro dessa memória - poderia ser EEPROM ou flash, por exemplo. Essas são conhecidas como memórias não voláteis, NVM. Uma variável alocada no NVM ainda seguirá todas as regras de uma variável const.
Existem várias maneiras diferentes de usar a
const
palavra-chave:Declare uma variável constante.
Isso pode ser feito como
Essas duas formas são completamente equivalentes . O último estilo é considerado ruim e não deve ser usado.
A razão pela qual a segunda linha é considerada incorreta é provavelmente porque "especificadores da classe de armazenamento", como static e extern, também podem ser declarados após o tipo real,
int static
etc. Mas fazer isso para os especificadores da classe de armazenamento é rotulado como um recurso obsoleto pelo comitê C (projeto ISO 9899 N1539, 6.11.5). Portanto, por uma questão de consistência, também não se deve escrever qualificadores de tipo dessa maneira. Não serve a nenhum outro propósito senão confundir o leitor de qualquer maneira.Declare um ponteiro para uma variável constante.
Isso significa que o conteúdo de 'X' não pode ser modificado. Esta é a maneira normal de declarar ponteiros como este, principalmente como parte dos parâmetros de função para "const correctness". Como 'X' não precisa ser declarado como const, pode ser qualquer variável. Em outras palavras, você sempre pode "atualizar" uma variável para const. Tecnicamente, C também permite fazer o downgrade de const para uma variável simples por previsões explícitas, mas fazer isso é considerado uma programação ruim e os compiladores geralmente dão avisos contra ele.
Declarar um ponteiro constante
Isso significa que o ponteiro em si é constante. Você pode modificar o que aponta, mas não pode modificar o ponteiro em si. Isso não tem muitos usos, existem alguns, como garantir que um ponteiro apontado para (ponteiro para ponteiro) não tenha seu endereço alterado enquanto passado como parâmetro para uma função. Você terá que escrever algo não muito legível como este:
Duvido que muitos programadores em C consigam obter a const e * bem ali. Eu sei que não posso - tive que verificar com o GCC. Eu acho que é por isso que você raramente vê essa sintaxe de ponteiro para ponteiro, mesmo que seja considerada uma boa prática de programação.
Ponteiros constantes também podem ser usados para garantir que a própria variável ponteiro seja declarada na memória somente leitura, por exemplo, você pode declarar algum tipo de tabela de pesquisa baseada em ponteiro e alocá-la no NVM.
E, é claro, como indicado por outras respostas, indicadores constantes também podem ser usados para reforçar a "correção constante".
Declare um ponteiro constante para dados constantes
Estes são os dois tipos de ponteiros descritos acima combinados, com todos os atributos dos dois.
Declarar uma função de membro somente leitura (C ++)
Como isso está marcado como C ++, também devo mencionar que você pode declarar funções de membro de uma classe como const. Isso significa que a função não tem permissão para modificar nenhum outro membro da classe quando é chamada, o que impede o programador da classe de erros acidentais, mas também informa o chamador da função de membro que eles não estarão mexendo em nada chamando. A sintaxe é:
fonte
(imo) realmente não faz sentido como padrão. o padrão mais sensato é passar como ponteiro não transferível (
int* const arg
). isto é, eu teria preferido que ponteiros passados como argumentos fossem declarados implicitamente const.a vantagem é que é fácil o suficiente e às vezes não é claro quando você modifica o endereço para o qual o argumento aponta, para que você possa introduzir um bug quando ele não é const com facilidade. alterar o endereço é atípico. é mais claro criar uma variável local se sua intenção é modificar o endereço. também, a manipulação bruta de ponteiro é uma maneira fácil de introduzir bugs.
portanto, é mais claro passar pelo endereço imutável e criar uma cópia (nesses casos atípicos) quando você deseja alterar o endereço apontado pelo argumento:
acrescentar que o local é praticamente gratuito e reduz a chance de erros, além de melhorar a legibilidade.
em um nível mais alto: se você está manipulando o argumento como uma matriz, normalmente é mais claro e menos propenso ao erro declarar o argumento como um contêiner / coleção.
em geral, adicionar const a valores, argumentos e endereços é uma boa idéia, porque você nem sempre percebe os efeitos colaterais, que o compilador aplica com satisfação. portanto, é tão útil quanto const, como usado em vários outros casos (por exemplo, a pergunta é semelhante a 'Por que devo declarar valores const?'). felizmente, também temos referências que não podem ser reatribuídas.
fonte
const
palavra - chave, ela deve ter umamutable
palavra - chave (bem, tem, mas com a semântica errada).Se você faz sistemas incorporados ou programação de driver de dispositivo em que possui dispositivos mapeados na memória, as duas formas de 'const' são frequentemente usadas, uma para impedir que o ponteiro seja reatribuído (uma vez que aponta para um endereço de hardware fixo) e, se o periférico Se o registro apontado for um registro de hardware somente leitura, outra const detectará muitos erros no tempo de compilação, em vez do tempo de execução.
Um registro de chip periférico de somente leitura de 16 bits pode ser algo como:
static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;
Então você pode ler facilmente o registro de hardware sem precisar recorrer à linguagem assembly:
input_word = *peripheral;
fonte
int iVal = 10; int * const ipPtr = & iVal;
Assim como uma variável const normal, um ponteiro const deve ser inicializado com um valor na declaração e seu valor não pode ser alterado.
Isso significa que um ponteiro const sempre apontará para o mesmo valor. No caso acima, o ipPtr sempre apontará para o endereço do iVal. No entanto, como o valor que está sendo apontado ainda não é const, é possível alterar o valor que está sendo apontado via desreferenciando o ponteiro:
* ipPtr = 6; // permitido, pois pnPtr aponta para um não-const int
fonte
A mesma pergunta pode ser feita sobre qualquer outro tipo (não apenas ponteiros):
fonte
Sua pergunta é realmente mais sobre por que definir qualquer variável como um const e não apenas o parâmetro const ponteiro para uma função. As mesmas regras se aplicam aqui como quando você define qualquer variável como constante, se for um parâmetro para funcionar ou variável de membro ou variável local.
No seu caso particular, funcionalmente, não faz diferença, como em muitos outros casos, quando você declara uma variável local como const, mas coloca uma restrição de que você não pode modificar essa variável.
fonte
Passar um ponteiro const para uma função faz pouco sentido, pois ele será passado pelo valor de qualquer maneira. É apenas uma daquelas coisas permitidas pelo design geral da linguagem. Proibí-lo apenas porque não faz sentido apenas faria a especificação do idioma. maior.
Se você estiver dentro de uma função, é claro que é outro caso. Ter um ponteiro que não pode alterar o que aponta é uma afirmação que torna o código mais claro.
fonte
Eu acho que uma vantagem seria que o compilador pode executar otimizações mais agressivas dentro da função, sabendo que esse ponteiro não pode mudar.
Também evita, por exemplo. passando esse ponteiro para uma subfunção que aceita uma referência de ponteiro não-const (e, portanto, poderia mudar o ponteiro como
void f(int *&p)
), mas eu concordo que a utilidade é um pouco limitada nesse caso.fonte
Um exemplo de onde um ponteiro const é altamente aplicável pode ser demonstrado assim. Considere que você tem uma classe com uma matriz dinâmica dentro dela e deseja passar o acesso do usuário à matriz, mas sem conceder a eles os direitos de alterar o ponteiro. Considerar:
Qual produz:
Mas se tentarmos isso:
Nós temos:
Tão claramente que podemos modificar o conteúdo da matriz, mas não o ponteiro da matriz. Bom se você quiser garantir que o ponteiro tenha um estado consistente ao transmiti-lo ao usuário. Há uma captura, no entanto:
Ainda podemos excluir a referência de memória do ponteiro, mesmo que não possamos modificar o ponteiro em si.
Portanto, se você deseja que a referência de memória sempre aponte para algo (o IE nunca é modificado, semelhante ao modo como uma referência atualmente funciona), é altamente aplicável. Se você deseja que o usuário tenha acesso total e o modifique, o non-const é para você.
Editar:
Depois de observar o comentário okorz001 de não poder atribuir devido ao fato de GetArray () ser um operando de valor certo, o comentário dele está totalmente correto, mas o acima descrito ainda se aplica se você retornar uma referência ao ponteiro (suponho que assumi que GetArray era referente a uma referência), por exemplo:
Retornará no primeiro, resultando em um erro:
Mas o segundo ocorrerá alegremente, apesar das possíveis conseqüências subjacentes.
Obviamente, a questão será levantada 'por que você gostaria de retornar uma referência a um ponteiro'? Existem casos raros em que você precisa atribuir memória (ou dados) diretamente ao ponteiro original em questão (por exemplo, criar seu próprio front-end malloc / free ou novo / free), mas nesses casos é uma referência não constante . Como referência a um ponteiro const, não encontrei uma situação que o justificasse (a menos que talvez como variáveis declaradas de referência const em vez de tipos de retorno?).
Considere se temos uma função que aceita um ponteiro const (versus um que não possui):
O erro na const produz a seguinte mensagem:
O que é bom, pois provavelmente não queremos fazer isso, a menos que desejemos causar os problemas indicados nos comentários. Se editarmos o decremento na função const, ocorrerá o seguinte:
Claramente, embora A seja 'Dados [1]', ele está sendo tratado como 'Dados [0]' porque o ponteiro NonConst permitiu a operação de decremento. Com o const implementado, como outra pessoa escreve, detectamos o bug em potencial antes que ele ocorra.
Uma outra consideração principal é que um ponteiro const pode ser usado como uma pseudo referência, pois a coisa para a qual os pontos de referência não podem ser alterados (é de se perguntar, se talvez fosse assim que foi implementado). Considerar:
Ao tentar compilar, produz o seguinte erro:
O que provavelmente é uma coisa ruim se uma referência constante a A for desejada. Se
B = NULL
for comentado, o compilador nos permitirá modificar*B
e, portanto, A. Isso pode não parecer útil com ints, mas considere se você tinha uma única postura de um aplicativo gráfico em que desejava um ponteiro não modificável que se referisse a ele que você poderia passar por aí.Seu uso é variável (desculpe o trocadilho não intencional), mas usado corretamente, é outra ferramenta na caixa para ajudar na programação.
fonte
Temp.GetArray() = NULL
falha porqueTemp.GetArray()
é um rvalue, não porque é qualificado como const. Além disso, acredito que o qualificador const seja removido de todos os tipos de retorno.Não há nada de especial nos ponteiros onde você nunca gostaria que eles fossem const. Assim como você pode ter
int
valores constantes de membro de classe , também pode ter indicadores constantes por razões semelhantes: Você deseja garantir que ninguém nunca mude o que está sendo apontado. As referências C ++ abordam isso de alguma forma, mas o comportamento do ponteiro é herdado de C.fonte
Acredito que isso impediria que o código aumentasse ou diminuísse o ponteiro no corpo da função.
fonte
Tipos de declarar quaisquer variáveis como:
(1) Declarar uma variável constante.
DataType const varibleName;
const dataType* PointerVaribleName=&X;
dataType* const PointerVaribleName=&X;
fonte