Qual é o sentido dos ponteiros const?

149

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?

R. Ruiz.
fonte
29
E se você quiser garantir, em tempo de compilação, que o ponteiro não pode e não deve ser modificado para apontar para outra coisa?
Platinum Azure
25
Exatamente o mesmo ponto que qualquer constparâmetro.
David Heffernan 10/10
2
@PlatinumAzure, tenho alguns anos de experiência em programação, mas minhas práticas de software estão faltando. Estou feliz por ter feito essa pergunta porque nunca senti a necessidade de fazer o compilador apontar meus próprios erros de lógica. Minha reação inicial no momento em que enviei a pergunta foi "por que devo me preocupar se o ponteiro é modificado ?, não afetará o chamador". Até ler todas as respostas, entendi que deveria me importar, porque, se tentar modificá-lo, eu e minha lógica de codificação estamos erradas (ou houve um erro de digitação, como falta do asterisco) e talvez eu não tenha percebido se o compilador não ' não me diga.
R. Ruiz.
3
@ R.Ruiz. Sem dúvida, mesmo os mais experientes de nós poderiam fazer com garantias extras de correção. Como a engenharia de software é uma forma de engenharia em que um pouco mais de margem de manobra pode ser aceitável (HTTP 502s aleatórios, conexões lentas, a imagem ocasional que falha ao carregar não é uma ocasião extraordinária, mas uma falha no mecanismo de um avião é inaceitável e provavelmente grave), às vezes pessoas programam com pressa indevida. O mesmo argumento que justifica a gravação de testes de unidade explicará o uso de constgarantias de correção. Isso apenas nos faz ter mais certeza de que nosso código está indubitavelmente correto.
Platinum Azure
3
Uma pergunta relacionada: stackoverflow.com/questions/219914/…
Raedwald 11/11

Respostas:

207

const é uma ferramenta que você deve usar na busca de um conceito C ++ muito importante:

Encontre erros no tempo de compilação, e não no tempo de execução, fazendo com que o compilador aplique o que você quer dizer.

Mesmo que isso não mude a funcionalidade, adicionar constgera um erro de compilador quando você está fazendo coisas que não pretendia. Imagine o seguinte erro de digitação:

void foo(int* ptr)
{
    ptr = 0;// oops, I meant *ptr = 0
}

Se você usar int* const, isso geraria um erro do compilador porque você está alterando o valor para ptr. 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 usar const.

dez quatro
fonte
8
Obrigado, esta é uma resposta que me convence. Você coloca const para que o compilador avise sobre seus próprios erros de atribuição. Seu exemplo é perfeito para ilustrar esse conceito, porque esse é um erro comum com ponteiros. Do que você!
R. Ruiz.
25
"Ajude o compilador a ajudá-lo" é o mantra que costumo cantar sobre isso.
Flexo
1
+1, mas aqui é um caso interessante onde é argueable: stackoverflow.com/questions/6305906/...
Raedwald
3
portanto, é a mesma resposta que você daria a "Por que tornar variáveis ​​privadas, quando você simplesmente não pode usá-las fora da classe".
perfil completo
isso pode ser um pouco fora de tópico, mas onde esse ponteiro const fica na memória? Eu sei que todas as const estão na parte global da memória, mas não tenho 100% de certeza sobre uma const que você recebe como uma var. obrigado e desculpe se isso é off topic
Tomer
77

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, onde consto 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 constparâmetros dentro da função.

Exemplo:

// foo.h
int frob(int x);
// foo.cpp
int frob(int const x) {
   MyConfigType const config = get_the_config();
   return x * config.scaling;
}

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.

Konrad Rudolph
fonte
17
+1de outro constfanático avaliado. No entanto, prefiro que meus compiladores latam para mim. Eu cometi muitos erros e sofreria muito se eles mordessem.
SBI
2
+1, recomendo a constestratégia "a menos que haja uma boa razão". Porém, existem algumas boas exceções, por exemplo, Copy and swap
Flexo
2
Se eu estivesse projetando um novo idioma, os objetos declarados seriam somente leitura ("const") por padrão. Você precisaria de alguma sintaxe especial, talvez uma palavra-chave "var", para tornar um objeto gravável.
Keith Thompson
@ Keith Estou tentado a dizer "é claro". Todo o resto é estúpido neste momento. Mas, infelizmente, a maioria dos designers de linguagem parece discordar de nós ... De fato, eu já postei isso (em uma pergunta agora excluída, “Qual é a sua opinião de programação mais controversa?”).
Konrad Rudolph
1
@KubaOber Não vejo como é uma pessimização. Se você descobrir mais tarde que precisa modificar o que foi passado no corpo da função, basta largar consto argumento e, de preferência, comentar o motivo. Esse pequeno trabalho extra não justifica marcar todos os argumentos como não- constpadrão e abrir-se a todos os erros potenciais que isso cria.
underscore_d
20

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:

void foo(T);

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:

// implementation 1
void foo(T const x)
{
  // I won't touch x
  T y = x;
  // ...
}

// implementation 2
void foo(T x)
{
  // l33t coding skillz
  while (*x-- = zap()) { /* ... */ }
}

Portanto, siga a regra simples para nunca colocar consta declaração (cabeçalho) e coloque-a na definição (implementação) se você não quiser ou precisar modificar a variável.

Kerrek SB
fonte
5
Eu concordo com isso - mas estou um pouco desconfortável com a idéia de tornar a declaração e a definição diferentes. Para outras coisas const, a declaração e a (parte do protótipo) da definição geralmente serão idênticas.
Keith Thompson
@KeithThompson: Bem, você não precisa fazer isso se não quiser. Apenas não coloque constna declaração. Depende inteiramente de você se você deseja adicionar o qualificador na implementação.
Kerrek SB
1
Como disse, concordo que fazer consta 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.)
Keith Thompson
16

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.

David Rodríguez - dribeas
fonte
Eu não sabia disso. Portanto, se eu tentar sobrecarregar void foo (int * const ptr) com void foo (int * t ptr), receberei um erro do compilador. Obrigado!
R. Ruiz.
@ R.Ruiz. Se sua desaceleração for void foo (int * t ptr) e sua definição for void foo (int * const ptr), você NÃO receberá um erro do compilador.
Trevor Hickey
1
@TrevorHickey Eles vão ... mas não pelo motivo que pensam. int* t ptré um erro de sintaxe. Sem isso, os dois são idênticos para fins de sobrecarga.
Underscore_d
14

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 = 42em seu programa.

cnicutar
fonte
1
O que importa para onde eles apontam, é um ponteiro alocado na pilha? A função não pode atrapalhar isso. Faz muito mais sentido usar ponteiros somente leitura ao lidar com ponteiros para ponteiros. É não a mesma coisa que const int! Eu vou votar isso apenas por essa declaração enganosa. const inte int constsão equivalentes, enquanto const int*e int* consttêm dois significados diferentes!
Lundin 10/10
1
@lundin Vamos supor que a função seja grande e tenha outras coisas. Acidentalmente, alguém pode apontar para outra coisa (na pilha da função). Isso não é problemático para o chamador, mas certamente pode ser para o chamado. Não vejo nada de enganador nas minhas declarações: " para o escritor da função , pode ser uma rede de segurança".
cnicutar
1
@Lundin About int constpart; 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.
cnicutar
1
OK. Acabei de escrever uma resposta longa, caso alguém mais você e eu esteja lendo este post. Também incluí uma justificativa de por que int const é um estilo ruim e const int é um bom estilo.
Lundin 10/10
Lundin, eu também estou lendo! Concordo que const int é o melhor estilo. @cnicutar, sua primeira resposta a Lundin é o que eu estava procurando quando enviei esta pergunta. Portanto, o problema não está no chamador (como eu pensava sem pensar), mas const é uma garantia para o chamado. Eu gosto dessa ideia, obrigado.
R. Ruiz.
14

Há muito na constpalavra-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 volatilee restrict. 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 constpalavra-chave:

Declare uma variável constante.

Isso pode ser feito como

const int X=1; or
int const X=1;

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 staticetc. 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.

const int* ptr = &X;

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

int* const ptr = &X;

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:

void func (int*const* ptrptr)

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

const int* const ptr=&X;

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 é:

void MyClass::func (void) const;
Lundin
fonte
8

... hoje percebi que ponteiros são passados ​​por valor para funções, o que faz sentido.

(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.

Então, qual é a vantagem da 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:

void func(int* const arg) {
    int* a(arg);
    ...
    *a++ = value;
}

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.

justin
fonte
4
+1 por este ser o padrão. C ++, como a maioria das linguagens, tem o caminho errado. Em vez de ter uma constpalavra - chave, ela deve ter uma mutablepalavra - chave (bem, tem, mas com a semântica errada).
Konrad Rudolph
2
Idéia interessante tendo const como padrão. Obrigado pela sua resposta!
R. Ruiz.
6

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;

Dan Haynes
fonte
5

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

jusathr
fonte
5

A mesma pergunta pode ser feita sobre qualquer outro tipo (não apenas ponteiros):

/* Why is n const? */
const char *expand(const int n) {
    if (n == 1) return "one";
    if (n == 2) return "two";
    if (n == 3) return "three";
    return "many";
}
pmg
fonte
LOL, você está certo. O caso dos ponteiros veio à minha mente primeiro porque pensei que eles eram especiais, mas são como qualquer outra variável passada por valor.
R. Ruiz.
5

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.

zar
fonte
seu comentário faz perceber o quão inútil é minha pergunta porque você está certo: é a mesma coisa que ter qualquer outro parâmetro como const. Pode parecer inútil, mas está lá para ajudar o programador a ter essa restrição e evitar bugs
R. Ruiz.
4

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.

Anders Abel
fonte
4

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.

MartinStettner
fonte
+1 para as otimizações. Pelo menos os livros dizem que é verdade. O que eu gostaria é entender exatamente o que são essas otimizações
R. Ruiz.
4

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:

#include <new>
#include <string.h>

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const GetArray(){ return Array; }
};

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
    Temp.GetArray()[1] = ' '; 
    printf("%s\n",Temp.GetArray());
}

Qual produz:

Os dados de entrada
colocar dados

Mas se tentarmos isso:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}

Nós temos:

error: lvalue necessário como operando esquerdo da atribuição // Drat frustrado novamente!

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:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    delete [] Temp.GetArray(); //Bwuahaha this actually works!
}

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:

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; } //Note & reference operator
        char * &GetNonConstArray(){ return Array; } //Note non-const
};

int main()
{
    TestA Temp;
    Temp.GetArray() = NULL; //Returns error
    Temp.GetNonConstArray() = NULL; //Returns no error
}

Retornará no primeiro, resultando em um erro:

erro: atribuição do local somente leitura 'Temp.TestA :: GetArray ()'

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):

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; }

        void ModifyArrayConst(char * const Data)
        {
            Data[1]; //This is okay, this refers to Data[1]
            Data--; //Produces an error. Don't want to Decrement that.
            printf("Const: %c\n",Data[1]);
        }

        void ModifyArrayNonConst(char * Data)
        {
            Data--; //Argh noo what are you doing?!
            Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
            printf("NonConst: %c\n",Data[1]);
        }
};

int main()
{
    TestA Temp;
    Temp.ModifyArrayNonConst("ABCD");
    Temp.ModifyArrayConst("ABCD");
}

O erro na const produz a seguinte mensagem:

erro: decremento do parâmetro somente leitura 'Dados'

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:

NãoConst: A
Const: B

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:

int main()
{
    int A = 10;
    int * const B = &A;
    *B = 20; //This is permitted
    printf("%d\n",A);
    B = NULL; //This produces an error
}

Ao tentar compilar, produz o seguinte erro:

erro: atribuição da variável somente leitura 'B'

O que provavelmente é uma coisa ruim se uma referência constante a A for desejada. Se B = NULLfor comentado, o compilador nos permitirá modificar *Be, 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.

SSight3
fonte
2
Temp.GetArray() = NULLfalha porque Temp.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.
Oscar Korz 10/10
@ okorz001: Após o teste, você está realmente correto. No entanto, o acima se aplica se você retornar uma referência ao próprio ponteiro. Vou editar minha postagem de acordo.
SSight3 12/12/11
3

Não há nada de especial nos ponteiros onde você nunca gostaria que eles fossem const. Assim como você pode ter intvalores 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.

Mark B
fonte
3

Acredito que isso impediria que o código aumentasse ou diminuísse o ponteiro no corpo da função.

Mike Christensen
fonte
3

Tipos de declarar quaisquer variáveis ​​como:
(1) Declarar uma variável constante.
DataType const varibleName;

 int const x;
    x=4; //you can assign its value only One time
(2) Declare um ponteiro para uma variável constante
const dataType* PointerVaribleName=&X;
 const int* ptr = &X;
     //Here pointer variable refer contents of 'X' that is const Such that its cannot be modified
dataType* const PointerVaribleName=&X;
 int* const ptr = &X;
     //Here pointer variable itself is constant  Such that value of 'X'  can be modified But pointer can't be modified

Pyadav
fonte
Nós fornecemos uma variável de ponteiro como um const em uma função como um argumento quando não wana para alterar seu valor real
Pyadav