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

Como dice el encabezado, ¿cuál es la diferencia entre

char a[] = ?string?; and char *p = ?string?; 

Esta pregunta me fue hecha en una entrevista. Incluso no entiendo la statement.

 char a[] = ?string? 

Aquí lo que es ? ¿operador? ¿Es parte de una cadena o tiene algún significado específico?

El primero es una matriz y el otro es un puntero.

La statement de matriz char a[6]; solicita que se reserve un espacio para seis caracteres, que se conocerá con el nombre a . Es decir, hay una ubicación llamada a en la que se pueden sentar seis personajes. La statement del puntero char *p; por otro lado, solicita un lugar que contiene un puntero. El puntero debe ser conocido con el nombre p , y puede señalar cualquier char (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 un código diferente dependiendo de si x es una matriz o un puntero. Dadas las declaraciones anteriores, cuando el comstackdor ve la expresión a[3] , emite un código para comenzar en la ubicación a , mover tres elementos más allá de él y buscar el carácter allí. Cuando ve la expresión p[3] , emite código para comenzar en la ubicación p , busca el valor del puntero allí, agrega tres tamaños de elemento al puntero y finalmente busca el carácter al que apunta. En el ejemplo anterior, tanto a[3] como un p[3] son el carácter l , pero el comstackdor llega de manera diferente.

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

El ? parece ser un error tipográfico, no es semánticamente válido. Entonces la respuesta asume el ? es un error tipográfico y explica lo que probablemente el entrevistador realmente quiso preguntar.


Ambos son claramente diferentes, para empezar:

  1. El primero crea un puntero.
  2. El segundo crea una matriz.

Siga leyendo para una explicación más detallada:

La versión de Array:

 char a[] = "string"; 

Crea una matriz lo suficientemente grande como para contener la cadena literal “cadena”, incluido su terminador NULL . La string matriz se inicializa con la cadena literal “cadena”. La matriz puede ser modificada en un momento posterior . Además, el tamaño de la matriz se conoce incluso en tiempo de comstackción, por lo que el operador sizeof se puede utilizar para determinar su tamaño.


La versión del puntero:

 char *p = "string"; 

Crea un puntero para señalar a una cadena literal “cadena”. Esto es más rápido que la versión de matriz, pero la cadena apuntada por el puntero no debe cambiarse , ya que está ubicada en una memoria definida solo por la implementación. La modificación de dicho literal de cadena da como resultado un comportamiento no definido .

De hecho, C ++ 03 desaprueba el uso [Ref 1] de literal de cadena sin la palabra clave const . Entonces la statement debería ser:

 const char *p = "string"; 

Además, debe usar la función strlen() y no sizeof para encontrar el tamaño de la cadena, ya que el operador sizeof 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, use la versión del puntero.
  • Si tiene la intención de cambiar los datos, use la versión de matriz.

Nota: Esto no es C ++, pero esto es específico de C.

Tenga en cuenta que el uso de literal de cadena sin la palabra clave const es perfectamente válido en C. Sin embargo, la modificación de un literal de cadena sigue siendo un comportamiento indefinido en C [Ref 2] .

Esto nos lleva a una pregunta interesante,
¿Cuál es la diferencia entre char * y const char * cuando se usa con literales de cadena en C?


Para los fanáticos de Standerdese:
[Ref 1] C ++ 03 Estándar: §4.2 / 2

Un literal de cadena (2.13.4) que no es un literal de cadena ancha se puede convertir a un valor r de tipo “puntero a char”; un literal de cadena ancho se puede convertir a un valor r 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 hay un tipo de destino de puntero apropiado explícito, y no cuando hay una necesidad general de convertir de un valor l a un valor r. [ Nota: esta conversión está en desuso Ver el Anexo D.] Con el propósito de clasificar en resolución de sobrecarga (13.3.3.1.1), esta conversión se considera una conversión de matriz a punta seguido por una conversión de calificación (4.4). [Ejemplo: “abc” se convierte en “puntero a const char” como una conversión de matriz a puntero, y luego a “puntero a char” como una conversión de calificación. ]

C ++ 11 simplemente elimina la cita anterior, lo que implica que es un código ilegal en C ++ 11.

[Ref 2] C99 estándar 6.4.5 / 5 “Literales de cuerda – 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 literal. La secuencia de caracteres multibyte se usa luego para inicializar una matriz de duración de almacenamiento estático y longitud suficiente para contener la secuencia. Para literales de cadena de caracteres, los elementos de la matriz tienen tipo char, y se inicializan con los bytes individuales de la secuencia de caracteres multibyte; para literales de cadenas anchas, 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 adecuados. Si el progtwig intenta modificar dicha matriz, el comportamiento no está definido.

 char a[] = "string"; 

Esto asigna la cadena en la stack.

 char *p = "string"; 

Esto crea un puntero en la stack que apunta al literal en el segmento de datos del proceso.

? es quien lo escribió sin saber lo que estaban haciendo.

Pila, montón, segmento de datos (y BSS) y secuencia de texto son los cuatro segmentos de la memoria de proceso. Todas las variables locales definidas estarán en la stack. La memoria asignada dinámicamente utilizando malloc y calloc estará en montón. Todas las variables globales y estáticas estarán en el segmento de datos. El segmento de texto tendrá el código de ensamblaje del progtwig y algunas constantes.

En estos 4 segmentos, el segmento de texto es el segmento de READ ONLY y en el rest los tres son para READ y WRITE .

char a[] = "string"; – Esta statemnt asignará memoria para 7 bytes en la stack (debido a la 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 instrucción asignará memoria para 4 bytes (si es máquina de 32 bits) en la stack (porque esta también es una variable local) y mantendrá el puntero de la cadena constante cuyo valor es "string" . Este 6 bytes de cadena constante estará en el segmento de texto. Este es un valor constante. La variable de puntero p solo apunta a esa cadena.

Ahora bien, a[0] (índice puede ser de 0 a 5) significa que accederá al primer carácter de esa cadena que esté en la stack. Entonces podemos escribir también en esta posición. a[0] = 'x' . Esta operación está permitida porque hemos READ WRITE acceso de READ WRITE en la stack.

Pero p[0] = 'x' provocará un locking, ya que solo tenemos acceso de READ al texto segement. La falla de segmentación ocurrirá si hacemos cualquier escritura en segmento de texto.

Pero puede cambiar el valor de la variable p , porque su variable local en la stack. 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 del puntero p a la dirección del start de la secuencia (de nuevo, el start también es un dato de solo lectura en el texto segement). Si desea modificar los valores presentes en *p significa ir a la memoria asignada dinámicamente.

 char *p = NULL; p = malloc(sizeof(char)*7); strcpy(p, "string"); 

Ahora se permite la operación p[0] = 'x' , porque ahora estamos escribiendo en heap.

char *p = "string"; crea un puntero a la memoria de solo lectura donde se almacena la cadena literal "string" . Intentar modificar la cadena que p apunta conduce a un comportamiento indefinido.

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

Difieren en cuanto a dónde se almacena la memoria. Idealmente, el segundo debería usar const char *.

El primero

 char buf[] = "hello"; 

crea un búfer automático lo suficientemente grande como para contener los caracteres y copiarlos (incluido el terminador nulo).

El segundo

 const char * buf = "hello"; 

debería usar const y simplemente crea un puntero que apunta a la memoria generalmente almacenada en un espacio estático donde es ilegal modificarlo.

Lo contrario (del hecho de que puede modificar el primero con seguridad y no el segundo) es que es seguro devolver el segundo puntero desde una función, pero no la primera. Esto se debe a que el segundo seguirá siendo un puntero de memoria válido fuera del scope de la función, el primero no lo hará.

 const char * sayHello() { const char * buf = "hello"; return buf; // valid } const char * sayHelloBroken() { char buf[] = "hello"; return buf; // invalid } 

a declara una matriz de valores de caracteres: una matriz de caracteres que finaliza.

p declara un puntero, que se refiere a una cadena C inmutable, terminada, cuya ubicación de almacenamiento exacta está definida por la implementación. Tenga en cuenta que esto debe ser const calificado (por ejemplo, const char *p = "string"; ).

Si lo imprime usando std::cout << "a: " << sizeof(a) << "\np: " << sizeof(p) << std::endl; , verá diferencias en sus tamaños (nota: los valores pueden variar según el sistema):

 a: 7 p: 8 

Aquí lo que es? ¿operador? ¿Es parte de una cadena o tiene algún significado específico?

 char a[] = ?string? 

Supongo que alguna vez fueron comillas dobles "string" , que potencialmente se convirtieron en "comillas inteligentes", luego no se pudieron representar como tales en el camino, y se convirtieron a ? .

C y C ++ tienen relaciones Pointer to Array muy similares …

No puedo hablar sobre las ubicaciones de memoria exactas de las dos afirmaciones sobre las que pregunta, pero encontré artículos interesantes y útiles para entender algunas de las diferencias entre la statement del puntero char y la statement de la matriz char.

Para mayor claridad:

Relación puntero y matriz C

C ++ Puntero a una matriz

Creo que es importante recordar que una matriz, en C y C ++, es un puntero constante al primer elemento de la matriz. Y, en consecuencia, puede realizar aritmética de puntero en la matriz.

char * p = “cadena”; <--- Este es un puntero que apunta a la primera dirección de una cadena de caracteres.

lo siguiente también es posible:

 char *p; char a[] = "string"; p = a; 

En este punto, p ahora hace referencia a la primera dirección de memoria de a (la dirección del primer elemento)

y entonces * p == ‘s’

* (p ++) == ‘t’ y así sucesivamente. (o * (p + 1) == ‘t’)

y lo mismo funcionaría para a: * (a ++) o * (a + 1) también sería igual a ‘t’