Diferença entre char * e const char *?


177

Qual é a diferença entre

char* name

que aponta para uma constante string literal e

const char* name

o que você quer dizer com " constante literal string" em C (não C ++)
gbulmer

1
... char * nome pode ser feita para apontar para um literal string constante
Iceman

a constante em "literal de cadeia constante" é redundante, pois todos os literais de cadeia são, em teoria, entidades constantes. É o conteúdo da variável que pode ser constante ou mutável. A declaração "const" simplesmente irá lançar um erro de tempo de compilação se você tentar alterar o conteúdo do caráter apontada pelo "nome"
Cupcake

Simples: o nome "char * name" é um ponteiro para char, ou seja, ambos podem ser alterados aqui. O nome "const char * name" é um ponteiro para const char, ou seja, o ponteiro pode mudar, mas não char.
AKD

Leia estas coisas da direita para a esquerda.
Jiapeng Zhang

Respostas:


406

char*é um ponteiro mutável para um caractere / sequência mutável .

const char*é um ponteiro mutável para um caractere / sequência imutável . Você não pode alterar o conteúdo dos locais para os quais este ponteiro aponta. Além disso, os compiladores são obrigados a fornecer mensagens de erro quando você tenta fazer isso. Pelo mesmo motivo, a conversão de const char *para char*foi preterida.

char* consté um ponteiro imutável (não pode apontar para nenhum outro local), mas o conteúdo do local no qual aponta é mutável .

const char* consté um ponteiro imutável para um caractere / sequência imutável .


4
A confusão pode ser esclarecida com o uso de uma variável após as declarações mencionadas acima e dando referência a essa variável.
Ank.karwasra

3
@ ankit.karwasra, você perdeu mais um:char const *
Pacerier

Suponho que duas opções com caracteres / cadeias de caracteres mutáveis ​​sejam altamente perigosas, pois você pode criar uma memória de falha de segmentação e, se você for realmente inteligente, poderá invadir o computador. É por isso que os compiladores sempre mostravam avisos nessas implementações, eu acho
Daniel N.

1
A mutação não causará char *falha de segmentação durante a execução?
Divyanshu Maithani

1
Então eu uso constse eu quero que o compilador dê erro se eu esqueci e alterei os dados por engano, certo?
precisa saber é o seguinte

43
char *name

Você pode alterar o caractere em que namepontos, e também o caractere em que aponta.

const char* name

Você pode alterar o caractere em que namepontos, mas não pode modificar o caractere em que aponta.
correção: você pode alterar o ponteiro, mas não o caracter para o qual nameaponta ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , consulte "Exemplos" ) Nesse caso, o constespecificador se aplica ao char, não ao asterisco.

De acordo com a página do MSDN e http://en.cppreference.com/w/cpp/language/declarations , o constantes do *faz parte da sequência decl-specifier, enquanto o constdepois *faz parte do declarador.
Uma sequência do especificador de declaração pode ser seguida por vários declaradores, razão pela qual const char * c1, c2declara c1comoconst char * e c2como const char.

EDITAR:

A partir dos comentários, sua pergunta parece estar se perguntando sobre a diferença entre as duas declarações quando o ponteiro aponta para uma string literal.

Nesse caso, você não deve modificar o caractere em que namepontos, pois isso pode resultar em comportamento indefinido . Literais de seqüência de caracteres podem ser alocados em regiões de memória somente leitura (implementação definida) e um programa do usuário não deve modificá-lo de qualquer maneira. Qualquer tentativa de fazer isso resulta em comportamento indefinido.

Portanto, a única diferença nesse caso (de uso com literais de seqüência de caracteres) é que a segunda declaração oferece uma pequena vantagem. Os compiladores geralmente emitem um aviso caso você tente modificar a string literal no segundo caso.

Exemplo de amostra online:

#include <string.h>
int main()
{
    char *str1 = "string Literal";
    const char *str2 = "string Literal";
    char source[] = "Sample string";

    strcpy(str1,source);    //No warning or error, just Undefined Behavior
    strcpy(str2,source);    //Compiler issues a warning

    return 0;
}

Resultado:

cc1: avisos sendo tratados como erros
prog.c: Na função 'main':
prog.c: 9: error: passar o argumento 1 de 'strcpy' descarta qualificadores do tipo de destino do ponteiro

Observe que o compilador avisa para o segundo caso, mas não para o primeiro.


Obrigado .. eu estava misturando com a constante string literal, que é definida como: char * name = "String Literal"; Mudando "String Literal" é indefinido ..
Iceman

@ user1279782: Err, espera! Você está falando de pontos apontando para literais de string aqui? Nesse caso, você não deve modificar o caracter para o qual os namepontos em ambos os casos. Isso pode resultar em UB.
Alok Salvar

Sim, esse era o ponto. Então, nesse caso, char * name e const char * name se comportam de maneira semelhante, certo?
Iceman

4
Essa resposta é extremamente ambígua ou simplesmente errada. Eu interpretaria "Você não pode alterar o caracter para o qual o nome aponta, mas Você pode modificar o caracter para o qual ele aponta". Como não sendo capaz de modificar o ponteiro em si, mas ser capaz de modificar o local da memória que ele aponta, o que é incorreto: ideone.com/6lUY9s alternativamente por puro C: ideone.com/x3PcTP
shroudednight

1
@shroudednight: Você precisa aprender um pouco mais sobre comportamentos indefinidos e precisa distinguir entre: permitido e não deve ser feito. :)
Alok Salvar

16
char mystring[101] = "My sample string";
const char * constcharp = mystring; // (1)
char const * charconstp = mystring; // (2) the same as (1)
char * const charpconst = mystring; // (3)

constcharp++; // ok
charconstp++; // ok
charpconst++; // compile error

constcharp[3] = '\0'; // compile error
charconstp[3] = '\0'; // compile error
charpconst[3] = '\0'; // ok

// String literals
char * lcharp = "My string literal";
const char * lconstcharp = "My string literal";

lcharp[0] = 'X';      // Segmentation fault (crash) during run-time
lconstcharp[0] = 'X'; // compile error

// *not* a string literal
const char astr[101] = "My mutable string";
astr[0] = 'X';          // compile error
((char*)astr)[0] = 'X'; // ok

1
Nenhum de seus ponteiros aponta para "literais de cadeia constante", conforme a pergunta.
caf

É importante notar que a mudança do char *valor dá falha de segmentação, uma vez que está tentando modificar um literal string (que está presente em memória apenas para leitura)
Divyanshu Maithani

10

Em nenhum dos casos, você pode modificar um literal de cadeia de caracteres, independentemente de o ponteiro para esse literal de cadeia ser declarado como char *ouconst char * .

No entanto, a diferença é que, se o ponteiro estiver const char *, o compilador deverá fornecer um diagnóstico se você tentar modificar o valor apontado, mas se o ponteiro estiver char *, não o fará.


1
"Em nenhum dos casos você pode modificar uma literal de string, independentemente de ... [ela] ser declarada como char * ou const char *" Concordo que o programador não deve tentar, mas você está dizendo que todo compilador C, em todos os plataforma rejeitará o código, providenciará a falha do código no tempo de execução ou algo mais? Eu acredito que um arquivo pode ter a definição e inicialização, e outro arquivo pode conter extern ... namee ter *name = 'X';. No 'sistema operacional adequado', isso pode falhar, mas em sistemas embarcados, espero que faça algo específico da plataforma / compilador.
gbulmer

@ bulbulmer: Você não pode modificar uma string literal em um programa C correto. O que um programa C incorreto que tenta pode resultar não está aqui nem ali.
caf

@ bulbulmer: Uma definição útil é um programa que não quebra nenhuma restrição especificada pelo padrão da linguagem C. Em outras palavras, um programa que modifica um literal de cadeia de caracteres está incorreto da mesma maneira que aquele que desreferencia um ponteiro nulo ou executa uma divisão por 0 está incorreto.
caf

caf - Eu pensei que poderia ser o que você quis dizer. Então "Em nenhum dos casos você pode modificar uma string literal" parece exagerar. Seria preciso dizer "Em ambos os casos, as restrições especificadas pelo padrão da linguagem C foram quebradas, independentemente ... Não é possível para o compilador ou o sistema de tempo de execução identificar violações do padrão em todos os casos". Suponho que o padrão assume a posição de que o efeito é indefinido?
gbulmer

1
Quando um padrão não pode afirmar nada de qualquer maneira, acho que definir comportamento como "indefinido" parece ser exatamente o limite certo e útil. Afirmar a relação que um 'programa C correto' ' não pode desreferenciar um ponteiro nulo' soa equivalente a provar o problema de parada. Mas eu não me importo. Eu não faria isso e esperar para fugir com ele 'livre scott' :-)
gbulmer

4

CASO 1:

char *str = "Hello";
str[0] = 'M'  //Warning may be issued by compiler, and will cause segmentation fault upon running the programme

O conjunto acima define str para apontar para o valor literal "Hello" que é codificado na imagem binária do programa, que é sinalizada como somente leitura na memória, significa que qualquer alteração nesse literal String é ilegal e causaria falhas de segmentação.

CASO 2:

const char *str = "Hello";
str[0] = 'M'  //Compile time error

CASO 3:

char str[] = "Hello";
str[0] = 'M'; // legal and change the str = "Mello".

2

O primeiro você pode realmente mudar, se quiser, o segundo, não. Leia sobre constcorreção (há alguns guias legais sobre a diferença). Também há char const * nameonde você não pode refazê-lo.


O que exatamente pode mudar?
Antti Haapala

2

A questão é qual é a diferença entre

char *name

que aponta para uma constante string literal e

const char *cname

Ou seja, dado

char *name = "foo";

e

const char *cname = "foo";

Não há muita diferença entre os 2 e ambos podem ser vistos como corretos. Devido ao longo legado do código C, os literais de string tiveram um tipo de char[], não const char[], e há muitos códigos mais antigos que da mesma forma aceitam, em char *vez deconst char * , mesmo quando não modificam os argumentos.

A principal diferença dos 2 em geral é que *cnameou cname[n]será avaliada em valores do tipo const char, enquanto *nameou name[n]será avaliada em valores do tipo char, que são valores modificáveis . Um compilador em conformidade é necessário para produzir uma mensagem de diagnóstico se o destino da atribuição não for um valor lificável modificável ; ele não precisa produzir nenhum aviso na atribuição para lvalues ​​do tipo char:

name[0] = 'x'; // no diagnostics *needed*
cname[0] = 'x'; // a conforming compiler *must* produce a diagnostics message

O compilador não é necessário para interromper a compilação nos dois casos; basta produzir um aviso para a atribuição cname[0]. O programa resultante não é um programa correto . O comportamento da construção é indefinido . Pode travar ou, pior ainda, pode não travar e pode alterar a string literal na memória.


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.