¿Cuál es la diferencia entre char a[] = ?string?; y char *p = ?cadena?;?

Resuelto Sachin Mhetre asked hace 12 años • 8 respuestas

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?

Sachin Mhetre avatar Feb 27 '12 12:02 Sachin Mhetre
Aceptado

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:

  1. El primero crea un puntero.
  2. 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 NULLterminador. La matriz stringse 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 sizeofse 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 constpalabra clave. Entonces la declaración debería ser:

const char *p = "string";

Además, debe usar la strlen()función y no sizeofencontrar el tamaño de la cadena, ya que el sizeofoperador 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 constpalabra 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.

Alok Save avatar Mar 09 '2012 08:03 Alok Save

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 nombre a. Es decir, hay un lugar nombrado aen el que pueden sentarse seis personajes. La declaración de puntero char *p;, por otro lado, solicita un lugar que contenga un puntero. El puntero debe ser conocido por el nombre py 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 si xes una matriz o un puntero. Dadas las declaraciones anteriores, cuando el compilador ve la expresión a[3], emite código para comenzar en la ubicación a, pasar tres elementos y buscar el carácter allí. Cuando ve la expresión p[3], emite código para comenzar en la ubicación p, 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, ambos a[3]y p[3]resultan ser el carácter l, pero el compilador llega allí de manera diferente.

Fuente: comp.lang.c Lista de preguntas frecuentes · Pregunta 6.2

 avatar Mar 09 '2012 11:03
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.

Ignacio Vazquez-Abrams avatar Feb 27 '2012 05:02 Ignacio Vazquez-Abrams

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 mallocy callocestará 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 ONLYsegmento y en los otros tres es para READy 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 psimplemente 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 WRITEacceso en la pila.

Pero esto p[0] = 'x'provocará un bloqueo, porque solo tenemos READacceso 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 pa la dirección de la cadena start(nuevamente starttambién son datos de solo lectura en un segmento de texto). Si desea modificar los valores presentes en *plos 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.

rashok avatar Jun 22 '2012 07:06 rashok

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 papunta a conduce a un comportamiento indefinido.

char a[] = "string";crea una matriz e inicializa su contenido utilizando una cadena literal "string".

LihO avatar Feb 27 '2012 05:02 LihO