Programación en C: malloc() dentro de otra función

9 minutos de lectura

avatar de usuario
HaggarTheHorrible

necesito ayuda con malloc() dentro de otra función.

estoy pasando un puntero y Talla a la función de mi main() y me gustaría asignar memoria para ese puntero dinámicamente usando malloc() desde dentro de esa función llamada, pero lo que veo es que… la memoria, que se está asignando, es para el puntero declarado dentro de mi función llamada y no para el puntero que está dentro de la main().

¿Cómo debo pasar un puntero a una función y asignar memoria para el puntero pasado? desde dentro de la función llamada?


He escrito el siguiente código y obtengo el resultado como se muestra a continuación.

FUENTE:

int main()
{
   unsigned char *input_image;
   unsigned int bmp_image_size = 262144;

   if(alloc_pixels(input_image, bmp_image_size)==NULL)
     printf("\nPoint2: Memory allocated: %d bytes",_msize(input_image));
   else
     printf("\nPoint3: Memory not allocated");     
   return 0;
}

signed char alloc_pixels(unsigned char *ptr, unsigned int size)
{
    signed char status = NO_ERROR;
    ptr = NULL;

    ptr = (unsigned char*)malloc(size);

    if(ptr== NULL)
    {
        status = ERROR;
        free(ptr);
        printf("\nERROR: Memory allocation did not complete successfully!");
    }

    printf("\nPoint1: Memory allocated: %d bytes",_msize(ptr));

    return status;
}

SALIDA DEL PROGRAMA:

Point1: Memory allocated ptr: 262144 bytes
Point2: Memory allocated input_image: 0 bytes

avatar de usuario
jamesdlin

¿Cómo debo pasar un puntero a una función y asignar memoria para el puntero pasado desde dentro de la función llamada?

Pregúntese esto: si tuviera que escribir una función que tuviera que devolver un int¿como lo harias?

O lo devolverías directamente:

int foo(void)
{
    return 42;
}

o devolverlo a través de un parámetro de salida agregando un nivel de indirección (es decir, usando un int* en vez de int):

void foo(int* out)
{
    assert(out != NULL);
    *out = 42;
}

Entonces, cuando devuelve un tipo de puntero (T*), es lo mismo: devuelves el tipo de puntero directamente:

T* foo(void)
{
    T* p = malloc(...);
    return p;
}

o agrega un nivel de direccionamiento indirecto:

void foo(T** out)
{
    assert(out != NULL);
    *out = malloc(...);
}

  • Gracias por dar la explicación que estaba demasiado apurado para dar.

    – Mark Ransom

    15 de mayo de 2010 a las 1:44

  • +1, me encanta todo menos la afirmación, sin embargo, está bien para una demostración tan simple.

    – Tim Publicar

    15 de mayo de 2010 a las 6:47

  • Me gusta la afirmación; es una parte del contrato para la función que la persona que llama debe obtener sistemáticamente correcta. Por supuesto, incluso un código más sutil podría hacer un NULL out permisible, haciéndolo corresponder a un parámetro de salida opcional. Pero eso no es lo que se necesita para alloc_pixels; la pregunta no requiere tal sofisticación.

    – Becarios Donal

    15 de mayo de 2010 a las 6:56

  • ¿Es seguro liberar (* salir) dentro de la función de llamada (principal en este caso)?

    – William Everett

    9 de enero de 2014 a las 16:51

  • @Pinyaka: Es seguro que llame la persona que llama free() en el puntero resultante (¿de qué otra forma liberaría la persona que llama la memoria asignada?). Sin embargo, la persona que llama estaría haciendo T* out = foo(); (en la primera forma) o T* out; foo(&out); (en la segunda forma). En ambos casos, la persona que llama tendría que llamar free(out)no free(*out).

    – jamesdlin

    9 de enero de 2014 a las 17:29

avatar de usuario
Canción WANG

Como se mencionó en las otras respuestas, necesitamos un puntero al puntero. ¿Pero por qué?

Necesitamos pasar el valor por un puntero para poder modificar el valor. Si desea modificar un inttienes que pasarlo por el int*.

En esta pregunta, el valor que queremos modificar es un puntero. int* (puntero cambiado de NULL a la dirección de la memoria asignada), por lo que necesitamos pasar un puntero al puntero int**.

Haciendo seguido, pInt en el interior foo(int*) es una copia del argumento. Cuando asignamos memoria a la variable local, la que está en el main() está intacto.

void foo(int* pInt)
{
   pInt = malloc(...);
}
int main()
{
   int* pInt;
   foo(pInt);
   return 0;
}

Así que necesitamos un puntero a puntero,

void foo(int** pInt)
{
   *pInt = malloc(...);
}
int main()
{
   int* pInt;
   foo(&pInt);
   return 0;
}

avatar de usuario
marca rescate

Debe pasar un puntero a un puntero como parámetro de su función.

int main()
{
   unsigned char *input_image;
   unsigned int bmp_image_size = 262144;

   if(alloc_pixels(&input_image, bmp_image_size) == NO_ERROR)
     printf("\nPoint2: Memory allocated: %d bytes",_msize(input_image));
   else
     printf("\nPoint3: Memory not allocated");     
   return 0;
}

signed char alloc_pixels(unsigned char **ptr, unsigned int size) 
{ 
    signed char status = NO_ERROR; 
    *ptr = NULL; 

    *ptr = (unsigned char*)malloc(size); 

    if(*ptr== NULL) 
    {
        status = ERROR; 
        free(*ptr);      /* this line is completely redundant */
        printf("\nERROR: Memory allocation did not complete successfully!"); 
    } 

    printf("\nPoint1: Memory allocated: %d bytes",_msize(*ptr)); 

    return status; 
} 

  • ¿¡Por qué estás llamando gratis en un bloque de código condicional que tiene garantizado un puntero NULL!?!? El free(*ptr) será cuando se le llame desde main() tratar de liberar input_image que era ummm, el término se me escapa… no asignado dinámicamente.

    –James Morris

    14 mayo 2010 a las 22:45


  • y @James: Hice lo que me sugirieron Mark y Matti, pero esta vez tanto mi _mize(input_image) en mi main() como _msize(**ptr) en mi función alloc_pixels(…) devuelven el tamaño como 0 Mientras que si es _msize(*ptr) (single *) devuelve 262144. ?

    – Haggar El Horrible

    14 mayo 2010 a las 23:09


  • @James Morris, simplemente copié el código que se publicó en la pregunta e hice la cantidad mínima de cambios. No quería quedar atrapado en una distracción del punto principal.

    – Mark Ransom

    15 de mayo de 2010 a las 1:50

  • @vikramtheone, lo siento, estaba un poco apurado y no hice esta respuesta tan completa como debería haber sido. Lo he editado para que sea más completo. Espero que pueda ver en qué se diferencia de su código original y por qué debe ser así.

    – Mark Ransom

    15 de mayo de 2010 a las 2:01


  • Probé lo mismo en MSVS, pero no funcionó. input_image sigue siendo ‘malo puntero’. ¿Cuál podría ser la razón?

    – Zeeshan

    7 de abril de 2016 a las 9:26

avatar de usuario
t0mm13b

Tienes que pasar el puntero por referenciano por copiael parámetro en la función alloc_pixels requiere el ampersand & para devolver la dirección del puntero, es decir llamar por referencia en C habla.

main()
{
   unsigned char *input_image;
   unsigned int bmp_image_size = 262144;

   if(alloc_pixels(&input_image, bmp_image_size)==NULL)
     printf("\nPoint2: Memory allocated: %d bytes",_msize(input_image));
   else
     printf("\nPoint3: Memory not allocated");     

}

signed char alloc_pixels(unsigned char **ptr, unsigned int size)
{
    signed char status = NO_ERROR;
    *ptr = NULL;

    *ptr = (unsigned char*)malloc(size);

    if((*ptr) == NULL)
    {
        status = ERROR;
        /* free(ptr);
        printf("\nERROR: Memory allocation did not complete successfully!"); */
    }

    printf("\nPoint1: Memory allocated: %d bytes",_msize(*ptr));

    return status;
}

He comentado las dos líneas. free(ptr) y “ERROR: …” dentro del alloc_pixels funciona como eso es confuso. No tienes que free un puntero si falla la asignación de memoria.

Editar: Después de mirar el MSDN enlace proporcionado por OP, una sugerencia, el ejemplo de código es el mismo que antes en mi respuesta … pero … cambie el especificador de formato a %u Para el size_t tipo, en el printf(...) llamar main().

main()
{
   unsigned char *input_image;
   unsigned int bmp_image_size = 262144;

   if(alloc_pixels(&input_image, bmp_image_size)==NULL)
     printf("\nPoint2: Memory allocated: %u bytes",_msize(input_image));
   else
     printf("\nPoint3: Memory not allocated");     

}

avatar de usuario
Matti Virkkunen

Si desea que su función modifique el puntero en sí, deberá pasarlo como un puntero a un puntero. He aquí un ejemplo simplificado:

void allocate_memory(char **ptr, size_t size) {
    void *memory = malloc(size);
    if (memory == NULL) {
        // ...error handling (btw, there's no need to call free() on a null pointer. It doesn't do anything.)
    }

    *ptr = (char *)memory;
}

int main() {
   char *data;
   allocate_memory(&data, 16);
}

  • es seguro llamar free() en un puntero nulo, ¿de qué se trata ese comentario?

    –Carl Norum

    14 mayo 2010 a las 22:37

  • @Carl Norum: Es seguro, pero inútil. En mi opinión, el código que no hace nada solo genera confusión para las personas que terminan leyéndolo más tarde y debe evitarse.

    –Matti Virkkunen

    14 mayo 2010 a las 22:38


  • @Matti Virkkunen: No tiene sentido decirle a la gente que no llame gratis con un puntero NULL y información errónea: está causando que las personas se confundan cuando ven un código que va en contra de su consejo.

    –James Morris

    14 mayo 2010 a las 22:43


  • @James Morris: Bien, bien… ¿te gusta más la redacción ahora?

    –Matti Virkkunen

    14 mayo 2010 a las 22:49

  • @Carl: He encontrado bibliotecas C (no muy agradables) que fallaban si se les pedía free(NULL); así que es bueno evitarlo de todos modos. (No, no recuerdo cuál. Fue hace bastante tiempo).

    – Becarios Donal

    14 mayo 2010 a las 22:55

La única forma en que podría hacer que el puntero a una solución de puntero funcione para un problema similar que estaba teniendo para esta función

    BOOL OpenBitmap2 ( LPCTSTR pszFileName, char** pszBMPFile)  

Fue mediante la asignación de un puntero temporal para almacenar la dirección

    char* BMPFile;
    { BMPFile = (char*)GlobalAlloc(GPTR, dwFileSize + 1);   // allocate storage using GlobalAlloc + 1 for null term string

luego reasignarlo

    {* pszBMPFile = BMPFile; return (0);} // Error = False

Cualquier comentario sobre por qué usar “* pszBMPFile” directamente con GlobalAlloc no funcionó sería apreciado. Respondí mi propia pregunta. Olvidé llevar el “*” con pszBMPFile en las otras líneas de código. Buenas lecciones de todos los colaboradores. Muchas gracias.

  • es seguro llamar free() en un puntero nulo, ¿de qué se trata ese comentario?

    –Carl Norum

    14 mayo 2010 a las 22:37

  • @Carl Norum: Es seguro, pero inútil. En mi opinión, el código que no hace nada solo genera confusión para las personas que terminan leyéndolo más tarde y debe evitarse.

    –Matti Virkkunen

    14 mayo 2010 a las 22:38


  • @Matti Virkkunen: No tiene sentido decirle a la gente que no llame gratis con un puntero NULL y información errónea: está causando que las personas se confundan cuando ven un código que va en contra de su consejo.

    –James Morris

    14 mayo 2010 a las 22:43


  • @James Morris: Bien, bien… ¿te gusta más la redacción ahora?

    –Matti Virkkunen

    14 mayo 2010 a las 22:49

  • @Carl: He encontrado bibliotecas C (no muy agradables) que fallaban si se les pedía free(NULL); así que es bueno evitarlo de todos modos. (No, no recuerdo cuál. Fue hace bastante tiempo).

    – Becarios Donal

    14 mayo 2010 a las 22:55

Esto no tiene sentido :

if(alloc_pixels(input_image, bmp_image_size)==NULL) 

alloc_pixels devuelve un signed char (ERROR o NO_ERROR) y lo comparas con NULL (que se supone que se usa para punteros).

Si quieres input_image para ser cambiado, necesita pasar un puntero a él para alloc_pixels.
alloc_pixels la firma seria la siguiente:

signed char alloc_pixels(unsigned char **ptr, unsigned int size)

Lo llamarías así:

alloc_pixels(&input_image, bmp_image_size);

Y la asignación de memoria

*ptr = malloc(size);

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad