2011-10-27 20 views
12

OK, tengo problemas para entender los punteros a los punteros frente a los punteros a las matrices. Considere el siguiente código:¿Por qué un puntero a un puntero es incompatible con un puntero a una matriz?

char s[] = "Hello, World"; 
char (*p1)[] = &s; 
char **p2 = &s; 
printf("%c\n", **p1); /* Works */ 
printf("%c\n", **p2); /* Segmentation fault */ 

¿Por qué la primera obra printf, mientras que el segundo no lo hace?

Por lo que entiendo, 's' es un puntero al primer elemento de la matriz (es decir, 'H'). Así que declarar p2 como char ** significa que es un puntero a un puntero a un char. Hacer que apunte a 's' debería ser legal, ya que 's' es un puntero a un char. Y, por lo tanto, desreferenciarlo (es decir, ** p2) debería dar 'H'. ¡Pero no es así!

+1

Ninguna de las asignaciones de compilar en VC++ 2010. – Jon

+0

Extraño. Funciona bien en GCC 4.4.4. – Meta

+0

@Meta: No en GCC 4.3.4 ([demo] (http://ideone.com/K6D1D)) o 4.5.1 ([demo] (http://ideone.com/gTGhY)) ... – ildjarn

Respuesta

11

Su malentendido yace en lo que s es. Es no un puntero: es una matriz.

Ahora en la mayoría de los contextos, s se evalúa como un puntero al primer elemento de la matriz: equivalente a &s[0], un puntero a ese 'H'. Sin embargo, lo importante aquí es que el valor del puntero que obtienes al evaluar s es un valor temporal, efímero, al igual que &s[0].

Como ese puntero no es un objeto permanente (no es lo que realmente está almacenado en s), no puede hacer que un puntero a puntero apunte a él. Para utilizar un puntero a puntero, debe tener un objeto real de puntero para apuntar a - por ejemplo, la siguiente es OK:

char *p = s; 
char **p2 = &p; 

Si se evalúa *p2, le dicen al compilador para cargar la cosa que p2 apunta y lo trata como un puntero a char. Eso está bien cuando p2 apunta realmente a un puntero a char; pero cuando lo hace char **p2 = &s;, lo que apunta a p2 no es un puntero en absoluto: es una matriz (en este caso, es un bloque de 13 char s).

+0

Ah bien. Creo que estoy empezando a entender ahora. ¿Puedes aclarar la parte sobre que s es un "valor efímero temporal"? ¿No sería la misma dirección cada vez? – Meta

+1

@Meta: Quiero decir que el puntero que 's' evalúa no es un objeto direccionable, de la misma manera que' a + 1' no (en standardese, no es un valor l). – caf

+0

@caf, si 's' no es un objeto direccionable, ¿cómo es que' & s' no es un error de compilación (pero '& (a + 1)' es)? – Shahbaz

1

From what I understand, 's' is a pointer to the first element of the array
No, s es una matriz. Se puede reducir a un puntero a una matriz, pero hasta ese momento, es una matriz. Un puntero a una matriz se convierte en un puntero al primer elemento de la matriz. (Sí, es un poco confuso.)

char (*p1)[] = &s; Esto está permitido, es un puntero a una matriz, se le ha asignado la dirección de una matriz. Señala el primer elemento de s.

char **p2 = &s;
Eso hace un puntero a un puntero y le asigna la dirección de la matriz. Le asigna un puntero al primer elemento de s (a char), cuando cree que es un puntero a un puntero a uno o más caracteres. Desreferenciar esto es un comportamiento indefinido. (Segfault en su caso)

La prueba de que son diferentes mentiras en sizeof(char[1000]) (tamaño retornos de 1000 caracteres, no el tamaño de un puntero) y funciones de la siguiente manera:

template<int length> 
void function(char (&arr)[length]) {} 

que recopilará cuando dado un conjunto, pero no un puntero.

1

Aquí está el ejemplo que funciona, además de las impresiones de direcciones de puntero Para simplificar las cosas para ver:

#include <stdio.h> 
char s[] = "Hello, World"; 
char (*p1)[] = &s; 
char *p2 = (char*)&s; 

int main(void) 
{ 
    printf("%x %x %x\n", s, p2, *p2); 
    printf("%x\n", &s); // Note that `s` and `&s` give the same value 
    printf("%x\n", &s[0]); 
    printf("%c\n", **p1); 
    printf("%c\n", *p2); 
} 
+0

Agregué una línea a su código que lo hace un poco más claro por qué sucede esto. – Shahbaz

Cuestiones relacionadas