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

1356

Eu sempre asneira como usar const int*, const int * conste 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.

MD XF
fonte
175
Você pode usar a "Regra no sentido horário / espiral" para decifrar a maioria das declarações de C e C ++.
James McNellis
52
O cdecl.org é um ótimo site que traduz automaticamente as declarações C para você.
Dave Gallagher
6
@Calmarius: comece onde o nome do tipo é / deveria estar, mova para a direita quando puder e para a esquerda quando for necessário . 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?
Mark K Cowan
1
@ MarkKCowan Em Pascal, seria algo parecido 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.
Calmarius
5
A primeira coisa à esquerda da "const" é o que é constante. Se "const" é a coisa mais distante à esquerda, a primeira coisa à direita é a constante.
Cupcake

Respostas:

2208

Leia de trás para frente (conforme orientado pela Regra no sentido horário / espiral ):

  • int* - ponteiro para int
  • int const * - ponteiro para const int
  • int * const - ponteiro const para int
  • int const * const - ponteiro const para const int

Agora o primeiro constpode 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 int
  • int ** const - um ponteiro const para um ponteiro para um int
  • int * const * - um ponteiro para um ponteiro const para um int
  • int const ** - um ponteiro para um ponteiro para uma const int
  • int * const * const - um ponteiro const para um ponteiro const para um int
  • ...

E para ter certeza de que estamos claros sobre o significado de const:

int a = 5, b = 10, c = 15;

const int* foo;     // pointer to constant int.
foo = &a;           // assignment to where foo points to.

/* dummy statement*/
*foo = 6;           // the value of a can´t get changed through the pointer.

foo = &b;           // the pointer foo can be changed.



int *const bar = &c;  // constant pointer to int 
                      // note, you actually need to set the pointer 
                      // here because you can't change it later ;)

*bar = 16;            // the value of c can be changed through the pointer.    

/* dummy statement*/
bar = &a;             // not possible because bar is a constant pointer.           

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 a const 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 um T* constponteiro, a menos que precise permitir NULLponteiros.

Matt Price
fonte
482
Gostaria de acrescentar uma regra prática que pode ajudá-lo a lembrar como descobrir se 'const' se aplica ao ponteiro ou aos dados apontados: divida a instrução no sinal asterix; se a palavra-chave const aparecer na parte esquerda (como em 'const int * foo') - ele pertence aos dados apontados, se estiver na parte correta ('int * const bar') - é sobre o ponteiro.
Michael
14
@ Michael: Parabéns a Michael por uma regra tão simples para lembrar / entender a regra const.
sivabudh
10
@ Jeffrey: lê-lo para trás funciona bem, desde que não haja parênteses. Então, bem ... use typedefs
Mooing Duck
12
+1, embora um resumo melhor seja: leia as declarações dos ponteiros ao contrário , ou seja, próximo à declaração de @ Michael: interrompa a leitura normal da esquerda para a direita no primeiro asterisco.
Wolf
3
@gedamial funciona, funciona bem, mas você deve atribuí-lo ao mesmo tempo em que o declara (porque não é possível reatribuir um "ponteiro const"). const int x = 0; const int *const px = &x; const int *const *const p = &px;funciona muito bem.
RastaJedi 8/08/16
357

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:

ponteiro para int

ponteiro const para int const

ponteiro para int const

ponteiro para const int

ponteiro const para int

Shijing Lv
fonte
8
@ Jan o link para o exemplo complexo não tem permissões. você pode publicá-lo diretamente aqui ou remover as restrições de visualização?
R71
8
@ Roger costumava ter todas as permissões de acesso aberto ... Eu não escrevi o artigo e não tenho permissões de acesso, infelizmente. No entanto, aqui é uma versão arquivada do artigo que ainda funciona: archive.is/SsfMX
Jan Rüegg
8
O exemplo complexo ainda é da direita para a esquerda, mas inclui a resolução de parênteses da maneira que normalmente seria. A coisa toda em espiral no sentido horário não facilita nada.
Matthew Leia
4
Exemplo final: void (*signal(int, void (*fp)(int)))(int);de archive.is/SsfMX
naXa 3/17/17
3
Não confie nesta regra. Isto não é universal. Existem alguns casos em que falha.
haccks
150

Acho que tudo já foi respondido aqui, mas quero acrescentar que você deve tomar cuidado com o typedefs! 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 tento colocar constà direita do tipo e nunca no começo.

Kaz Dragon
fonte
20
E para mim, este é o motivo para nunca digitar indicadores. Eu não vejo o benefício em coisas como typedef int* PINT(presumo que seja algo que veio das práticas em C e muitos desenvolvedores continuaram fazendo isso). Ótimo, substituí-o *por um P, não acelera a digitação, além de apresentar o problema mencionado.
Mephane
1
@Ephane - eu posso ver isso. No entanto, para mim, parece um pouco para trás evitar um bom recurso de linguagem para continuar usando uma regra sintática excepcional (sobre posicionamento "const"), em vez de evitar o uso da regra sintática excepcional para que você possa usar com segurança esse recurso de linguagem .
TED
6
@Mephane 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.
ApproachingDarknessFish
5
@KazDragon THANKS! Sem ele, eu teria mexido com todas essas coisas de digitação PVOID, LPTSTRna API do Win32!
David Lee
2
@Mephane: Eu tive que usar pSomething algumas vezes ao usar determinadas macros herdadas que foram escritas para aceitar um tipo, mas que se separariam se o tipo não fosse um único identificador alfanumérico. :)
Groo
56

Como praticamente todo mundo apontou:

Qual é a diferença entre const X* p, X* const pe const X* const p?

Você precisa ler as declarações do ponteiro da direita para a esquerda.

  • const X* p significa "p aponta para um X que é const": o objeto X não pode ser alterado via p.

  • X* const p significa "p é um ponteiro const para um X que não é const": você não pode alterar o ponteiro p em si, mas pode alterar o objeto X via p.

  • const X* const p significa "p é um ponteiro const para um X que é const": você não pode alterar o ponteiro p, nem o objeto X via p.

Lucas
fonte
3
Não se esqueça que 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.
Jesse Chisholm
explicação simples e agradável!
Edison Lo
50
  1. 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.

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

    Uma vez que um ponteiro constante aponte para uma variável, ele não poderá 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 se pode alterar o valor de uma variável que aponta é conhecido como 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 o valor mantido nesse endereço.

    int var3 = 0;
    int var4 = 0;
    const int * const ptr4 = &var3;
    *ptr4 = 1;     // Error
     ptr4 = &var4; // Error
Behrooz Tabesh
fonte
20

A regra geral é que a constpalavra - chave se aplica ao que a precede imediatamente. Exceção, uma partida constse aplica ao que se segue.

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

AProgrammer
fonte
19

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.

TED
fonte
2
Estou dividido sobre esta questão. Logicamente, faz sentido. No entanto, a maioria dos desenvolvedores de c ++ escreveria const T*e tornou-se mais natural. Quantas vezes você usa um de T* constqualquer maneira, geralmente uma referência funciona bem. Fiquei impressionado com tudo isso uma vez ao querer um boost::shared_ptr<const T>e, em vez disso, escrevi const boost::shared_ptr<T>. O mesmo problema em um contexto um pouco diferente.
6118 Matt Price
Na verdade, uso ponteiros constantes com mais frequência do que constantes. Além disso, você deve pensar em como vai reagir na presença de ponteiros para ponteiros (etc.) É certo que esses são mais raros, mas seria bom pensar nas coisas de uma maneira que você possa lidar com essas situações com o applomb.
TED
1
A outra boa vantagem de colocar a const à direita do tipo é que agora tudo à esquerda de qualquer consté o tipo daquilo que é const, e tudo à sua direita é aquilo que é realmente const. Tome int const * const * p;como exemplo. Não, eu normalmente não escrevo assim, este é apenas um exemplo. Primeiro const: digite int, E o int que é const é o conteúdo do ponteiro const que é o conteúdo de p. Segunda const: type é ponteiro para constint, const oblect é o conteúdo dep
dgnuff 15/18
18

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 constantes dela. É preciso inicializá-lo imediatamente no construtor, porque, é claro, não é possível definir o valor posteriormente, pois isso o alteraria. Por exemplo:

const int Constant1=96; 

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 #definecomando 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 constpara determinar se o ponteiro ou o que ele aponta é constante ou ambos. Por exemplo:

const int * Constant2 

declara que Constant2é ponteiro variável para um número inteiro constante e:

int const * Constant2

é uma sintaxe alternativa que faz o mesmo, enquanto

int * const Constant3

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

int const * const Constant4

declara 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

ufukgun
fonte
9

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

  1. Se a palavra constaparecer à esquerda do asterisco, o que é apontado é constante
  2. Se a palavra constaparecer à direita do asterisco, o ponteiro em si é constante
  3. Se constaparecer nos dois lados, ambos são constantes
rgk
fonte
7

É simples, mas complicado. Por favor, note que podemos trocar o constqualificador 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 ponteiro paqui é 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 leitura

const int *const p1 ==> *pe psão somente leitura [ pé um ponteiro constante para um número inteiro constante]. Como o ponteiro paqui é 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 ==> *pe psão somente leitura [ pé um ponteiro constante para um número inteiro constante]. Como o ponteiro paqui é somente leitura, a declaração e a definição devem estar no mesmo local.

Abhijit Sahu
fonte
6

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 & constnã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 uma TYPE 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.

Jeff Burdges
fonte
6

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á-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 constponteiro para const char.

Lá.

insira a descrição da imagem aqui

Felicidades e hth. - Alf
fonte
3
você tem uma cotação para a primeira frase?
Sp2danny 14/09/16
@ sp2danny: Pesquisando no Google, “a sintaxe C falhou no experimento” apenas abre uma série de entrevistas com Bjarne Stroustrup, onde ele expressa sua opinião nessa direção, por exemplo, “eu considero a sintaxe do declarador C um experimento que falhou” na entrevista do Slashdot. Portanto, não tenho referência à alegação sobre os pontos de vista dos designers originais de C. Acho que isso pode ser encontrado por um esforço de pesquisa suficientemente forte, ou talvez refutado simplesmente perguntando a eles, mas acho que é melhor do jeito que está agora. com essa parte da reivindicação, ainda indecisa e provavelmente verdadeira :)
Cheers e hth. - Alf
1
"A sintaxe das declarações C e C ++ tem sido repetidamente descrita como um experimento com falha, pelos designers originais." errado para C, altere sua frase sobre C ou forneça algumas aspas.
Stargateur
3
@ Stargateur: Aparentemente, você leu os comentários anteriores e encontrou algo que poderia aproveitar para o pedantismo. Boa sorte com sua vida. De qualquer forma, os veteranos como eu lembram-se de muita coisa que não podemos provar sem nos envolvermos em pesquisas muito demoradas. Você poderia apenas aceitar minha palavra.
Saúde e hth. #
1
@Stargateur "Sethi (...) observou que muitas das declarações e expressões aninhadas se tornariam mais simples se o operador de indireção fosse tomado como operador de postfix em vez de prefixo, mas já era tarde para mudar". é da DMR. É claro que o DMR não inventou palavras-chave const e voláteis; elas vieram do C ++ / X3J11, conforme evidenciado nessa página.
Antti Haapala
6

Para mim, a posição de, constou seja, se aparece à ESQUERDA ou à DIREITA ou à esquerda e à direita em relação à *ajuda me ajuda a descobrir o significado real.

  1. A constpara a ESQUERDA *indica que o objeto apontado pelo ponteiro é um constobjeto.

  2. A constà DIREITA de *indica que o ponteiro é um constponteiro.

A tabela a seguir foi retirada do Leitor de Cursos de Laboratório Stanford CS106L Standard C ++ Programming.

insira a descrição da imagem aqui

sri
fonte
3

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

Prática geral. Tente fazer tudo o constque puder. Ou, de outra forma, faça tudo constpara começar e remova exatamente o conjunto mínimo de consts 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 constobjeto, fará muito melhor para encontrar quem o declarou constno 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 nas int * const bar;coisas diferentes existem const- 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 como void 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 &param)agora faz a diferença. Conforme declarado, funcé permitido mudar param, e qualquer site de chamadas deve estar pronto para lidar com as consequências. Mudar a declaração para void func(int const &param)mudar o contrato e garantir que funcagora não pode mudar param, 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)e void 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á nullptrmenos algumas outras assegura garantia contratual funcque nunca vai receber um nullptrem param.

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.

dgnuff
fonte
3

A const com o int em ambos os lados fará ponteiro para constante int :

const int *ptr=&i;

ou:

int const *ptr=&i;

constafter *fará um ponteiro constante para int :

int *const ptr=&i;

Nesse caso, todos eles são ponteiros para números inteiros constantes , mas nenhum deles é ponteiro constante:

 const int *ptr1=&i, *ptr2=&j;

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:

int const *ptr1=&i, *const ptr2=&j;
Caçador
fonte
3
  • se constestiver à esquerda de *, refere-se ao valor (não importa se é const intou int const)
  • se constestiver à direita de *, refere-se ao ponteiro em si
  • pode ser ambos ao mesmo tempo

Um 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 exemplo

int x = 5;
const int *p = &x;
x = 6; //legal
printf("%d", *p) // prints 6
*p = 7; //error 

Isso deve ser usado principalmente em assinaturas de funções, para garantir que a função não possa alterar acidentalmente os argumentos passados.

nota azul
fonte
2

Por uma questão de integridade para C, seguindo as outras explicações, não tenho certeza para C ++.

  • pp - ponteiro para ponteiro
  • p - ponteiro
  • dados - a coisa apontada, em exemplos x
  • negrito - variável somente leitura

Ponteiro

  • p dados - int *p;
  • p dados -int const *p;
  • p dados -int * const p;
  • p dados -int const * const p;

Ponteiro para ponteiro

  1. dados pp p - int **pp;
  2. dados pp p -int ** const pp;
  3. dados pp p -int * const *pp;
  4. dados pp p -int const **pp;
  5. dados pp p -int * const * const pp;
  6. dados pp p -int const ** const pp;
  7. dados pp p -int const * const *pp;
  8. dados pp p -int const * const * const pp;
// Example 1
int x;
x = 10;
int *p = NULL;
p = &x;
int **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 2
int x;
x = 10;
int *p = NULL;
p = &x;
int ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 3
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 4
int const x = 10; // Definition must happen during declaration
int const * p = NULL;
p = &x;
int const **pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 5
int x;
x = 10;
int * const p = &x; // Definition must happen during declaration
int * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 6
int const x = 10; // Definition must happen during declaration
int const *p = NULL;
p = &x;
int const ** const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

// Example 7
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const *pp = NULL;
pp = &p;
printf("%d\n", **pp);

// Example 8
int const x = 10; // Definition must happen during declaration
int const * const p = &x; // Definition must happen during declaration
int const * const * const pp = &p; // Definition must happen during declaration
printf("%d\n", **pp);

N-níveis de desreferência

Apenas continue, mas que a humanidade excomunde você.

int x = 10;
int *p = &x;
int **pp = &p;
int ***ppp = &pp;
int ****pppp = &ppp;

printf("%d \n", ****pppp);
Comportamento indefinido
fonte
0
  1. const int*- ponteiro para intobjeto constante .

Você pode alterar o valor do ponteiro; você não pode alterar o valor do intobjeto, o ponteiro aponta para.


  1. const int * const- ponteiro constante para intobjeto constante .

Você não pode alterar o valor do ponteiro nem o valor do intobjeto para o qual o ponteiro aponta.


  1. int const *- ponteiro para intobjeto constante .

Esta declaração é equivalente a 1. const int*- Você pode alterar o valor do ponteiro, mas não pode alterar o valor do intobjeto, para o qual o ponteiro aponta.


Na verdade, existe uma quarta opção:

  1. int * const- ponteiro constante para o intobjeto.

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 intobjeto, mas esse valor intpode 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.

RobertS suporta Monica Cellio
fonte