malloc para estructura y puntero en C

84

Supongamos que quiero definir una estructura que represente la longitud del vector y sus valores como:

Ahora, suponga que quiero definir un vector y y asignarle memoria.

Mi búsqueda en Internet muestra que debería asignar la memoria para x por separado.

Pero, parece que estoy asignando memoria para y-> x dos veces, una mientras asigna memoria para y y la otra mientras asigna memoria para y-> x, y parece una pérdida de memoria. Le agradecería mucho que me hiciera saber qué hace realmente el compilador y cuál sería la forma correcta de inicializar tanto y como y-> x.

Gracias por adelantado.

Pouya
fuente
5
Como lo señaló eminentemente paxdiablo, por favor no ingrese el valor de retorno de malloc()en C. Nunca entenderé por qué todos sienten la necesidad de hacerlo. :(
relájese
14
@unwind, tal vez sean viejos programadores de C ++ actualizando a C :-)
paxdiablo
@unwind Cuando utilizo el compilador nvcc de Nvidia en código C, si no lanzo el resultado de malloc, arroja un error.
Nubcake
@Nubcake De acuerdo con este enlace, eso podría deberse a que nvcc ejecuta el compilador subyacente en modo C ++, debido a que su interfaz CUDA es C ++. En C, no obtendrá errores para esto. En C ++ void *no se convierte automáticamente a otros punteros, y la conversión es necesaria (o simplemente no se usa malloc()en C ++, por supuesto).
relajarse el
@unwind Sí, más tarde me enteré de esto :) Solo quería indicar una situación en la que si no arrojabas el resultado, arrojaría un error.
Nubcake

Respuestas:

156

No, estás no la asignación de memoria para y->xdos veces.

En cambio, está asignando memoria para la estructura (que incluye un puntero) más algo para que apunte ese puntero.

Piénsalo de esta manera:

Entonces, realmente necesita las dos asignaciones ( 1y 2) para almacenar todo.

Además, su tipo debe ser, struct Vector *yya que es un puntero, y nunca debe convertir el valor de retorno mallocen C, ya que puede ocultar ciertos problemas que no desea ocultar: C es perfectamente capaz de convertir implícitamente el void*valor de retorno a cualquier otro puntero.

Y, por supuesto, probablemente desee encapsular la creación de estos vectores para facilitar su gestión, como con:

Al encapsular la creación de esa manera, se asegura de que los vectores estén completamente construidos o no estén construidos en absoluto; no hay posibilidad de que estén a medio construir. También le permite cambiar totalmente las estructuras de datos subyacentes en el futuro sin afectar a los clientes (por ejemplo, si desea hacer arreglos dispersos para intercambiar espacio por velocidad).

paxdiablo
fuente
28
Tiembla ante mi habilidad artística en ASCII :-)
paxdiablo
Muchas gracias. De mucha ayuda.
Pouya
1
En if (retval == NULL), retval debe ser retVal
Legion Daeth
5

La primera vez, se puede asignar memoria para Vector, lo que significa que las variables x, n.

Sin embargo x , todavía no apunta a nada útil .

Por eso también se necesita una segunda asignación .

Karthik T
fuente
3

Pocos puntos

struct Vector y = (struct Vector*)malloc(sizeof(struct Vector)); Está Mal

debería ser struct Vector *y = (struct Vector*)malloc(sizeof(struct Vector));ya que ysostiene el puntero a struct Vector.

1st malloc()solo asigna memoria suficiente para contener la estructura vectorial (que es puntero a double + int)

En segundo lugar, malloc()asigna memoria para contener 10 dobles.

Rajneesh
fuente
2

De hecho, podría hacer esto en un solo malloc asignando el Vector y la matriz al mismo tiempo. P.ej:

Esto asigna Vector 'y', luego hace que y-> x apunte a los datos extra asignados inmediatamente después de la estructura Vector (pero en el mismo bloque de memoria).

Si se requiere cambiar el tamaño del vector, debe hacerlo con las dos asignaciones como se recomienda. La matriz interna y-> x podría cambiarse de tamaño mientras se mantiene intacta la estructura vectorial 'y'.

PQuinn
fuente
¿Por qué escribiste cast y to char específicamente? ¿Por qué no (double *) y + sizeof (struct Vector)?
Vishnu Prasath
sizeof devuelve el tamaño de la estructura en bytes, y el operador aritmético de puntero '+' agregará al puntero 'y' es múltiplo del tamaño de ( y). Si hiciéramos lo que hiciste anteriormente, y aumentaría en sizeof (doble) * sizeof (estructura), que es demasiado. La conversión de y a char nos permite incrementar y por sizeof (char) * sizeof (struct) = 1 * sizeof (struct)
PQuinn
2

En principio, ya lo estás haciendo bien. Para lo que quieres necesitas dos malloc()s.

Solo algunos comentarios:

debiera ser

En la primera línea, asigna memoria para un objeto Vector. malloc()devuelve un puntero a la memoria asignada, por lo que y debe ser un puntero vectorial. En la segunda línea, asigna memoria para una matriz de 10 dobles.

En C no necesita las conversiones explícitas, y escribir en sizeof *ylugar de sizeof(struct Vector)es mejor para la seguridad de tipos y, además, ahorra en escribir.

Puede reorganizar su estructura y hacer una sola malloc()así:

Wernsey
fuente
"ahorra escribiendo" nunca es un argumento válido para las decisiones de programación. La verdadera razón por la que tomaría * y es por razones de seguridad, asegurándose de asignar tanto espacio como sea necesario para la variable correspondiente.
Lundin
@Lundin He actualizado mi respuesta, pero para mí el argumento de "seguridad de tipos" está casi en la misma liga que la escrituraif(NULL == foo)
Wernsey
Usted es el que debe usar la predicación sizeof *y. Si lo hace solo por escribir 5 letras menos (tamaño de * y versus tamaño de (Vector)) sin otro argumento, entonces debe ser porque encuentra que la tarea de escribir 5 letras en su teclado es un gran obstáculo. Y si es así, entonces quizás considere otra opción de carrera, ya que la programación implica una gran cantidad de mecanografía en el teclado ...
Lundin
@Lundin Writing sizeof *yte ayuda a combatir errores como escribir sizeof(Vector)cuando quisiste sizeof(Matrix). ¿Con qué frecuencia comete errores como esos? ¿Qué tan rápido los encuentra y los arregla cuando lo hace? Estoy de acuerdo en que aumenta la seguridad de los tipos y escribo sizeof *yen mi propio código, pero es casi tan paranoico como escribir if(NULL == foo)para evitar errores tipográficos en el ==.
Wernsey
1
@ShmuelKamensky Son funcionalmente equivalentes. yes un puntero a struct Vectorso sizeof *yes decir "tamaño de lo que apunta y", entonces sizeof struct Vector.
Wernsey
1

Cuando asigna memoria struct Vector, simplemente asigna memoria para el puntero x, es decir, para el espacio, donde se colocará su valor, que contiene la dirección. Entonces, de esa manera, no asigna memoria para el bloque, al que y.xhará referencia.

Andremoniy
fuente
1

Primero malloc asigna memoria para la estructura, incluida la memoria para x (puntero a doble). El segundo malloc asigna memoria para el valor doble al que apunta x.

oleg_g
fuente
0

Cuando malloc(sizeof(struct_name))asigna memoria automáticamente para el tamaño completo de la estructura, no es necesario que mallocice cada elemento dentro.

Utilice la -fsanitize=addressbandera para comprobar cómo utilizó la memoria de su programa.

Asg constante
fuente
Incorrecto. x es solo un puntero, debe asignar memoria para el valor al que apunta x. Lea otra respuesta para obtener más detalles. (ej .: stackoverflow.com/a/14768280/5441253 )
Maxime Ashurov