¿Cuál es la diferencia entre char a[] = ?string?; y char *p = ?cadena?;?
Como dice el título, ¿Cuál es la diferencia entre
char a[] = ?string?; and
char *p = ?string?;
Esta pregunta me la hicieron en una entrevista. Ni siquiera entiendo la declaración.
char a[] = ?string?
¿Aquí qué es ?
el operador? ¿Es parte de una cadena o tiene algún significado específico?
Parece ?
ser un error tipográfico, no es semánticamente válido. Entonces, la respuesta asume que ?
es un error tipográfico y explica lo que probablemente el entrevistador realmente quiso preguntar.
Para empezar, ambos son claramente diferentes:
- El primero crea un puntero.
- El segundo crea una matriz.
Siga leyendo para obtener una explicación más detallada:
La versión de matriz:
char a[] = "string";
Crea una matriz que es lo suficientemente grande como para contener la cadena literal "cadena", incluido su NULL
terminador. La matriz string
se inicializa con la cadena literal "cadena". La matriz se puede modificar más adelante . Además, el tamaño de la matriz se conoce incluso en el momento de la compilación, por lo que sizeof
se puede utilizar el operador para determinar su tamaño.
La versión del puntero:
char *p = "string";
Crea un puntero para apuntar a una "cadena" literal de cadena. Esto es más rápido que la versión de matriz, pero la cadena apuntada por el puntero no debe cambiarse , porque está ubicada en una memoria definida por implementación de solo lectura. La modificación de dicha cadena literal da como resultado un comportamiento indefinido .
De hecho, C++ 03 desaprueba el uso [Ref 1] de literales de cadena sin la const
palabra clave. Entonces la declaración debería ser:
const char *p = "string";
Además, debe usar la strlen()
función y no sizeof
encontrar el tamaño de la cadena, ya que el sizeof
operador simplemente le dará el tamaño de la variable del puntero.
¿Qué versión es mejor y cuál debo usar?
Depende del uso.
- Si no necesita realizar ningún cambio en la cadena, utilice la versión de puntero.
- Si tiene la intención de cambiar los datos, utilice la versión de matriz.
Nota: Esto no es C++ pero es específico de C.
Tenga en cuenta que el uso de un literal de cadena sin la const
palabra clave es perfectamente válido en C. Sin embargo, modificar un literal de cadena sigue siendo un comportamiento indefinido en C [Ref 2] .
Esto plantea una pregunta interesante: ¿
Cuál es la diferencia entre char* y const char* cuando se usan con cadenas literales en C?
Para ventiladores estándar:
[Ref 1] Estándar C++03: §4.2/2
Un literal de cadena (2.13.4) que no sea un literal de cadena ancha se puede convertir en un valor rvalue de tipo “puntero a carácter”; un literal de cadena ancha se puede convertir en un valor rvalue de tipo "puntero a wchar_t". En cualquier caso, el resultado es un puntero al primer elemento de la matriz. Esta conversión se considera solo cuando existe un tipo de destino de puntero apropiado explícito y no cuando existe una necesidad general de convertir de un valor l a un valor r. [ Nota: esta conversión está en desuso . Véase el Anexo D. ] A los efectos de la clasificación en resolución de sobrecarga (13.3.3.1.1), esta conversión se considera una conversión de matriz a puntero seguida de una conversión de calificación (4.4). [Ejemplo: "abc" se convierte en "puntero a carácter constante" como una conversión de matriz a puntero, y luego a "puntero a carácter" como una conversión de calificación. ]
C++11 simplemente elimina la cita anterior, lo que implica que es código ilegal en C++11.
[Ref 2] Estándar C99 6.4.5/5 "Literales de cadena - Semántica":
En la fase de traducción 7, se agrega un byte o código de valor cero a cada secuencia de caracteres multibyte que resulta de una cadena literal o literales. Luego, la secuencia de caracteres multibyte se utiliza para inicializar una matriz de duración de almacenamiento estático y longitud suficiente para contener la secuencia. Para los literales de cadenas de caracteres, los elementos de la matriz tienen el tipo char y se inicializan con los bytes individuales de la secuencia de caracteres multibyte; para literales de cadena ancha, los elementos de la matriz tienen el tipo wchar_t y se inicializan con la secuencia de caracteres anchos...
No se especifica si estas matrices son distintas siempre que sus elementos tengan los valores apropiados. Si el programa intenta modificar dicha matriz, el comportamiento no está definido.
El primero es una matriz y el otro es un puntero.
La declaración de matriz
char a[6];
solicita que se reserve espacio para seis caracteres, que se conocerán por el nombrea
. Es decir, hay un lugar nombradoa
en el que pueden sentarse seis personajes. La declaración de punterochar *p;
, por otro lado, solicita un lugar que contenga un puntero. El puntero debe ser conocido por el nombrep
y puede apuntar a cualquier carácter (o conjunto contiguo de caracteres) en cualquier lugar.Las declaraciones
char a[] = "string"; char *p = "string";
daría como resultado estructuras de datos que podrían representarse así:
+---+---+---+---+---+---+----+ a: | s | t | r | i | n | g | \0 | +---+---+---+---+---+---+----+ +-----+ +---+---+---+---+---+---+---+ p: | *======> | s | t | r | i | n | g |\0 | +-----+ +---+---+---+---+---+---+---+
Es importante darse cuenta de que una referencia como
x[3]
genera código diferente dependiendo de six
es una matriz o un puntero. Dadas las declaraciones anteriores, cuando el compilador ve la expresióna[3]
, emite código para comenzar en la ubicacióna
, pasar tres elementos y buscar el carácter allí. Cuando ve la expresiónp[3]
, emite código para comenzar en la ubicaciónp
, buscar el valor del puntero allí, agregar tres tamaños de elementos al puntero y, finalmente, buscar el carácter al que apunta. En el ejemplo anterior, ambosa[3]
yp[3]
resultan ser el carácterl
, pero el compilador llega allí de manera diferente.
Fuente: comp.lang.c Lista de preguntas frecuentes · Pregunta 6.2
char a[] = "string";
Esto asigna la cadena en la pila.
char *p = "string";
Esto crea un puntero en la pila que apunta al literal en el segmento de datos del proceso.
?
es quien lo escribió sin saber lo que estaba haciendo.
Pila, montón, segmento de datos (y BSS) y segmento de texto son los cuatro segmentos de la memoria de proceso. Todas las variables locales definidas estarán en la pila. La memoria asignada dinámicamente usando malloc
y calloc
estará en el montón. Todas las variables globales y estáticas estarán en el segmento de datos. El segmento de texto tendrá el código ensamblador del programa y algunas constantes.
En estos 4 segmentos, el segmento de texto es el READ ONLY
segmento y en los otros tres es para READ
y WRITE
.
char a[] = "string";
- Esta declaración asignará memoria para 7 bytes en la pila (porque es una variable local) y mantendrá los 6 caracteres ( s, t, r, i, n, g
) más el carácter NULL ( \0
) al final.
char *p = "string";
- Esta declaración asignará memoria para 4 bytes (si es una máquina de 32 bits) en la pila (porque también es una variable local) y contendrá el puntero de la cadena constante cuyo valor es "string"
. Estos 6 bytes de cadena constante estarán en un segmento de texto. Este es un valor constante. La variable puntero p
simplemente apunta a esa cadena.
Ahora a[0]
(el índice puede ser de 0 a 5) significa que accederá al primer carácter de esa cadena que está en la pila. Entonces podemos escribir también en esta posición. a[0] = 'x'
. Esta operación está permitida porque tenemos READ WRITE
acceso en la pila.
Pero esto p[0] = 'x'
provocará un bloqueo, porque solo tenemos READ
acceso al segmento de texto. Se producirá un error de segmentación si escribimos en un segmento de texto.
Pero puedes cambiar el valor de la variable p
, porque es una variable local en la pila. como abajo
char *p = "string";
printf("%s", p);
p = "start";
printf("%s", p);
Esto está permitido. Aquí estamos cambiando la dirección almacenada en la variable de puntero p
a la dirección de la cadena start
(nuevamente start
también son datos de solo lectura en un segmento de texto). Si desea modificar los valores presentes en *p
los medios, opte por la memoria asignada dinámicamente.
char *p = NULL;
p = malloc(sizeof(char)*7);
strcpy(p, "string");
Ahora p[0] = 'x'
se permite la operación, porque ahora estamos escribiendo en el montón.
char *p = "string";
crea un puntero a la memoria de solo lectura donde "string"
se almacena el literal de cadena. Intentar modificar una cadena que p
apunta a conduce a un comportamiento indefinido.
char a[] = "string";
crea una matriz e inicializa su contenido utilizando una cadena literal "string"
.