Declaración e inicialización de puntero de doble carácter C

6 minutos de lectura

Siempre pensé que declarar

char *c = "line";

era lo mismo que

char c[] = "line";

y así lo hice

char **choices = { "New Game", "Continue Game", "Exit" };

Lo que me da un tipo de puntero incompatible, donde

char *choices[] = { "New Game", "Continue Game", "Exit" };

no. ¿Alguna ayuda para entender esto?

  • Matrices C != Punteros C.

    – nmichaels

    28 de enero de 2011 a las 19:12

  • En el Ejemplo 1, c es un puntero a un carácter. En el ejemplo 2, c es una matriz de caracteres. En el Ejemplo 3, las opciones son un puntero a un puntero a un carácter (y se le asigna incorrectamente una matriz de caracteres). En el Ejemplo 4, las opciones son una matriz de punteros a los caracteres (y se le asigna correctamente una matriz de caracteres).

    – osterwal

    28 de enero de 2011 a las 20:58

  • @oosterwal: para ex3 y ex4 dijiste “… asignó una matriz de caracteres”, pero lo correcto es “… asignó una matriz de punteros a los caracteres”. Para el ex3 – existe la posibilidad de asignar un matriz de punteros a caracteres usando literales compuestos.

    – Martín Babacaev

    28 de enero de 2011 a las 21:22


  • @Martin Babacaev (y cualquier otra persona interesada): Hace unos años encontré un artículo sobre la regla de la espiral en el sentido de las agujas del reloj para averiguar qué significan algunas de esas declaraciones extrañas. Lo recomiendo encarecidamente a cualquiera que tenga problemas con esto: c-faq.com/decl/spiral.anderson.html

    – osterwal

    31 de enero de 2011 a las 13:38


  • @oosterwal: gracias. me gusta este unixwiz.net/techtips/reading-cdecl.html. también explica declaraciones abstractas p.ej int (*(*)())()

    – Martín Babacaev

    31 de enero de 2011 a las 14:36

avatar de usuario
zwol

char *c = "line";

es no lo mismo que

char c[] = "line";

es realmente lo mismo que

static const char hidden_C0[] = "line";
char *c = (char *)hidden_C0;

salvo que la variable hidden_C0 no es directamente accesible. Pero lo verá si descarga el lenguaje ensamblador generado (generalmente tendrá un nombre que no es un identificador C válido, como .LC0). Y en su ejemplo de matriz de constantes de cadena, sucede lo mismo:

char *choices[] = { "New Game", "Continue Game", "Exit" };

se convierte

const char hidden_C0[] = "New Game";
const char hidden_C1[] = "Continue Game";
const char hidden_C2[] = "Exit";

char *choices[] = { (char *)hidden_C0, (char *)hidden_C1, (char *)hidden_C2 };

Ahora, este es un comportamiento de caso especial que está disponible solamente para constantes de cadena. No puedes escribir

int *numbers = { 1, 2, 3 };

debes escribir

int numbers[] = { 1, 2, 3 };

y por eso no puedes escribir

char **choices = { "a", "b", "c" };

o.

(Su confusión es un caso especial del concepto erróneo común de que las matrices son “lo mismo que” los punteros en C. No lo son. Las matrices son matrices. Las variables con tipos de matriz sufren tipo de descomposición a un tipo de puntero cuando están usó (en casi todos los contextos), pero no cuando se definen).

Bueno, no son lo mismo. Es más fácil para la mayoría de las personas pensar que son iguales, por lo que todos comienzan a pensar de esa manera hasta que se encuentran con un problema como el anterior 🙂

Iba a escribir algo largo y sin aliento, pero luego pensé… Alguien más debe haber hecho esto ya. Y tienen. Esta es una muy buena explicación:

http://www.lysator.liu.se/c/c-faq/c-2.html

La forma más fácil de pensarlo es que cuando haces algo como:

  char *foo = "something";

Realmente estás haciendo algo como:

  char randomblob[] = "something";
  char *foo = randomblob;

Ahora… esa no es realmente una imagen precisa (aunque no soy un experto en compilación). Al menos te permite pensar en las cosas de una manera un poco más correcta.

Entonces, volviendo a tu problema, si yo entiende las cosas bien (lo que nunca está garantizado), no puede hacer su línea de ejemplo n. ° 3 en C. Tiene razón en que alguien podría escribir un compilador que haría lo correcto aquí, pero gcc no lo hace. El cuarto ejemplo, sin embargo, hace “lo correcto” y le brinda “una matriz de punteros que apuntan a una matriz de caracteres const”.

Una vez encontré una página web que traducía un tipo C complejo al inglés. Sin embargo, eso fue probablemente a principios de los 90, pero apuesto a que si buscas en Google lo suficiente, obtendrás una descripción de redacción más precisa que la que acabo de preparar.

  • Muchas gracias por el enlace, siempre pensé que los arreglos hacían lo mismo que los punteros. Supongo que nunca leí mucho sobre arreglos. Para mí siempre fue algo que apuntaba a la primera cajita de la memoria y iba desde allí hasta obtener un ‘\0’, por lo que tener una declaración u otra parecía lo mismo. Las otras respuestas también fueron buenas, pero tu enlace me aclaró las cosas.

    – vascop

    28 de enero de 2011 a las 19:31

  • por este y otros errores como este C es un cuchillo afilado

    – Mandrágora

    28 de enero de 2011 a las 23:51

avatar de usuario
Martín Babacaev

esta bien solo escribe

char **choices = (char *[]){ "New Game", "Continue Game", "Exit" };

Sin embargo, choices solo se puede utilizar para direccionamiento lineal. Por ejemplo:

printf ("%s", &(*choices)[0]); salidas: New Game
printf ("%s", &(*choices)[1]); salidas: ew Game
printf ("%s", &(*choices)[9]); salidas: Continue Game

Así que no es una broma, es una inicialización válida. Sólo otro tipo de uso.


También puedes encontrar un ejemplo muy cercano aquíexplicando Literales compuestos noción.

  • estoy a favor literales compuestos pero o esta respuesta es una broma o me estoy perdiendo por completo por qué harías tal cosa.

    – SiegeX

    28 de enero de 2011 a las 19:27

  • Esta expresión se compila sin errores ni advertencias en GCC4.2. Esta pregunta se trata de un problema de compilación, no de uso.

    – Martín Babacaev

    28 de enero de 2011 a las 19:29


  • @SiegeX: actualicé mi respuesta con ejemplos de cómo se puede usar esto

    – Martín Babacaev

    28 de enero de 2011 a las 20:01

  • Debería haber sido más específico con mi comentario. ¿Por qué no declararía simplemente ‘opciones’ como char *choices[] = { "New Game", "Continue Game", "Exit" }; ?

    – SiegeX

    29 de enero de 2011 a las 4:10

Estándar C en línea (borrador n1256):

6.7.8 Inicialización


11 El inicializador para un escalar será un expresión única, opcionalmente entre llaves. El valor inicial del objeto es el de la expresión (después de la conversión); se aplican las mismas restricciones de tipo y conversiones que para la asignación simple, tomando el tipo del escalar como la versión no calificada de su tipo declarado.

16 De lo contrario, el inicializador de un objeto que tiene tipo agregado o unión será un lista de inicializadores entre llaves para los elementos o miembros nombrados.

Énfasis añadido.

char ** es un tipo escalar, no un agregado, por lo que no es compatible con el inicializador {"New Game", "Continue Game", "Exit"}. Por el contrario, char *[] es un tipo agregado (matriz).

Del mismo modo, no podrías escribir algo como

int *foo = {1, 2, 3};

porque int * no es un tipo de matriz.

tu comprensión de

char *c = "line";

y

char c[] = "line";

está ligeramente apagado; están no lo mismo. El primer formulario copia el Dirección del literal de cadena al valor del puntero c. El segundo formulario copia el contenido de la expresión de matriz "line" al búfer designado por c.

¿Ha sido útil esta solución?