¿Cuál es el punto de malloc (0)?


Respuestas:


121

Según las especificaciones, malloc (0) devolverá "un puntero nulo o un puntero único que se puede pasar con éxito a free ()".

Básicamente, esto le permite no asignar nada, pero aún así pasar la variable "artista" a una llamada a free () sin preocupaciones. A efectos prácticos, es prácticamente lo mismo que hacer:

artist = NULL;

51
Personalmente, creo que configurar NULL es una mejor estrategia multiplataforma, ya que free () está garantizado (por especificación) para funcionar bien en NULL como entrada.
Reed Copsey

2
Como mencionó C. Ross, algunas plataformas, técnicamente, podrían devolver un puntero aquí (es decir, un "puntero único que se puede pasar a libre"), pero si lo está tratando como un char *, eso puede darle un Carácter inválido, no terminado. Podría ser peligroso confiar en esto en situaciones multiplataforma.
Reed Copsey

9
Realmente desearía que las especificaciones también dijeran "pasó de forma segura a reasignar"
hanshenrik

1
@NSAddict "estructura vacía donde sizeof devolverá 0", proporcione un ejemplo, suena como una extensión de idioma.
chux - Restablecer a Monica

1
@hanshenrik ¿Quién dice que no puedes? realloc()le permite pasar cualquier puntero válido devuelto por malloc(). Debería ser suficiente.
glglgl

51

El estándar C (C17 7.22.3 / 1) dice:

Si el tamaño del espacio solicitado es cero, el comportamiento está definido por la implementación: o se devuelve un puntero nulo o el comportamiento es como si el tamaño fuera un valor distinto de cero, excepto que el puntero devuelto no se utilizará para acceder a un objeto.

Por lo tanto, malloc(0)podría devolver NULLo un puntero válido que no puede ser desreferenciado . En cualquier caso, es perfectamente válido invocarlo free().

Realmente no creo que malloc(0)tenga mucho uso, excepto en los casos en que malloc(n)se llama en un bucle, por ejemplo, y npodría ser cero.

Mirando el código en el enlace, creo que el autor tenía dos conceptos erróneos:

  • malloc(0)devuelve un puntero válido siempre , y
  • free(0) es malo.

Entonces, se aseguró de que artisty otras variables siempre tuvieran algún valor "válido" en ellas. El comentario dice tanto: // these must always point at malloc'd data.


10
El hecho de que sea dependiente de la implementación lo hace más o menos completamente inútil: esta es una de las partes más cutres del estándar C, y algunos miembros del comité de estándares (por ejemplo, PJ Plauger) se han quejado al respecto.

10
Estoy de acuerdo. Si se malloc(0)devuelve un puntero válido, malloc()devolver NULLsignifica "falla" siempre, y 0ya no es un caso especial, que es más consistente.
Alok Singhal

1
Dado que las circunstancias de mallocfalla para obtener memoria están definidas por la implementación, una implementación podría simplemente definir que las asignaciones de tamaño 0 son siempre insatisfactorias ( ENOMEM), y ahora malloc(0)devolver 0 (con errno==ENOMEM) es consistente. :-)
R .. GitHub DEJA DE AYUDAR A ICE

6
¿Puede reallocdevolver un puntero malloc(0)? ¿Puede usted realloc((char*)NULL)?
Braden Best

3
@Braden Best Sí a ambos.
chux - Reincorporar a Monica el

11

El comportamiento de malloc (0) es específico de la implementación. La biblioteca puede devolver NULL o tener el comportamiento malloc normal, sin memoria asignada. Haga lo que haga, debe estar documentado en alguna parte.

Por lo general, devuelve un puntero que es válido y único, pero NO debe desreferenciarse. También tenga en cuenta que PUEDE consumir memoria a pesar de que en realidad no asignó nada.

Es posible reasignar un puntero malloc (0) no nulo.

Sin embargo, tener un malloc (0) literalmente no es de mucha utilidad. Se usa principalmente cuando una asignación dinámica es de cero bytes y no le importó validarla.


9
malloc()debe mantener "información de mantenimiento" en algún lugar (este tamaño del bloque asignado, por ejemplo, y otros datos auxiliares). Entonces, si malloc(0)no regresa NULL, usará memoria para almacenar esa información, y si no free()d, constituirá una pérdida de memoria.
Alok Singhal

Las implementaciones de Malloc realizan el mantenimiento de registros, lo que podría agregar una cierta cantidad de datos por puntero devuelto además del tamaño solicitado.
user7116

1
La memoria consumida y la memoria asignada no significan lo mismo. En este mismo caso, la mayoría de las implementaciones devolverán un puntero único. Esto significa que una parte del espacio de direcciones debe sacrificarse por ese puntero. Dependiendo del asignador, esto podría significar que asignará 1 byte o más.
Coincoin

1
La biblioteca puede hacer lo que quiera ; bueno, puede devolver un puntero único que ningún otro malloc()devolverá o devolverá NULL.
Alok Singhal

1
@jldupont: al menos la biblioteca Microsoft C Run-Time devuelve un puntero único para malloc(0). Sin embargo, en la misma implementación de la biblioteca C estándar, realloc(ptr, 0)libera ptry devuelve NULL.
Medinoc

5

Hay una respuesta en otra parte de esta página que comienza "malloc (0) devolverá una dirección de memoria válida y cuyo rango dependerá del tipo de puntero al que se le está asignando memoria". Esta declaración es incorrecta (no tengo suficiente reputación para comentar esa respuesta directamente, por lo que no puedo poner este comentario directamente debajo).

Hacer malloc (0) no asignará automáticamente la memoria del tamaño correcto. La función malloc no sabe a qué está enviando su resultado. La función malloc se basa puramente en el número de tamaño que le da como argumento. Debe hacer malloc (sizeof (int)) para obtener suficiente almacenamiento para contener un int, por ejemplo, no 0.


4

malloc(0)no tiene ningún sentido para mí, a menos que el código se base en un comportamiento específico de la implementación. Si el código está destinado a ser portátil, entonces debe tener en cuenta el hecho de que un retorno NULL de malloc(0)no es un error. Entonces, ¿por qué no simplemente asignar NULL a de artisttodos modos, ya que ese es un resultado exitoso válido y es menos código, y no hará que sus programadores de mantenimiento se tomen tiempo para resolverlo?

malloc(SOME_CONSTANT_THAT_MIGHT_BE_ZERO)o malloc(some_variable_which_might_be_zero)tal vez podría tener sus usos, aunque nuevamente debe tener especial cuidado de no tratar una devolución NULL como un error si el valor es 0, pero se supone que un tamaño 0 está bien.


3

Hay muchas respuestas a medias verdaderas por aquí, así que aquí están los hechos concretos. La página de manual de malloc()dice:

Si el tamaño es 0, entonces malloc () devuelve NULL o un valor de puntero único que luego se puede pasar con éxito a free ().

Eso significa que no hay absolutamente ninguna garantía de que el resultado de malloc(0)sea ​​único o no NULL. La única garantía la proporciona la definición de free(), nuevamente, esto es lo que dice la página de manual:

Si ptr es NULL, no se realiza ninguna operación.

Entonces, cualquier cosa que malloc(0)regrese, se puede pasar con seguridad free(). Pero también puede hacerlo un NULLpuntero.

En consecuencia, escribir no artist = malloc(0); es mejor que escribir artist = NULL;


1
Lástima que la implementación no pueda devolver un puntero no nulo ni único. De esa manera, malloc(0)podría devolver, digamos, 0x1, y free()podría tener una verificación de caso especial de 0x1 tal como lo ha hecho para 0x0.
Todd Lehman

3
@Todd Lehman Una implementación puede hacer lo que sugiere. La especificación C no especifica que el resultado debe ser " NULLo un puntero único". en su lugar, "un puntero nulo o un puntero al espacio asignado". No hay un requisito único . OTOH, devolver un valor especial no único puede interrumpir el código que cuenta con valores únicos. Quizás una pregunta de caso de esquina para SO.
chux - Reincorporación a Monica

mantambién puede documentar la forma definida por la implementación utilizada en * nix. En este caso no es así, pero todavía no es una fuente canónica para el general C.
Lundin

@Lundin True. Pero las páginas de manual son mucho más accesibles que el estándar C, y las páginas de manual de los sistemas GNU / Linux generalmente documentan bastante bien qué estándar (s) sigue la implementación. Junto con información sobre qué partes se adhieren a qué estándar, en caso de que difieran. Tengo la sensación de que ambos quieren ser precisos y anunciar cada bit que es una extensión GNU ...
cmaster - reinstalar monica

3

Por qué no deberías hacer esto ...

Dado que el valor de retorno de malloc depende de la implementación, puede obtener un puntero NULL o alguna otra dirección. Esto puede terminar creando desbordamientos de búfer de pila si el código de manejo de errores no verifica tanto el tamaño como el valor devuelto, lo que genera problemas de estabilidad (fallas) o incluso peores problemas de seguridad.

Considere este ejemplo, donde el acceso adicional a la memoria a través de la dirección devuelta dañará el montón si el tamaño es cero y la implementación devuelve un valor no NULO.

size_t size;

/* Initialize size, possibly by user-controlled input */

int *list = (int *)malloc(size);
if (list == NULL) {
  /* Handle allocation error */
}
else {
  /* Continue processing list */
}

Vea esta página de codificación segura de los estándares de codificación CERT, donde tomé el ejemplo anterior para leer más.



2

Es cierto que nunca había visto esto antes, esta es la primera vez que veo esta sintaxis, se podría decir, un caso clásico de exceso de funciones. Junto con la respuesta de Reed, me gustaría señalar que hay algo similar, que parece una función sobrecargada realloc:

  • foo no es NULL y tamaño es cero, realloc(foo, size);. Cuando pasas un puntero no NULL y un tamaño de cero para reasignar, la reasignación se comporta como si hubieras llamado a free (…)
  • foo es NULL y el tamaño es distinto de cero y mayor que 1 realloc(foo, size);,. Cuando pasa un puntero NULL y el tamaño no es cero, realloc se comporta como si hubiera llamado malloc (…)

Espero que esto ayude, Saludos cordiales, Tom.


1

Para responder realmente a la pregunta hecha: no hay razón para hacer eso


0

malloc (0) devolverá NULL o un puntero válido que se puede pasar correctamente a free. Y aunque parece que la memoria a la que apunta es inútil o no se puede escribir o leer, eso no siempre es cierto. :)

int *i = malloc(0);
*i = 100;
printf("%d", *i);

Esperamos una falla de segmentación aquí, pero sorprendentemente, ¡imprime 100! Es porque malloc realmente pide una gran cantidad de memoria cuando llamamos a malloc por primera vez. Cada llamada a malloc después de eso, usa la memoria de ese gran trozo. Solo después de que termina esa gran parte, se solicita una nueva memoria.

Uso de malloc (0): si se encuentra en una situación en la que desea que las llamadas a malloc posteriores sean más rápidas, llamar a malloc (0) debería hacerlo por usted (excepto en los casos extremos).


1
Es posible que escribir en *ino se bloquee en su caso, pero de todos modos es un comportamiento indefinido. ¡Cuidado con los demonios nasales!
John Dvorak

1
Si. Eso es verdad. Es específico de la implementación. Lo he verificado en MaxOS X y alguna distribución de Linux. No lo he probado en otras plataformas. Dicho esto, el concepto que he descrito ha sido descrito en el libro "El lenguaje de programación C" de Brain Kernighan y Dennis Ritchie.
Sagar Bhosale

Lo sé: comentario super tardío sobre esta pregunta. Pero a veces hay un uso malloc(0)que no se menciona. En aquellas implementaciones en las que devuelve un valor no NULO, especialmente en una compilación DEBUG, es probable que asigne MÁS de lo que solicitó y le da el puntero para pasar su encabezado interno. Esto le permite tener una idea del uso real de la memoria si lo obtiene antes y después de una serie de asignaciones. por ejemplo: void* before = malloc(0); ... void* after = malloc(0); long long total = after - before;o algo así.
Jesse Chisholm

Leí "El lenguaje de programación C" de Brain Kernighan y Dennis Ritchie y no recuerdo que dijera nada al respecto malloc(0). ¿Podría decirnos a qué capítulo se refiere también? Proporcionar una cotización exacta también sería bueno.
Андрей Беньковский

0

En Windows:

  • void *p = malloc(0);asignará un búfer de longitud cero en el montón local. El puntero devuelto es un puntero de montón válido.
  • mallocen última instancia, llama HeapAllocutilizando el montón de tiempo de ejecución de C predeterminado que luego llama RtlAllocateHeap, etc.
  • free(p);utiliza HeapFreepara liberar el búfer de longitud 0 en el montón. No liberarlo resultaría en una pérdida de memoria.

0

En realidad, es bastante útil y (obviamente en mi humilde opinión), el comportamiento permitido de devolver un puntero NULL está roto. Un puntero dinámico es útil no solo por lo que apunta, sino también por el hecho de que su dirección es única. Devolver NULL elimina esa segunda propiedad. Todos los programas mallocs I incrustados (de hecho con bastante frecuencia) tienen este comportamiento.



-2

Aquí está el análisis después de ejecutar con la herramienta de verificación de memoria valgrind.

==16740== Command: ./malloc0
==16740==
p1 = 0x5204040
==16740==
==16740== HEAP SUMMARY:
==16740==     in use at exit: 0 bytes in 0 blocks
==16740==   total heap usage: 2 allocs, 2 frees, 1,024 bytes allocated
==16740==
==16740== All heap blocks were freed -- no leaks are possible

y aquí está mi código de muestra:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
   //int i;
   char *p1;

   p1 = (char *)malloc(0);
   printf("p1 = %p\n", p1);

   free(p1);

   return 0;

}

De forma predeterminada, se asignan 1024 bytes. Si aumento el tamaño de malloc, los bytes asignados aumentarán en 1025 y así sucesivamente.


-3

De acuerdo con la respuesta de Reed Copsey y la página de manual de malloc, escribí algunos ejemplos para probar. Y descubrí que malloc (0) siempre le dará un valor único. Mira mi ejemplo:

char *ptr;
if( (ptr = (char *) malloc(0)) == NULL )
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

La salida será "Tiene un puntero válido", lo que significa que ptrno es nulo.


-6

malloc(0)devolverá una dirección de memoria válida y cuyo rango dependerá del tipo de puntero al que se le está asignando memoria. También puede asignar valores al área de memoria, pero esto debe estar dentro del rango del tipo de puntero que se esté utilizando. También puede liberar la memoria asignada. Explicaré esto con un ejemplo:

int *p=NULL;
p=(int *)malloc(0);
free(p);

El código anterior funcionará bien en un gcccompilador en una máquina Linux. Si tiene un compilador de 32 bits, puede proporcionar valores en el rango de enteros, es decir, -2147483648 a 2147483647. Lo mismo se aplica a los caracteres también. Tenga en cuenta que si se cambia el tipo de puntero declarado, el rango de valores cambiará independientemente del mallocencasillado, es decir

unsigned char *p=NULL;
p =(char *)malloc(0);
free(p);

p tomará un valor de 0 a 255 de char ya que se declara un int sin signo.


5
Krellan tiene razón al señalar que esta respuesta es incorrecta: malloc()no sabe nada sobre el elenco (que en realidad es completamente superfluente en C). Desreferenciar el valor de retorno de malloc(0)invocará un comportamiento indefinido.
cmaster - reinstalar a monica

-6

Solo para corregir una falsa impresión aquí:

artist = (char *) malloc(0);nunca jamás volverá NULL; no es lo mismo que artist = NULL;. Escribir un programa simple y comparar artistcon NULL. if (artist == NULL)es falso y if (artist)es verdadero.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.