2012-09-15 25 views
5

Tengo que usar el siguiente bloque de código para una tarea escolar, ESTRICTAMENTE SIN NINGUNA MODIFICACIÓN.matriz dinámica de punteros a estructuras

typedef struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
}* pStudentRecord; 

pStudentRecord* g_ppRecords; 
int g_numRecords =0; 

Aquí g_ppRecords se supone que es una matriz de punteros a las estructuras. Lo que estoy completamente no entender cómo es que la declaración puede pStudentRecords *g_ppRecords; significa g_ppRecords a ser una matriz debido a una serie debería definirse como

type arrayname[size]; 

He intentado asignar memoria dinámicamente a g_ppRecords, pero eso no está ayudando.

g_ppRecords = (pStudentRecord*) malloc(sizeof(pStudentRecord*)*(g_numRecords+1)); 
+0

Un puntero solo apunta a una dirección. Puede tener reservada la mayor cantidad de memoria comenzando en esa dirección como desee (hasta que se quede sin espacio). – chris

+0

Creo que es una pregunta pobre, entiendes que una matriz podría definirse de esa manera, ya que tratas de asignarle un espacio, entonces, ¿qué estás preguntando ?, no es suficiente asignar un espacio para el puntero a struct, que es btw pStudentRecord y no pSt ... ord *, para usarlo, ¡también necesita asignar un lugar para la estructura! – Michael

Respuesta

1

observar que pStudentRecord se typedef'd como un puntero a una estructura. Los punteros en C simplemente apuntan al inicio de un bloque de memoria, ya sea que ese bloque contenga 1 elemento (un puntero "escalar" normal) o 10 elementos (un puntero de "matriz"). Así, por ejemplo, la siguiente

char c = 'x'; 
char *pc = &c; 

hace pc apuntan a un trozo de memoria que comienza con el carácter 'x', mientras que el siguiente

char *s = "abcd"; 

hace s apuntan a un trozo de memoria que comienza con "abcd" (y seguido de un byte nulo). Los tipos son los mismos, pero pueden ser utilizados para diferentes propósitos.

Por lo tanto, una vez asignado, pude acceder a los elementos de g_ppRecords haciendo, p. Ej. g_ppRecords[1]->firstName.

Ahora, para asignar esta matriz: desea utilizar g_ppRecords = malloc(sizeof(pStudentRecord)*(g_numRecords+1)); (aunque tenga en cuenta que sizeof(pStudentRecord*) y sizeof(pStudentRecord) son iguales ya que ambos son tipos de puntero). Esto hace una matriz no inicializada de estructura punteros. Para cada puntero de estructura en la matriz, deberá asignarle un valor asignando una nueva estructura. El quid de la cuestión es cómo se puede asignar una sola estructura, es decir

g_ppRecords[1] = malloc(/* what goes here? */); 

Por suerte, en realidad se puede eliminar la referencia de punteros en sizeof:

g_ppRecords[1] = malloc(sizeof(*g_ppRecords[1])); 

Tenga en cuenta que sizeof es una construcción compilador. Incluso si g_ppRecords[1] no es un puntero válido, el tipo sigue siendo válido, por lo que el compilador calculará el tamaño correcto.

+0

g_ppRecords [1] = (pStudentRecord *) malloc (sizeof (char *) * 2 + sizeof (int) + sizeof (float)); ? –

+0

Se agregó una mejor solución. En realidad, no es una solución obvia, pensándolo bien. – nneonneo

+0

+1 por enseñarme un par de trucos nuevos ... –

0

Una matriz a menudo se denomina con un puntero a su primer elemento. Si malloc espacio suficiente para 10 registros de estudiantes y luego almacena un puntero al inicio de ese espacio en g_ppRecords, g_ppRecords [9] contará 9 registros de longitud de puntero hacia delante y eliminará la referencia de lo que hay allí. Si ha gestionado su espacio correctamente, ¿cuál será el último registro en su matriz, porque reservó suficiente espacio para 10.

En resumen, ha asignado el espacio, y puede tratarlo como desee si es la longitud correcta, incluso como una matriz.

No estoy seguro de por qué está asignando espacio para g_numRecords + 1 registros. A menos que g_numRecords tenga un nombre confuso, ese es espacio para uno más de lo que necesita.

-1

Aquí g_ppRecords se supone que es una matriz de punteros a las estructuras. Lo que estoy completamente fallando en entender es que ¿cómo puede la declaración * pStudentRecords g_ppRecords; significa que g_ppRecords es una matriz. como una matriz se debe definir como

escriba arrayname [size];

umm type arrayname[size]; es una manera de muchas maneras para definir una matriz en C.

esto define estáticamente una matriz, con la mayoría de los valores que se almacenan en la pila en función de la ubicación de la misma definición, el tamaño de la matriz debe conocerse en tiempo de compilación, aunque puede que este ya no sea el caso en algunos compiladores modernos.

Otra forma sería crear dinámicamente una matriz en tiempo de ejecución, por lo que no es necesario conocer el tamaño en tiempo de compilación, aquí es donde entran los punteros, son variables que almacenan la dirección de trozos de memoria asignados dinámicamente .

un ejemplo simple sería algo como esto type *array = malloc(sizeof(type) * number_of_items); malloc devuelve una dirección de memoria que está almacenada en array, tenga en cuenta que no encasillamos el tipo de devolución por razones de seguridad.

Volviendo al problema en cuestión.

typedef struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
}* pStudentRecord; 

pStudentRecord* g_ppRecords; 
int g_numRecords = 0; 

este typedef es un poco diferente de la mayoría nota de la }* básicamente es un puntero a una estructura por lo siguiente:

pStudentRecord* g_ppRecords; 

es en realidad:

struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
}** pStudentRecord; 

es un puntero a una puntero, en cuanto a por qué definirían el typedef de esta manera, está más allá de mí, y yo personalmente no lo recomiendo, ¿por qué?

bien, un problema sería ¿cómo podemos obtener el tamaño de la estructura a través de su nombre? simple, no podemos! si usamos sizeof(pStudentRecord) obtendremos 4 o 8 dependiendo de la arquitectura subyacente, porque eso es un puntero, sin conocer el tamaño de la estructura, no podemos asignarla dinámicamente usando su nombre typedef, entonces, ¿qué podemos hacer? declarar un segunda estructura como esta:

typedef struct 
{ 
    char* firstName; 
    char* lastName; 
    int id; 
    float mark; 
} StudentRecord; 

g_ppRecords = malloc(sizeof(StudentRecord) * g_numRecords); 

cualquier manera que realmente necesita para ponerse en contacto con la persona que creó el código original, o las personas que mantienen y plantear sus inquietudes.

g_ppRecords=(pStudentRecord) malloc((sizeof(char*) + 
            sizeof(char*) + 
            sizeof(int) + 
            sizeof(float)) *(g_numRecords+1)); 

esto puede parecer como una forma posible, por desgracia, hay no guarantees sobre estructuras, por lo que en realidad puede containg almohadilla entre los miembros por lo que el tamaño total de la estructura puede ser en realidad más grande que sus miembros combinados, no mencionar que su dirección probablemente difiera.

EDITAR

Al parecer, podemos obtener el tamaño de la estructura simplemente inferir su tipo

manera:

pStudentRecord g_ppRecords = malloc(sizeof(*g_ppRecords) * g_numRecords); 

funciona bien!

+0

para el cuidado del votante de abajo para elaborar? –

+0

Esa es una manera muy, muy mala de usar 'malloc' ya que codifica la definición de la estructura (e ignora el relleno, etc.). No estás en lo correcto al decir que no hay otra manera de hacerlo; ver mi solución El código tal como está escrito es perfectamente válido y utilizable, aunque algo inusual. – nneonneo

+0

Dije ** peor **, pero lo eliminaré. –

3

EDITAR: actualizó la sección "GRAN ERROR".

Una lección rápida sobre typedefs estilo C (diferente de C++!), Y por qué es cómo es y cómo usarlo.

En primer lugar, un truco tipo typedef básico.

typedef int* int_pointer; 
int_pointer ip1; 
int *ip2; 
int a; // Just a variable 
ip1 = &a; // Sets the pointer to a 
ip2 = &a; // Sets the pointer to a 
*ip1 = 4; // Sets a to 4 
*ip2 = 4; // Sets a to 4 

IP1 e IP2 son del mismo tipo: un tipo int-puntero a, a pesar de que usted no puso a * en la declaración de IP1. Eso * estaba en cambio en la declaración.

Cambiando de tema. Hablas de matrices se declaran como

int array1[4]; 

Para ello dinámicamente en tiempo de ejecución, es posible hacer:

int *array2 = malloc(sizeof(int) * 4); 
int a = 4; 
array1[0] = a; 
array2[0] = a; // The [] implicitly dereferences the pointer 

Ahora, lo que si queremos una matriz de punteros? Se vería así:

int *array1[4]; 
int a; 
array1[0] = &a; // Sets array[0] to point to variable a 
*array1[0] = 4; // Sets a to 4 

Asignemos esa matriz dinámicamente.

int **array2 = malloc(sizeof(int *) * 4); 
array2[0] = &a; // [] implicitly dereferences 
*array2[0] = 4; // Sets a to 4 

Observe la int **. Eso significa puntero-a puntero-a-int. Podemos, si elegimos, usar un puntero typedef.

typedef int* array_of_ints; 
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4); 
array3[0] = &a; // [] implicitly dereferences 
*array3[0] = 4; // Sets a to 4 

¿Ves que solo hay una * en esa última declaración? Eso es porque UNO de ellos está "en el typedef". Con esa última declaración, ahora tiene una matriz de tamaño 4 que consta de 4 punteros a ints (int *).

Es importante señalar PRECEDENCIA DEL OPERADOR aquí. El operador de desreferencia [] tiene preferencia sobre el * uno. Así que para ser absolutamente claro, lo que estamos haciendo es la siguiente:

*(array3[0]) = 4; 

Ahora, vamos a cambiar los temas de Estructuras y typedefs.

struct foo { int a; }; // Declares a struct named foo 
typedef struct { int a; } bar; // Typedefs an "ANONYMOUS STRUCTURE" referred to by 'bar' 

¿Por qué alguna vez escribirías una estructura anónima? Bueno, para la legibilidad!

struct foo a; // Declares a variable a of type struct foo 
bar b;  // Notice how you don't have to put 'struct' first 

Declarar una función ...

funca(struct foo* arg1, bar *arg2); 

Vea como no teníamos que poner 'struct' delante de arg2?

Ahora, vemos que el código tiene que usar define una estructura de esta manera:

typedef struct { } * foo_pointers; 

que es análogo a la forma en que hicimos una matriz de punteros antes:

typedef int* array_of_ints; 

Comparar uno al lado del otro

typedef struct { } * foo_pointers; 
typedef int* array_of_ints; 

La única diferencia es que uno es para una struct {} y la otra es para int.

Con nuestros foo_pointers, se puede declarar una matriz de punteros a foo como tal:

foo_pointers fooptrs[4]; 

Ahora tenemos una matriz que almacena 4 punteros a una estructura anónima que no puede tener acceso.

TOPIC SWITCH!

DESAFORTUNADAMENTE PARA USTED, su maestro cometió un error. Si uno mira el tamaño de() del tipo foo_pointers arriba, encontrará que devuelve el tamaño de un puntero a esa estructura, NO el tamaño de la estructura. Esto es 4 bytes para la plataforma de 32 bits u 8 bytes para la plataforma de 64 bits. Esto se debe a que tipificamos un PUNTERO A UNA ESTRUCTURA, no una estructura en sí misma. sizeof (pStudentRecord) devolverá 4.

¡De modo que no puede asignar espacio para las estructuras de manera obvia! Sin embargo, los compiladores permiten esta estupidez. pStudentRecord no es un nombre/tipo que se puede usar para asignar memoria de forma válida, es un puntero a una estructura "conceptual" anónima, pero podemos suministrarle el tamaño de eso al compilador.

pStudnetRecord g_ppRegistros [2]; pStudentRecord * record = malloc (sizeof (* g_ppRecords [1]));

Un mejor práctica es hacer esto:

typedef struct { ... } StudentRecord; // Struct 
typedef StudentRecord* pStudentRecord; // Pointer-to struct 

Ahora tendríamos la capacidad de hacer estructura de StudentRecord, así como punteros a ellos con pStudentRecord de, de una manera clara.

Aunque el método que está obligado a utilizar es una práctica muy mala, no es exactamente un problema en este momento. Volvamos a nuestro ejemplo simplificado usando ints.

¿Qué pasa si quiero hacer un typedef para complicar mi vida, pero explicar el concepto que está pasando aquí? Volvamos al antiguo código int.

typedef int* array_of_ints; 
int *array1[4]; 
int **array2 = malloc(sizeof(int *) * 4); // Equivalent-ish to the line above 
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4); 
int a, b, c, d; 
*array1[0] = &a; *array1[1] = &b; *array1[2] = &c; *array1[3] = &d; 
*array2[0] = &a; *array2[1] = &b; *array2[2] = &c; *array2[3] = &d; 
*array3[0] = &a; *array3[1] = &b; *array3[2] = &c; *array3[3] = &d; 

Como se puede ver, podemos usar esto con nuestra pStudentRecord:

pStudentRecord array1[4]; 
pStudentRecord *array2 = malloc(sizeof(pStudentRecord) * 4); 

poner todo junto, y se sigue lógicamente que:

array1[0]->firstName = "Christopher"; 
*array2[0]->firstName = "Christopher"; 

son equivalentes. (Nota: no haga exactamente lo mismo que hice anteriormente, asignar un puntero char * en el tiempo de ejecución a una cadena solo es correcto si sabe que ya tiene suficiente espacio asignado).

Esto solo trae un último bit.¿Qué hacemos con todo este recuerdo que malloc'd? ¿Cómo lo liberamos?

free(array1); 
free(array2); 

Y hay un final de una lección tarde-noche en punteros, typedefs de estructuras anónimas, y otras cosas.

+0

Gracias por su comentario. Eso me ayudó! – Rachael

Cuestiones relacionadas