Observação: Talvez nunca nem eu nem você tenhamos que nos preocupar com operações do gênero, de tão baixo nível, ainda mais que como eu mesmo disse, isso é uma função da biblioteca padrão (ou como no caso de C++ que abole as strings de C com a classe string). Mas, estudar nunca é demais, isso ajuda a clarear ainda mais sobre ponteiros.
De modo geral (mas com várias contradições), uma string em C é basicamente um array de chars, ou especificamente um char *. Por essa razão é possível operar em strings apenas utilizando ponteiros. Um exemplo bem legal é a função strlen da biblioteca padrão. O código comenta tudo sobre o assunto, então vamos direto ao ponto.
Ponteiros de C e C++ possuem várias características avançadas e até mesmo alguns detalhes irritantes, que confundem qualquer um. Procurarei em 2 ou 3 tutoriais cobrir detalhes obscuros de ponteiros aqui no site.
Nesse artigo, vou cobrir a relação ponteiro vs. constantes:
Ponteiro:
tipo *nome;
Ponteiro não constante para dados constantes:
const tipo *nome;
Ponteiro constante para dados não constantes:
tipo *const nome;
Ponteiro e dados constantes:
const tipo *const nome;
A ideia é que se o operador * vem depois de const são assumidos os dados bases (assim, é um ponteiro para dados constantes). Se o operador vem antes de const, então é um ponteiro constante.
Isso pode ser facilmente identificado quando é feita uma leitura da direita para a esquerda:
// Leitura de declarações <-
int *x; // x é um ponteiro para int
const int *y; // y é um ponteiro não constante para um int constante
const int *const p; // p é um ponteiro constante para um int constante
int *const w; // w é um ponteiro constante para um int não constante
Exemplo completo
// +——————————————————————–+
// | Ponteiro não constante para dados não constantes |
// +——————————————————————–+
char *p, a, b;
p = &a; // Recebe endereço de a
*p = ‘A’; // Altera conteúdo de a
p = &b; // Recebe endereço de b
// +——————————————————————–+
// | Ponteiro não constante para dados constantes |
// +——————————————————————–+
const int *p;
int i = 25, z;
// p aponta para i, o qual não pode ser alterado.
p = &i;
// Mas, p pode apontar para outra variável
p = &z;
// Esse comando gera falha, porque p vai tentar alterar
// o conteúdo de z, mas não pode pois foi declarado para
// apontar para dados constantes
// *p = 35;
// Comando válido, por não se referenciar ao ponteiro, e sim,
// diretamente na variável
i = 45;
// +——————————————————————–+
// | Ponteiro constante para dados não constantes |
// +——————————————————————–+
int i = 25, z;
// Como é ponteiro constante, atribuição de endereço deve ser feito
// na definição.
int *const p = &i;
// Comando inválido, não se pode alterar para onde p aponta
// p = &i;
// Comando válido, por se tratar de dados não constantes
*p = 45;
// +——————————————————————–+
// | Ponteiro constante para dados constantes |
// +——————————————————————–+
int i = 25, z;
// Como é ponteiro constante, atribuição de endereço deve ser feito
// na definição.
const int *const p = &i;
// Comando inválido, não se pode alterar para onde p aponta
// p = &i;
// Comando inválido, por se tratar de dados constantes
// *p = 45;
// Comando válido, por não se referenciar ao ponteiro, e sim,
// diretamente na variável
i = 45;