TransWikia.com

¿Por qué se puede asignar directamente una cadena de caracteres a un apuntador y en cambio, no una constante de caracter?

Stack Overflow en español Asked by DiegoSD on August 27, 2021

En C, lo siguiente es correcto:

char *aptr_cadena;
aptr_cadena="se puede asignar una cadena así directamente";

Sin embargo, ésto no:

aptr_cadena= 'x';

Quiero saber el por qué fundamentado. Si es posible una bibliografía donde lo explique. Gracias.

2 Answers

Una cadena literal no es más que un bloque de memoria donde en cada dirección estará guardado el caracter.

Ejemplo:

char *a;
a = "Hello";

Entonces, la cadena Hello en memoria se vería reflejado de este modo:

Address:    0x20   0x21  0x22  0x23  0x24  0x25
Data:        H       e     l     l    o     

Por ende, lo que recibe el puntero es simplemente la dirección base donde se aloje la cadena. En este caso, el puntero a guarda la dirección 0x20, ya que a partir de esta dirección podemos acceder a los demás caracteres.

Ejemplo:

char *a;
a = "Hello";
//A partir de la dirección 0x20, podemos acceder a la dirección 0x21 donde está guardado la letra e
printf("%cn", *(a + 1));

Ahora si llegáramos hacer esto:

char* a = 'x';
printf("Code ASCII: %dn", a);

Por supuesto que no es correcto, porque el puntero a solo puede almacenar direcciones de memoria y el caracter 'x' en realidad es un entero, es decir, ¡le estás asignando al puntero un entero!

Esto provocará un fallo de segmentación si intentara desreferenciar el puntero:

char* a = 'x';
*a = 's'; //error en runtime.
printf("Code ASCII: %dn", a);

Y sabéis porque? Porque el puntero a no apunta a una dirección que si le pertenezca al programa, por lo tanto, estaríamos accediendo a una dirección que no es accesible.

Sabiendo todo esto, ahora si respondamos tu pregunta:

¿Por qué se puede asignar directamente una cadena de caracteres a un apuntador y en cambio, no una constante de caracter?

En el primero se puede porque lo que realmente estás asignando en el puntero es una dirección de memoria y en el segundo no, es un simple entero. Es más, el compilador como mínimo debería lanzar una advertencia.

Correct answer by MrDave1999 on August 27, 2021

La respuesta dada por MrDave1999 es correcta y explica perfectamente la diferencia entre asignar una cadena literal como "esta" o una constante de tipo carácter como 'a'.

Quiero no obstante añadir algo, que creo que es la razón por la que una línea como esta:

char *t = "Hola";

resulta tan chocante a quienes programan en C y quieren entender cómo funcionan realmente las cosas.

El dato clave que falta para tener el momento "¡Ajá!" en la cabeza es el siguiente:

Toda cadena literal, entendiendo como tal toda secuencia de caracteres delimitada por comillas dobles en el código fuente, es en realidad para el compilador una expresión de tipo puntero a char

La razón es que tan pronto como el compilador detecta "Algo entre comillas", almacena ese texto en una zona aparte del ejecutable, denominada zona de las cadenas literales, y sustituye la expresión por la dirección dentro del fichero en la que lo ha guardado (esta dirección será modificada por el cargador al cargar el programa en memoria, de forma que se refiera a la dirección de memoria donde el literal haya sido cargado)

Y lo importante es que esto no es un "caso especial" para la asignación de punteros a char, sino que ocurre en cualquier cadena literal que pueda aparecer en el código. Es por tanto un comportamiento consistente que ayuda a comprender muchas cosas.

Por ejemplo, considera el siguiente programa:

int main() {
  printf("Hola ");
  printf("mundon");
}

Según el compilador va procesando el fuente, se encuentra dos cadenas literales: "Hola " y "mundon". Las "extrae" y las deja en la zona de cadenas literales, que supongamos que comienza en un offset 0x0100 dentro del fichero y que contendría entonces lo siguiente:

0x0100 Hola_␀
0x0106 mundo␤␀

(he representado con un _ el espacio que hay tras "Hola", con el retorno de carro que resulta de n y con el ASCII nulo (0) que el C coloca al final de toda cadena como terminador). Como se ve el primer literal ocupa 6 bytes (contando al terminador ) por lo que el siguiente literal comienza en el offset 0x0106.

Por tanto una vez el compilador "sabe" dónde ha guardado esas cadenas, sustituye en el fuente los literales por las direcciones donde han sido guardadas. Es decir, el código se convierte a ojos del compilador en este otro:

int main() {
  printf(0x0100);
  printf(0x0106);
}

Cuando el programa, una vez compilado a código máquina y almacenado en un ejecutable en disco, sea cargado en memoria, el cargador del operativo decidirá en que zona de memoria cargarlo, y como consecuencia reescribirá los punteros para que apunten a la dirección donde finalmente esten esas cadenas literales.

Es decir, y por resumir:

Una cadena literal es en el fondo una dirección de memoria: la dirección donde esa cadena ha sido almacenada por el compilador+cargador.

Así ya no es de extrañar que puedas hacer una asignación como:

char *t = "Hola";

pues estás asignando a un puntero (t) una dirección de memoria (la del literal) y no hay contradicción alguna.

De este modo tampoco es de extrañar que a todas las funciones de la biblioteca estándar C que esperan un parámetro de tipo char *, les puedas pasar un literal, como por ejemplo:

strcmp(t, "mundo");

La función strcmp() (comparar cadenas) espera dos punteros como parámetro. El primero t está claro. El segundo "mundo", ahora también debería estar claro. Es una cadena literal y por tanto una dirección de memoria.

Answered by abulafia on August 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP