2009-06-19 11 views
6
char **Data[70]={NULL}; 

¿Cuál es la terminología correcta para esto? ¿De qué otra manera podría escribirse? ¿Cómo se ve en la memoria? Estoy leyendo muchos tutoriales sobre punteros, pero no lo veo en esta sintaxis. Cualquier ayuda es apreciada. Gracias.¿Cómo funciona una matriz de punteros a punteros?

Respuesta

24

Esta estructura

char **Data[70]={NULL}; 

es una matriz de 70 punteros a punteros a char. El compilador asigna 70 * sizeof(char**) bytes para esta matriz, que suponiendo punteros de 32 bits es de 280 bytes.

Si piensa internamente en un "puntero a char" como una cadena, que no es cierto pero está lo suficientemente cerca, esta es una matriz de 70 punteros a cadenas. Para hacer un poco de arte ASCII y pretender que ha asignado y lleno de algunos valores ....

Array of  One or more 
char **   char * 
+---------+  +---------+ 
| 0 | --> | ptr | --> "Hello, world" 
+---------+  +---------+ 
| 1 | 
+---------+  +---------+ 
| 2 | ----> | ptr2 | --> "Goodbye, cruel world" 
+---------+  +---------+ 
| 3 | 
+---------+   +---------+ 
| 4 | ------> | ptr3[0] | --> "Message 0" 
+---------+   +---------+ 
    ...    | ptr3[1] | --> "Message 1" 
+---------+   +---------+ 
| 69 |   | ptr3[2] | --> "Message 2" 
+---------+   +---------+ 

que podría hacer lo anterior con código como este (comprobación de valores de retorno de error malloc omitidos):

char **Data[70]={NULL}; 
char **ptr, **ptr2, **ptr3; 

ptr = (char **) malloc(sizeof(char *)); 
*ptr = "Hello, world"; 
Data[0] = ptr; 

ptr2 = (char **) malloc(sizeof(char *)); 
*ptr2 = "Goodbye, cruel world"; 
Data[2] = ptr2; 

ptr3 = (char **) malloc(10 * sizeof(char *)); 
Data[4] = ptr3; 

ptr3[0] = "Message 0"; 
ptr3[1] = "Message 1"; 
... 
ptr3[9] = "Message 9"; 

printf("%s\n", *Data[0]); 
printf("%s\n", Data[2][0]); 
printf("%s\n", Data[4][0]); 
printf("%s\n", Data[4][1]); 
     ... 
printf("%s\n", Data[4][9]); 

Piénselo de esta manera: cada entrada en la matriz es char **. Cada entrada puede apuntar a una ubicación arbitraria en la memoria, siendo dicha ubicación char * y así poder apuntar a una matriz de caracteres terminada en nulo, también conocida como "cadena".

Nota cuidadosamente la distinción entre esto y lo que se obtiene cuando se asigna una matriz 2D:

char *Data2[10][70]={NULL}; 

La asignación de Data2 anterior le da una matriz de 2 dimensiones de char * punteros, siendo dicha 2-d matriz asignado en un solo trozo de memoria (10 * 70 * sizeof(char*) bytes, o 2800 bytes con punteros de 32 bits). No tiene la capacidad de asignar los punteros char ** a ubicaciones arbitrarias en la memoria que tenga con la matriz unidimensional de char ** punteros.

También tenga en cuenta (dada anteriormente declaraciones de Data y Data2) que el compilador generará código diferente para las siguientes referencias array:

Data[0][0] 
Data2[0][0] 

Aquí hay otra manera de pensar acerca de esta: Imagine que tiene varias arrays de punteros a cadenas:

char *table0[] = { "Tree", "Bench", "Stream" }; 
char *table1[] = { "Cow", "Dog", "Cat" }; 
char *table2[] = { "Banana", "Carrot", "Broccoli" }; 
char **Data[3]; 

Data[0] = table0; 
Data[1] = table1; 
Data[2] = table2; 

tiene una matriz de punteros a "conjunto de puntero a char". Si ahora imprime el valor de data[1][1], piénselo de esta manera: le muestra un puntero al arreglo table1. Entonces el valor table1[1] es igual a "Dog".

+1

su gran respuesta ha sido desvergonzadamente vinculada a esta pet-peeve-answer: http://stackoverflow.com/questions/423823/whats-your-favorite-programmer-ignorance-pet-peeve/484900#484900. aplausos :) –

+0

gracias por tomarse el tiempo para escribir una buena respuesta. Eso aclara mucho. En cuanto a su primer diagrama, ¿cómo sabe si el puntero apunta a un solo puntero (como ptr2) o una matriz de punteros, como ptr3 [0-2] ¿Puede ser ambos como en el diagrama? Por ejemplo, tu segundo diagrama es consistente. Gracias. –

+0

No hay forma de saber a qué apunta exactamente un puntero char **. Podría apuntar a un área de memoria de cualquier tamaño. Sin mirar el código donde está asignado, no hay forma de saberlo. – Eddie

0

Esto es, de hecho, un puntero a un puntero a punteros. Sin embargo, dado que un "puntero" no es más que un lugar en la memoria, realmente no sirve de mucho hacer esto simplemente haciendo Char * Data [70], aparte de hacer obvio que cada char * es un puntero a otro char *, en lugar de un puntero a char.

1

Esto no es muy obvia:

char **Data[70]={NULL}; 

pero con una declaración alternativa, como:

char* Data[2][3] = { 
    {"Nick", "Tom", "Helen"}, 
    {"one", "two", "three"} 
}; 

podemos ver fácilmente que es una matriz de 2 dimensiones de cadenas.

Editar: He utilizado los datos [2] [3] para mostrar que se trata de una matriz 2D. Utilicé tamaño fijo para las dimensiones como 2 & 3 solo para demostración. Por supuesto que podemos tener:

char* Data[][3]={ 
    {"Nick", "Tom", "Helen"}, 
    {"one", "two", "three"}, 
    // ... 
}; 

o char** Data[]

Ok, esto es lo que quiero decir con 2-D matriz:

char** Data[2]={0}; 

void test() 
{ 
    char* d1[] = {"1", "2"}; 
    char* d2[] = {"10", "20", "30"}; 
    Data[0] = d1; 
    Data[1] = d2; 

    printf("%s\n", Data[0][0]); 
    printf("%s\n", Data[0][1]); 
    printf("%s\n", Data[1][0]); 
    printf("%s\n", Data[1][1]); 
    printf("%s\n", Data[1][2]); 
} 
+1

No es en realidad una matriz de cadenas 2D. Tu declaración te da algo diferente a su declaración. – Eddie

+0

¿Entonces no es una matriz 2d, de punteros? –

+0

¿Cuál es la diferencia? –

2

Es un poco difícil pensar en un uso práctico para una array de char **. Especialmente uno con 70 elementos.

Sin embargo, supongamos que voy a ejecutar 70 programas. Como probablemente sepa, los argumentos del programa comúnmente se pasan como un parámetro char** argv a main() (o char*[] argv, que en una firma de función es la misma cosa). Entonces, si quería almacenar los punteros argv para todos esos programas, había usado una matriz como Data. Obviamente, también necesitaría más memoria en otro lugar, para las cadenas reales y las matrices argv que ocupar, pero es un comienzo.

La inicialización de una matriz con {NULL} establece todos sus elementos en NULL. Esta es una abreviatura útil: puede inicializar una matriz con {firstelement, secondelement, ...}, pero si no proporciona suficientes términos, todo el resto se trata como 0.

Al igual que cualquier otra matriz de punteros, inicializados con {NULO}, lo que se ve en la memoria es 70 punteros NULL sentados en una fila. Por lo general, no hay diferencia en la memoria entre char** y cualquier otro puntero de objeto. Creo que es legal escribir una implementación extraña en la que hay una diferencia, pero no esperes para encontrar una.

Por lo tanto, la diferencia entre el 70 NULL char** en una fila y 70 NULL char* en una fila es lo que habría estar en el otro extremo del puntero, si no eran nulas. En el otro extremo de char** es un puntero a un char. En el otro extremo de char* es un char. O bien el puntero, o el carácter, podría ser el primero en una matriz, dependiendo de cómo se usa.

1

Lo que tienes es un conjunto de 70 punteros, cada uno de los cuales apunta a otro puntero, cada uno de esos punteros apuntan a un char. En una nota interesante, las matrices en sí son punteros, por lo que tiene tres niveles de punteros.

+0

Las matrices NO son punteros. –