Eu sempre asneira como usar const int*
, const int * const
e int const *
corretamente. Existe um conjunto de regras que define o que você pode ou não fazer?
Quero saber tudo o que fazer e o que não fazer em termos de tarefas, passagem para as funções, etc.
int *(*)(char const * const)
. Comece a direita do parêntesis*
então nós temos que ir para a esquerda:pointer
. Fora dos parênteses, podemos mover para a direita:pointer to function of ...
. Então nós temos que mover para a esquerda:pointer to function of ... that returns pointer to int
. Repita para expandir o parâmetro (o...
):pointer to function of (constant pointer to constant char) that returns pointer to int
. Qual seria a declaração de uma linha equivalente em uma linguagem de fácil leitura como Pascal?function(x:^char):^int
. Existem tipos de funções que implicam um ponteiro para uma função; portanto, não é necessário especificá-la, e Pascal não aplica a correção constante. Pode ser lido da esquerda para a direita.Respostas:
Leia de trás para frente (conforme orientado pela Regra no sentido horário / espiral ):
int*
- ponteiro para intint const *
- ponteiro para const intint * const
- ponteiro const para intint const * const
- ponteiro const para const intAgora o primeiro
const
pode estar em ambos os lados do tipo, de modo que:const int *
==int const *
const int * const
==int const * const
Se você quer enlouquecer, pode fazer coisas assim:
int **
- ponteiro para ponteiro para intint ** const
- um ponteiro const para um ponteiro para um intint * const *
- um ponteiro para um ponteiro const para um intint const **
- um ponteiro para um ponteiro para uma const intint * const * const
- um ponteiro const para um ponteiro const para um intE para ter certeza de que estamos claros sobre o significado de
const
:foo
é um ponteiro variável para um número inteiro constante. Isso permite alterar o que você aponta, mas não o valor que você aponta. Na maioria das vezes isso é visto com seqüências de caracteres em estilo C, nas quais você tem um ponteiro para aconst char
. Você pode alterar para qual string você aponta, mas não pode alterar o conteúdo dessas strings. Isso é importante quando a própria string está no segmento de dados de um programa e não deve ser alterada.bar
é um ponteiro constante ou fixo para um valor que pode ser alterado. Isso é como uma referência sem o açúcar sintático extra. Por esse motivo, geralmente você usaria uma referência na qual usaria umT* const
ponteiro, a menos que precise permitirNULL
ponteiros.fonte
const int x = 0; const int *const px = &x; const int *const *const p = &px;
funciona muito bem.Para quem não conhece a Regra no sentido horário / espiral: comece pelo nome da variável, mova no sentido horário (nesse caso, vá para trás) para o próximo ponteiro ou tipo . Repita até a expressão terminar.
Aqui está uma demonstração:
fonte
void (*signal(int, void (*fp)(int)))(int);
de archive.is/SsfMXAcho que tudo já foi respondido aqui, mas quero acrescentar que você deve tomar cuidado com o
typedef
s! Eles não são apenas substituições de texto.Por exemplo:
O tipo de
astring
échar * const
, nãoconst char *
. Essa é uma das razões pelas quais sempre tento colocarconst
à direita do tipo e nunca no começo.fonte
typedef int* PINT
(presumo que seja algo que veio das práticas em C e muitos desenvolvedores continuaram fazendo isso). Ótimo, substituí-o*
por umP
, não acelera a digitação, além de apresentar o problema mencionado.PINT
é realmente um uso bastante idiota de um typedef, especialmente porque me faz pensar que as lojas do sistema usam cerveja para memória. typedef s são bastante úteis para lidar com ponteiros para funções, no entanto.PVOID
,LPTSTR
na API do Win32!Como praticamente todo mundo apontou:
Qual é a diferença entre
const X* p
,X* const p
econst X* const p
?fonte
const X* p;
==X const * p;
como em"p points to an X that is const": the X object can't be changed via p.
Referência constante:
Uma referência a uma variável (aqui int), que é constante. Passamos a variável como referência principalmente, porque as referências são menores em tamanho que o valor real, mas há um efeito colateral e isso ocorre porque é como um alias para a variável real. Podemos alterar acidentalmente a variável principal por meio de nosso acesso total ao alias, portanto, fazemos com que seja constante para evitar esse efeito colateral.
Ponteiros constantes
Uma vez que um ponteiro constante aponte para uma variável, ele não poderá apontar para nenhuma outra variável.
Ponteiro para constante
Um ponteiro através do qual não se pode alterar o valor de uma variável que aponta é conhecido como ponteiro para constante.
Ponteiro constante para uma constante
Um ponteiro constante para uma constante é um ponteiro que não pode alterar o endereço para o qual está apontando e nem o valor mantido nesse endereço.
fonte
A regra geral é que a
const
palavra - chave se aplica ao que a precede imediatamente. Exceção, uma partidaconst
se aplica ao que se segue.const int*
é o mesmo queint const*
e significa "ponteiro para constante int" .const int* const
é o mesmo queint const* const
e significa "ponteiro constante para constante int" .Edit: Para os prós e contras, se esta resposta não for suficiente, você poderia ser mais preciso sobre o que deseja?
fonte
Esta pergunta mostra precisamente por que eu gosto de fazer as coisas da maneira que mencionei na minha pergunta : const após o tipo de ID é aceitável?
Em suma, acho que a maneira mais fácil de lembrar a regra é que o "const" vai depois a coisa se aplica a. Portanto, em sua pergunta, "int const *" significa que o int é constante, enquanto "int * const" significaria que o ponteiro é constante.
Se alguém decide colocá-lo bem na frente (por exemplo: "const int *"), como uma exceção especial nesse caso, isso se aplica à coisa que está depois dela.
Muitas pessoas gostam de usar essa exceção especial porque acham que é melhor. Eu não gosto disso, porque é uma exceção e, portanto, confunde as coisas.
fonte
const T*
e tornou-se mais natural. Quantas vezes você usa um deT* const
qualquer maneira, geralmente uma referência funciona bem. Fiquei impressionado com tudo isso uma vez ao querer umboost::shared_ptr<const T>
e, em vez disso, escreviconst boost::shared_ptr<T>
. O mesmo problema em um contexto um pouco diferente.const
é o tipo daquilo que é const, e tudo à sua direita é aquilo que é realmente const. Tomeint const * const * p;
como exemplo. Não, eu normalmente não escrevo assim, este é apenas um exemplo. Primeiroconst
: digite int, E o int que é const é o conteúdo do ponteiro const que é o conteúdo dep
. Segunda const: type é ponteiro paraconst
int, const oblect é o conteúdo dep
Uso Simples de
const
.O uso mais simples é declarar uma constante nomeada. Para fazer isso, declara-se uma constante como se fosse uma variável, mas adiciona-a
const
antes dela. É preciso inicializá-lo imediatamente no construtor, porque, é claro, não é possível definir o valor posteriormente, pois isso o alteraria. Por exemplo:criará uma constante inteira, chamada sem imaginação
Constant1
, com o valor 96.Tais constantes são úteis para parâmetros usados no programa, mas não precisam ser alterados após a compilação do programa. Ele tem uma vantagem para os programadores em relação ao
#define
comando do pré-processador C, pois ele é entendido e usado pelo próprio compilador, não apenas substituído pelo texto do programa pelo pré-processador antes de chegar ao compilador principal, portanto as mensagens de erro são muito mais úteis.Também funciona com ponteiros, mas é preciso ter cuidado
const
para determinar se o ponteiro ou o que ele aponta é constante ou ambos. Por exemplo:declara que
Constant2
é ponteiro variável para um número inteiro constante e:é uma sintaxe alternativa que faz o mesmo, enquanto
declara que
Constant3
é um ponteiro constante para um número inteiro variável edeclara que
Constant4
é um ponteiro constante para um número inteiro constante. Basicamente, 'const' se aplica ao que estiver à sua esquerda imediata (exceto se não houver nada lá; nesse caso, se aplica ao que estiver à sua direita imediata).ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html
fonte
Eu tinha a mesma dúvida que você até encontrar este livro do guru do C ++ Scott Meyers. Consulte o terceiro item deste livro, onde ele fala em detalhes sobre o uso
const
.Basta seguir este conselho
const
aparecer à esquerda do asterisco, o que é apontado é constanteconst
aparecer à direita do asterisco, o ponteiro em si é constanteconst
aparecer nos dois lados, ambos são constantesfonte
É simples, mas complicado. Por favor, note que podemos trocar o
const
qualificador com qualquer tipo de dados (int
,char
,float
, etc.).Vamos ver os exemplos abaixo.
const int *p
==>*p
é somente leitura [p
é um ponteiro para um número inteiro constante]int const *p
==>*p
é somente leitura [p
é um ponteiro para um número inteiro constante]int *p const
==> Declaração incorreta . O compilador gera um erro de sintaxe.int *const p
==>p
é somente leitura [p
é um ponteiro constante para um número inteiro]. Como o ponteirop
aqui é somente leitura, a declaração e a definição devem estar no mesmo local.const int *p const
==> Declaração incorreta . O compilador gera um erro de sintaxe.const int const *p
==>*p
é somente leituraconst int *const p1
==>*p
ep
são somente leitura [p
é um ponteiro constante para um número inteiro constante]. Como o ponteirop
aqui é somente leitura, a declaração e a definição devem estar no mesmo local.int const *p const
==> Declaração incorreta . O compilador gera um erro de sintaxe.int const int *p
==> Declaração incorreta . O compilador gera um erro de sintaxe.int const const *p
==>*p
é somente leitura e é equivalente aint const *p
int const *const p
==>*p
ep
são somente leitura [p
é um ponteiro constante para um número inteiro constante]. Como o ponteirop
aqui é somente leitura, a declaração e a definição devem estar no mesmo local.fonte
Existem muitos outros pontos sutis que cercam a correção const no C ++. Suponho que a pergunta aqui tenha sido simplesmente sobre C, mas darei alguns exemplos relacionados, pois a tag é C ++:
Você costuma passar argumentos grandes, como cadeias, o
TYPE const &
que impede que o objeto seja modificado ou copiado. Exemplo:TYPE& TYPE::operator=(const TYPE &rhs) { ... return *this; }
Mas
TYPE & const
não tem sentido porque as referências são sempre constantes.Você sempre deve rotular métodos de classe que não modificam a classe como
const
, caso contrário, não poderá chamar o método a partir de umaTYPE const &
referência. Exemplo:bool TYPE::operator==(const TYPE &rhs) const { ... }
Existem situações comuns em que o valor de retorno e o método devem ser const. Exemplo:
const TYPE TYPE::operator+(const TYPE &rhs) const { ... }
De fato, os métodos const não devem retornar dados de classe interna como uma referência a não-const.
Como resultado, é necessário criar frequentemente um método const e um não-const usando sobrecarga const. Por exemplo, se você definir
T const& operator[] (unsigned i) const;
, provavelmente também desejará a versão não const fornecida por:inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }
Além disso, não existem funções const em C, funções que não são membros não podem ser const em C ++, métodos const podem ter efeitos colaterais e o compilador não pode usar funções const para evitar chamadas de função duplicadas. De fato, mesmo uma
int const &
referência simples pode testemunhar o valor a que se refere ser alterado em outro lugar.fonte
A sintaxe da declaração C e C ++ foi repetidamente descrita como um experimento com falha, pelos designers originais.
Em vez disso, vamos nomear o tipo "ponteiro para
Type
"; Vou chamá-loPtr_
:Agora
Ptr_<char>
é um ponteiro parachar
.Ptr_<const char>
é um ponteiro paraconst char
.E
const Ptr_<const char>
é umconst
ponteiro paraconst char
.Lá.
fonte
Para mim, a posição de,
const
ou seja, se aparece à ESQUERDA ou à DIREITA ou à esquerda e à direita em relação à*
ajuda me ajuda a descobrir o significado real.A
const
para a ESQUERDA*
indica que o objeto apontado pelo ponteiro é umconst
objeto.A
const
à DIREITA de*
indica que o ponteiro é umconst
ponteiro.A tabela a seguir foi retirada do Leitor de Cursos de Laboratório Stanford CS106L Standard C ++ Programming.
fonte
Isso aborda principalmente a segunda linha: melhores práticas, atribuições, parâmetros de função etc.
Prática geral. Tente fazer tudo o
const
que puder. Ou, de outra forma, faça tudoconst
para começar e remova exatamente o conjunto mínimo deconst
s necessário para permitir que o programa funcione. Isso será uma grande ajuda para alcançar a correção constante e ajudará a garantir que erros sutis não sejam introduzidos quando as pessoas tentam atribuir coisas que não deveriam modificar.Evite const_cast <> como uma praga. Existem um ou dois casos de uso legítimos para isso, mas eles são muito poucos e distantes entre si. Se você estiver tentando mudar um
const
objeto, fará muito melhor para encontrar quem o declarouconst
no primeiro ritmo e conversar sobre o assunto com eles para chegar a um consenso sobre o que deve acontecer.O que leva muito bem às tarefas. Você pode atribuir a algo apenas se não for const. Se você deseja atribuir algo que seja const, veja acima. Lembre-se de que nas declarações
int const *foo;
e nasint * const bar;
coisas diferentes existemconst
- outras respostas aqui abordaram esse assunto de maneira admirável, por isso não vou entrar nisso.Parâmetros da função:
Passe por valor: por exemplo,
void func(int param)
você não se importa de uma maneira ou de outra no local da chamada. Pode-se argumentar que há casos de uso para declarar a função comovoid func(int const param)
mas que não têm efeito no chamador, apenas na própria função, na medida em que qualquer valor passado não pode ser alterado pela função durante a chamada.Passe por referência: por exemplo,
void func(int ¶m)
agora faz a diferença. Conforme declarado,func
é permitido mudarparam
, e qualquer site de chamadas deve estar pronto para lidar com as consequências. Mudar a declaração paravoid func(int const ¶m)
mudar o contrato e garantir quefunc
agora não pode mudarparam
, significando que o que é passado é o que será devolvido. Como outros observaram, isso é muito útil para a passagem barata de um objeto grande que você não deseja alterar. Passar uma referência é muito mais barato do que passar um objeto grande por valor.Passe pelo ponteiro: por exemplo,
void func(int *param)
evoid func(int const *param)
Estes dois são praticamente sinônimo com os seus homólogos de referência, com a ressalva de que a função chamada agora precisa verificar se hánullptr
menos algumas outras assegura garantia contratualfunc
que nunca vai receber umnullptr
emparam
.Opinião sobre esse tópico. Provar a correção em um caso como esse é terrivelmente difícil, é muito fácil cometer um erro. Portanto, não se arrisque e sempre verifique os parâmetros do ponteiro
nullptr
. Você economizará dor e sofrimento e será difícil encontrar insetos a longo prazo. E quanto ao custo da verificação, é muito barato e, nos casos em que a análise estática incorporada no compilador pode gerenciá-la, o otimizador o elimina de qualquer maneira. Ative a geração de código de tempo de link para MSVC ou WOPR (eu acho) para o GCC, e você obterá todo o programa, ou seja, mesmo em chamadas de função que cruzam o limite de um módulo de código-fonte.No final do dia, tudo o que foi dito acima é um argumento muito sólido para sempre preferir referências a ponteiros. Eles são apenas mais seguros o tempo todo.
fonte
A const com o int em ambos os lados fará ponteiro para constante int :
ou:
const
after*
fará um ponteiro constante para int :Nesse caso, todos eles são ponteiros para números inteiros constantes , mas nenhum deles é ponteiro constante:
Nesse caso, todos são ponteiros para um número inteiro constante e ptr2 é um ponteiro constante para um número inteiro constante . Mas ptr1 não é um ponteiro constante:
fonte
const
estiver à esquerda de*
, refere-se ao valor (não importa se éconst int
ouint const
)const
estiver à direita de*
, refere-se ao ponteiro em siUm ponto importante:
const int *p
não significa que o valor a que você está se referindo seja constante !! . Isso significa que você não pode alterá-lo através desse ponteiro (ou seja, não é possível atribuir $ * p = ... `). O valor em si pode ser alterado de outras maneiras. Por exemploIsso deve ser usado principalmente em assinaturas de funções, para garantir que a função não possa alterar acidentalmente os argumentos passados.
fonte
Por uma questão de integridade para C, seguindo as outras explicações, não tenho certeza para C ++.
x
Ponteiro
int *p;
int const *p;
int * const p;
int const * const p;
Ponteiro para ponteiro
int **pp;
int ** const pp;
int * const *pp;
int const **pp;
int * const * const pp;
int const ** const pp;
int const * const *pp;
int const * const * const pp;
N-níveis de desreferência
Apenas continue, mas que a humanidade excomunde você.
fonte
const int*
- ponteiro paraint
objeto constante .Você pode alterar o valor do ponteiro; você não pode alterar o valor do
int
objeto, o ponteiro aponta para.const int * const
- ponteiro constante paraint
objeto constante .Você não pode alterar o valor do ponteiro nem o valor do
int
objeto para o qual o ponteiro aponta.int const *
- ponteiro paraint
objeto constante .Esta declaração é equivalente a 1.
const int*
- Você pode alterar o valor do ponteiro, mas não pode alterar o valor doint
objeto, para o qual o ponteiro aponta.Na verdade, existe uma quarta opção:
int * const
- ponteiro constante para oint
objeto.Você pode alterar o valor do objeto para o qual o ponteiro aponta, mas não pode alterar o valor do próprio ponteiro. O ponteiro sempre apontará para o mesmo
int
objeto, mas esse valorint
pode ser alterado.Se você deseja determinar um certo tipo de construção C ou C ++, pode usar a Regra no sentido horário / espiral feita por David Anderson; mas não confundir com a regra de Anderson feita por Ross J. Anderson, que é algo bastante distinto.
fonte