¿Diferencia entre char * y const char *?

177

Cuál es la diferencia entre

char* name

que apunta a un literal de cadena constante, y

const char* name
Repartidor de hielo
fuente
¿Qué quieres decir con " literal de cadena constante " en C (no C ++)
gbulmer
1
... char * name se puede hacer que apunte a un literal de cadena constante
Iceman
la constante en "literal de cadena constante" es redundante, ya que todos los literales de cadena son en teoría entidades constantes. Es el contenido de la variable que puede hacerse constante o mutable. La declaración "const" simplemente arrojará un error de tiempo de compilación si intenta cambiar el contenido del personaje señalado por "nombre"
Cupcake
Simple: el nombre "char * name" es un puntero a char, es decir, ambos se pueden cambiar aquí. El nombre "const char * name" es un puntero a const char, es decir, el puntero puede cambiar pero no char.
akD
Lea estas cosas de derecha a izquierda.
Jiapeng Zhang

Respuestas:

406

char*es un puntero mutable a un carácter / cadena mutable .

const char*es un puntero mutable a un carácter / cadena inmutable . No puede cambiar el contenido de las ubicaciones a las que apunta este puntero. Además, los compiladores deben enviar mensajes de error cuando intentas hacerlo. Por la misma razón, la conversión de const char *a char*está en desuso.

char* constes un puntero inmutable (no puede apuntar a ninguna otra ubicación) pero los contenidos de la ubicación a la que apunta son mutables .

const char* constes un puntero inmutable a un carácter / cadena inmutable .

ankit.karwasra
fuente
44
La confusión puede aclararse con el uso de una variable después de las declaraciones mencionadas anteriormente y al hacer referencia a esa variable.
ankit.karwasra
3
@ ankit.karwasra, te perdiste uno más:char const *
Pacerier
Supongo que dos opciones con caracteres / cadenas mutables son muy peligrosas, ya que podrías hacer una memoria de falla de segmentación y, si eres realmente inteligente, podrías hackear la computadora. Es por eso que los compiladores siempre mostraron advertencias en esas implementaciones, creo
Daniel N.
1
¿La mutación no char *da error de segmentación mientras se ejecuta?
Divyanshu Maithani
1
Entonces uso constsi quiero que el compilador dé un error si olvidé y cambié los datos por error, ¿verdad?
Contador م
43
char *name

Puede cambiar el carácter a qué namepuntos, y también el carácter al que apunta.

const char* name

Puede cambiar el carácter a qué namepuntos, pero no puede modificar el carácter al que apunta.
corrección: puede cambiar el puntero, pero no el carácter al que nameapunta ( https://msdn.microsoft.com/en-us/library/vstudio/whkd4k6a(v=vs.100).aspx , consulte "Ejemplos" ) En este caso, el constespecificador se aplica char, no el asterisco.

De acuerdo con la página de MSDN y http://en.cppreference.com/w/cpp/language/declarations , el constantes *es parte de la secuencia de rechazo-especificador, mientras que el constdespués *es parte del declarador.
Una secuencia de especificador de declaración puede ser seguida por múltiples declaradores, razón por la cual const char * c1, c2declara c1as const char *y c2as const char.

EDITAR:

A partir de los comentarios, su pregunta parece ser la diferencia entre las dos declaraciones cuando el puntero apunta a un literal de cadena.

En ese caso, no debe modificar el carácter a qué namepuntos, ya que podría dar lugar a un comportamiento indefinido . Los literales de cadena se pueden asignar en regiones de memoria de solo lectura (implementación definida) y un programa de usuario no debe modificarlo de ninguna manera. Cualquier intento de hacerlo da como resultado un comportamiento indefinido.

Entonces, la única diferencia en ese caso (de uso con literales de cadena) es que la segunda declaración le da una ligera ventaja. Los compiladores generalmente le darán una advertencia en caso de que intente modificar el literal de cadena en el segundo caso.

Ejemplo de muestra en línea:

#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;
}

Salida:

cc1: las advertencias se tratan como errores
prog.c: en la función 'main':
prog.c: 9: error: pasar el argumento 1 de 'strcpy' descarta los calificadores del tipo de destino del puntero

Observe que el compilador advierte para el segundo caso pero no para el primero.

Alok Save
fuente
Gracias ... me estaba mezclando con el literal de cadena constante, que se define como: char * name = "String Literal"; Cambiar "String Literal" no está definido ..
Iceman
@ user1279782: ¡Err, espera! ¿Estás hablando de pointes apuntando a literales de cadena aquí? En ese caso , no debe modificar el carácter al que nameapuntan en ninguno de los casos, ya que podría dar lugar a UB.
Alok Save
Sí, ese era el punto. Entonces, en ese caso, char * name y const char * name se comportan de manera similar, ¿verdad?
Iceman
44
Esta respuesta es extremadamente ambigua o simplemente errónea. Interpretaría "No puede cambiar el carácter al que apunta el nombre, pero puede modificar el carácter al que apunta". Como no ser capaz de modificar el puntero en sí, pero ser capaz de modificar la posición de memoria al que apunta, lo cual es incorrecto: ideone.com/6lUY9s alternativamente por pura C: ideone.com/x3PcTP
shroudednight
1
@shroudednight: necesita aprender un poco más sobre comportamientos indefinidos y debe distinguir entre: permitido y no debe hacerse. :)
Alok Save
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
Afriza N. Arief
fuente
1
Ninguno de sus punteros apunta a "literales de cadena constantes" según la pregunta.
caf
Vale la pena señalar que cambiar el char *valor da un error de segmentación ya que estamos tratando de modificar un literal de cadena (que está presente en la memoria de solo lectura)
Divyanshu Maithani
10

En ningún caso puede modificar un literal de cadena, independientemente de si el puntero a ese literal de cadena se declara como char *o const char *.

Sin embargo, la diferencia es que si el puntero es const char *el compilador debe dar un diagnóstico si intenta modificar el valor señalado, pero si el puntero es, char *entonces no lo hace.

coste y flete
fuente
1
"En ningún caso puede modificar un literal de cadena, independientemente de si ... [se] declara como char * o const char *" Estoy de acuerdo en que el programador no debe intentarlo, pero ¿está diciendo que cada compilador de C, en cada la plataforma rechazará el código, organizará el error del código en el tiempo de ejecución, o algo más? Creo que un archivo podría tener la definición y la inicialización, y otro archivo podría contener extern ... namey tener *name = 'X';. En el 'sistema operativo adecuado', eso podría fallar, pero en los sistemas integrados, esperaría que hiciera algo específico para la plataforma / compilador.
Gbulmer
@gbulmer: no puede modificar un literal de cadena en un programa C correcto. Lo que puede resultar en un programa C incorrecto que intenta no está ni aquí ni allá.
caf
@gbulmer: Una definición útil es un programa que no rompe las restricciones especificadas por el estándar del lenguaje C. En otras palabras, un programa que modifica un literal de cadena es incorrecto de la misma manera que uno que desreferencia un puntero nulo o realiza una división por 0 es incorrecto.
caf
caf - Pensé que eso podría ser lo que querías decir. Entonces "En ningún caso puede modificar un literal de cadena" parece haber terminado de indicarlo. Sería exacto decir "En ambos casos, las restricciones especificadas por el estándar del lenguaje C se han roto, independientemente ... No es posible que el compilador o el sistema de tiempo de ejecución identifique infracciones del estándar en todos los casos". ¿Asumo que el estándar toma la posición de que el efecto no está definido?
gbulmer
1
Cuando un estándar no puede afirmar nada de ninguna manera, creo que definir el comportamiento como 'indefinido' parece ser exactamente el límite correcto y útil. Para afirmar la relación, un 'programa C correcto' ' no puede desreferenciar un puntero nulo' suena equivalente a probar el problema de detención. Pero no me importa No lo haría y esperaría salir con la suya 'scott free' :-)
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

Lo anterior establece que str apunte al valor literal "Hola", que está codificado en la imagen binaria del programa, que está marcado como de solo lectura en la memoria, significa que cualquier cambio en este literal de cadena es ilegal y arrojaría fallas de segmentación.

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".
Mohit
fuente
2

Lo primero que puedes cambiar realmente si quieres, lo segundo no puedes. Lea sobre la constcorrección (hay algunas buenas guías sobre la diferencia). También hay un lugar char const * namedonde no puedes cambiarlo.

chikuba
fuente
¿Qué puede cambiar exactamente?
Antti Haapala
2

La pregunta es cuál es la diferencia entre

char *name

que apunta a un literal de cadena constante, y

const char *cname

Es decir

char *name = "foo";

y

const char *cname = "foo";

No hay mucha diferencia entre los 2 y ambos pueden verse como correctos. Debido al largo legado del código C, los literales de cadena han tenido un tipo de char[], no const char[], y hay muchos códigos antiguos que también aceptan en char *lugar de const char *, incluso cuando no modifican los argumentos.

La principal diferencia de los 2 en general es que *cnameo cname[n]evaluará valores de tipo const char, mientras que *nameo name[n]evaluará valores de tipo char, que son valores modificables . Se requiere un compilador conforme para generar un mensaje de diagnóstico si el objetivo de la asignación no es un valor modificable ; no necesita producir ninguna advertencia sobre la asignación a valores de tipo char:

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

El compilador no está obligado a detener la compilación en ninguno de los casos; es suficiente que produzca una advertencia para la asignación cname[0]. El programa resultante no es un programa correcto . El comportamiento de la construcción es indefinido . Puede bloquearse, o peor aún, podría no bloquearse y cambiar el literal de cadena en la memoria.

Antti Haapala
fuente