const pointer c++




Qual é a diferença entre const int*, const int*const e int const*? (10)

  1. Referência constante:

    Uma referência a uma variável (aqui int), que é constante. Nós passamos a variável como uma referência principalmente, porque as referências são menores em tamanho do que o valor real, mas há um efeito colateral e isso é porque é como um alias para a variável real. Podemos alterar acidentalmente a variável principal através do nosso acesso completo ao alias, por isso, tornamos constante para evitar esse efeito colateral.

    int var0 = 0;
    const int &ptr1 = var0;
    ptr1 = 8; // Error
    var0 = 6; // OK
  2. Ponteiros constantes

    Depois que um ponteiro constante aponta para uma variável, ele não pode apontar para nenhuma outra variável.

    int var1 = 1;
    int var2 = 0;
    
    int *const ptr2 = &var1;
    ptr2 = &var2; // Error
  3. Ponteiro para constante

    Um ponteiro através do qual não é possível alterar o valor de uma variável que ele aponta é conhecido como um ponteiro para constante.

    int const * ptr3 = &var2;
    *ptr3 = 4; // Error
  4. 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 pode alterar o valor mantido nesse endereço.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error

Eu sempre bagunço como usar const int* , const int * const e int const * corretamente. Existe um conjunto de regras que define o que você pode e não pode fazer?

Eu quero saber todos os fazer e todos não fazer em termos de atribuições, passando para as funções, etc.


É simples, mas complicado. Por favor, note que podemos trocar o qualificador const 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 inteiro constante]

int const *p ==> *p é somente leitura [ p é um ponteiro para um inteiro constante]

int *p const ==> Declaração errada . Compilador lança um erro de sintaxe.

int *const p ==> p é somente leitura [ p é um ponteiro constante para um inteiro]. Como ponteiro p aqui é somente leitura, a declaração e a definição devem estar no mesmo lugar.

const int *p const ==> Declaração errada . Compilador lança um erro de sintaxe.

const int const *p ==> *p é somente leitura

const int *const p1 ==> *p e p são somente leitura [ p é um ponteiro constante para um inteiro constante]. Como ponteiro p aqui é somente leitura, a declaração e a definição devem estar no mesmo lugar.

int const *p const ==> Declaração errada . Compilador lança um erro de sintaxe.

int const int *p ==> Declaração errada . Compilador lança um erro de sintaxe.

int const const *p ==> *p é somente leitura e é equivalente a int const *p

int const *const p ==> *p e p são somente leitura [ p é um ponteiro constante para um inteiro constante]. Como ponteiro p aqui é somente leitura, a declaração e a definição devem estar no mesmo lugar.


A regra geral é que a palavra-chave const se aplica ao que a precede imediatamente. Exceção, uma const inicial aplica-se ao que segue.

  • const int* é o mesmo que int const* e significa "ponteiro para constante int" .
  • const int* const é o mesmo que int 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 você quer?


A sintaxe de declaração C e C ++ foi repetidamente descrita como um experimento falho, pelos projetistas originais.

Em vez disso, vamos nomear o tipo “pointer to Type ”; Eu vou chamá-lo Ptr_ :

template< class Type >
using Ptr_ = Type*;

Agora Ptr_<char> é um ponteiro para char .

Ptr_<const char> é um ponteiro para const char .

E const Ptr_<const char> é um ponteiro const char para const char .

Lá.


Esta questão mostra precisamente porque eu gosto de fazer as coisas do jeito que eu mencionei na minha pergunta é const depois do tipo id aceitável?

Em suma, acho que a maneira mais fácil de lembrar a regra é que o "const" vai atrás da coisa a que se aplica. Então, na sua pergunta, "int const *" significa que o int é constante, enquanto "int * const" significaria que o ponteiro é constante.

Se alguém decide colocá-lo na frente (por exemplo: "const int *"), como uma exceção especial, nesse caso, aplica-se a coisa depois dela.

Muitas pessoas gostam de usar essa exceção especial porque acham que parece mais legal. Eu não gosto disso, porque é uma exceção e, portanto, confunde as coisas.


Eu acho que tudo já está respondido aqui, mas eu só quero acrescentar que você deve ficar atento aos typedef ! Eles não são apenas substituições de texto.

Por exemplo:

typedef char *ASTRING;
const ASTRING astring;

O tipo de astring é char * const , não const char * . Essa é uma das razões pelas quais sempre costumo colocar const à direita do tipo e nunca no início.


Existem muitos outros pontos sutis em torno da correção de const em C ++. Suponho que a questão aqui tenha sido simplesmente sobre C, mas darei alguns exemplos relacionados, já que a tag é C ++:

  • Você costuma passar argumentos grandes como strings como 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 const.

  • Você deve sempre rotular os métodos de classe que não modificam a classe como const , caso contrário, você não poderá chamar o método a partir de uma referência TYPE const & . TYPE const & . 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 { ... }

    Na verdade, os métodos const não devem retornar dados de classe internos como uma referência para não-const.

  • Como resultado, muitas vezes é necessário criar um método const e um não-constante usando sobrecarga de const. Por exemplo, se você definir T const& operator[] (unsigned i) const; , então você provavelmente também vai querer a versão não const dada por:

    inline T& operator[] (unsigned i) { return const_cast<char&>( static_cast<const TYPE&>(*this)[](i) ); }

Afaik, não existem funções const em C, funções non-member 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. Na verdade, até mesmo um simples int const & referência podem testemunhar o valor ao qual ele se refere ser alterado em outro lugar.


Isso aborda principalmente a segunda linha: melhores práticas, atribuições, parâmetros de função, etc.

Prática geral. Tente fazer tudo o que puder. Ou, para colocar de outra forma, faça tudo const para começar, e então remova exatamente o conjunto mínimo de const necessárias para permitir que o programa funcione. Isso será uma grande ajuda para obter constness-correctness, e ajudará a garantir que bugs sutis não sejam introduzidos quando as pessoas tentarem atribuir coisas que não devem ser modificadas.

Evite const_cast <> como a peste. Há um ou dois casos de uso legítimo, mas são muito poucos e distantes entre si. Se você está tentando mudar um objeto const , você fará muito melhor para encontrar quem o declarou no primeiro passo e falar com ele sobre o assunto para chegar a um consenso sobre o que deveria acontecer.

O que leva muito nitidamente a atribuições. Você só pode atribuir algo se não for const. Se você quiser atribuir algo que seja const, veja acima. Lembre-se que nas declarações int const *foo; e int * const bar; coisas diferentes são const - outras respostas aqui abordaram essa questão de maneira admirável, então não vou entrar nisso.

Parâmetros de função:

Passar por valor: por exemplo, void func(int param) você não se importa de uma forma ou de outra no site de chamada. Pode-se argumentar que há casos de uso para declarar a função como void func(int const param) mas que não tem efeito sobre o chamador, apenas na própria função, pois qualquer valor que seja passado não pode ser alterado pela função durante a chamada.

Passar por referência: por exemplo void func(int &param) Agora faz diferença. Como apenas declarado func é permitido alterar param , e qualquer site de chamada deve estar pronto para lidar com as conseqüências. Mudar a declaração para void func(int const &param) muda o contrato e garante que func não pode mudar o param , significando que o que é passado é o que vai voltar. Como outros notaram, isso é muito útil para passar de forma barata um objeto grande que você não deseja alterar. Passar uma referência é muito mais barato do que passar um objeto grande por valor.

Passar por ponteiro: por exemplo, void func(int *param) e void func(int const *param) Esses dois são praticamente sinônimos de suas contrapartes de referência, com a ressalva de que a função chamada agora precisa verificar nullptr menos que outra garantia contratual garante func que nunca receberá um nullptr no param .

Peça de opinião sobre esse tópico. Provar a correção em um caso como este é infernalmente difícil, é muito fácil cometer um erro. Portanto, não se arrisque e sempre verifique os parâmetros do ponteiro para nullptr . Você vai se poupar dor e sofrimento e difícil encontrar erros a longo prazo. E quanto ao custo do cheque, é muito barato, e nos casos em que a análise estática incorporada no compilador pode gerenciá-lo, o otimizador irá eliminá-lo de qualquer maneira. Ative a Geração de Código de Tempo do Link para o MSVC, ou o WOPR (eu acho) para o GCC, e você obterá o programa inteiro, ou seja, mesmo em chamadas de função que cruzam um limite de módulo de código-fonte.

No final do dia, todos os itens acima fazem um caso muito sólido para sempre preferir referências a ponteiros. Eles são apenas mais seguros ao redor.


Para aqueles que não sabem sobre a Regra Horária / Espiral: Comece pelo nome da variável, mova o relógio (nesse caso, retroceda) para o próximo ponteiro ou tipo . Repita até a expressão terminar.

Aqui está uma demonstração:


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 adicione 'const' antes dela. É preciso inicializá-lo imediatamente no construtor porque, naturalmente, não é possível definir o valor mais tarde, pois isso seria alterá-lo. Por exemplo,

const int Constant1=96; 

irá criar uma constante inteira, unimaginatively chamada 'Constant1', com o valor 96.

Tais constantes são úteis para parâmetros que são usados ​​no programa, mas não precisam ser alterados depois que o programa é compilado. Ele tem uma vantagem para programadores sobre o comando '#define' do pré-processador C, pois é entendido e usado pelo próprio compilador, não apenas substituído no texto do programa pelo pré-processador antes de chegar ao compilador principal, então as mensagens de erro são muito mais úteis .

Ele também trabalha com ponteiros, mas é preciso ter cuidado onde 'const' para determinar se o ponteiro ou o que ele aponta é constante ou ambos. Por exemplo,

const int * Constant2 

declara que Constant2 é um ponteiro variável para um inteiro constante e

int const * Constant2

é uma sintaxe alternativa que faz o mesmo, enquanto

int * const Constant3

declara que Constant3 é ponteiro constante para um inteiro variável e

int const * const Constant4

declara que Constant4 é um ponteiro constante para um inteiro constante. Basicamente, 'const' aplica-se a qualquer coisa que esteja imediatamente à sua esquerda (exceto se não houver nada lá, caso em que se aplica a qualquer que seja seu direito imediato).

ref: http://duramecho.com/ComputerInformation/WhyHowCppConst.html





const