2010-01-25 22 views
52

¿Qué devuelve malloc(0)? ¿La respuesta sería la misma para realloc(malloc(0),0)?¿Qué devuelve malloc (0)?

#include<stdio.h> 
#include<malloc.h> 
int main() 
{ 
     printf("%p\n", malloc(0)); 
     printf("%p\n", realloc(malloc(0), 0)); 
     return 0; 
} 

salida de gcc linux:

[email protected]:~$ gcc -Wall mal.c 
[email protected]:~$ ./a.out 
0x9363008 
(nil) 
[email protected]:~$ 

La salida van cambiando cada vez para malloc(0). ¿Es esta una respuesta estándar? ¿Y por qué alguien estaría interesado en obtener ese puntero, además de la investigación académica?

EDIT:

If malloc(0) vuelve puntero ficticio, entonces, ¿cómo siguientes obras:

int main() 
{ 
    void *ptr = malloc(0); 
    printf("%p\n", realloc(ptr, 1024)); 
    return 0; 
} 

EDIT:

Las siguientes salidas de código "posible" para cada iteración. ¿Por qué no debería fallar?

#include<stdio.h> 
#include<malloc.h> 
int main() 
{ 

     int i; 
     void *ptr; 
     printf("Testing using BRUTE FORCE\n"); 
     for (i=0; i<65000; i++) 
     { 
       ptr = malloc(0); 
       if (ptr == realloc(ptr, 1024)) 
         printf("Iteration %d: possible\n", i); 
       else 
       { 
         printf("Failed for iteration %d\n", i); 
         break; 
       } 
     } 
     return 0; 
} 
+0

@ tommieb75: es útil haber respondido la pregunta original. Una vez dicho esto, esta pregunta tiene dos partes, y la segunda parte es más interesante y no es un engaño. Ver mi respuesta a esta pregunta para más detalles. –

Respuesta

43

Otros han respondido que malloc(0) funciona. Responderé a una de las preguntas que usted solicitó y que aún no se contestaron (creo). La pregunta es sobre realloc(malloc(0), 0):

¿Qué devuelve malloc(0)? ¿La respuesta sería la misma para realloc(malloc(0),0)?

La norma dice esto acerca de realloc(ptr, size):

  • si ptr es NULL, se comporta como malloc(size),
  • lo contrario (ptr no es NULL), se desasigna el puntero del objeto de edad a por ptr y devuelve un puntero a un nuevo búfer asignado. Pero si size es 0, C89 dice que el efecto es equivalente a free(ptr). Curiosamente, no puedo encontrar esa afirmación en el borrador C99 (n1256 o n1336). En C89, el único valor razonable para devolver en ese caso sería NULL.

Por lo tanto, hay dos casos:

  • malloc(0) vuelve NULL en una implementación. Entonces su llamada realloc() es equivalente a realloc(NULL, 0).Eso es equivalente a malloc(0) desde arriba (y eso es NULL en este caso).
  • malloc(0) devuelve no NULL. Entonces, la llamada es equivalente a free(malloc(0)). En este caso, malloc(0) y realloc(malloc(0), 0) son no equivalente.

en cuenta que hay un caso interesante aquí: en el segundo caso, cuando malloc(0) devuelve no- NULL en caso de éxito, todavía puede volver NULL para indicar el fracaso. Esto dará como resultado una llamada como: realloc(NULL, 0), que sería equivalente a malloc(0), que puede o no devolver NULL.

no estoy seguro de si la omisión en el C99 es un descuido o si significa que en C99, realloc(ptr, 0) para no NULLptr no es equivalente a free(ptr). Acabo de probar esto con gcc -std=c99, y lo anterior es equivalente a free(ptr).

Edición: Creo que entiendo lo que su confusión es:

Veamos un fragmento de su código de ejemplo:

ptr = malloc(0); 
if (ptr == realloc(ptr, 1024)) 

Lo anterior no es lo mismo que malloc(0) == realloc(malloc(0), 1024). En el segundo, la llamada malloc() se realiza dos veces, mientras que en el primero, está pasando un puntero previamente asignado al realloc().

Analicemos primero el primer código. Suponiendo que malloc(0) no devuelve NULL en caso de éxito, ptr tiene un valor válido. Cuando lo hace realloc(ptr, 1024), realloc() básicamente le da un nuevo búfer que tiene el tamaño 1024, y el ptr deja de ser válido. Una implementación conforme puede devolver la misma dirección que la que ya está en ptr. Por lo tanto, su condición if puede regresar verdadera. (Tenga en cuenta, sin embargo, mirando el valor de ptr después de realloc(ptr, 1024) puede ser un comportamiento indefinido.)

Ahora la pregunta que usted hace: malloc(0) == realloc(malloc(0), 1024). En este caso, supongamos que tanto el malloc(0) en el LHS y RHS devuelve no NULL. Entonces, están garantizados para ser diferentes. Además, el valor devuelto desde malloc() en el LHS no ha sido free() d todavía, por lo que cualquier otro malloc(), calloc() o realloc() no puede devolver ese valor. Esto significa que si usted escribió su condición:

if (malloc(0) == realloc(malloc(0), 1024) 
    puts("possible"); 

no verá possible en la salida (a menos que ambos malloc() y realloc() fallar y devolver NULL).

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

int main(void) 
{ 
    void *p1; 
    void *p2; 

    p1 = malloc(0); 
    p2 = realloc(p1, 1024); 
    if (p1 == p2) 
     puts("possible, OK"); 

    /* Ignore the memory leaks */ 
    if (malloc(0) == realloc(malloc(0), 1024)) 
     puts("shouldn't happen, something is wrong"); 
    return 0; 
} 

En OS X, mi código no generaba nada cuando lo ejecutaba. En Linux, imprime possible, OK.

+0

@Alok: Buen trabajo. Comprobé la condición en Linux si (malloc (0) == realloc (malloc (0), 1024). ¡Está fallando cada vez! –

+0

Cuando dices "fallar", te refieres a que el programa de arriba muestra "no debería suceder, algo es incorrecto "? –

+0

" El estándar dice esto sobre realloc (ptr, size): si ptr es NULL, se comporta como malloc (tamaño), de lo contrario (ptr no es NULL), desasigna el antiguo puntero a ptr y lo devuelve un puntero a un nuevo búfer asignado. "Siento que debería mencionar que' realloc' solo realiza un 'free (ptr)' si el área de memoria debe moverse, y si id no lo hizo, no se realiza la desasignación, simplemente un expansión. Al menos eso es lo que entiendo de la página 'hombre'. "Si el área señalada se movió, se hace un (ptr) libre." –

15

En C89, malloc (0) es dependiente de la implementación - No sé si C99 se ha fijado o no. En C++, utilizando:

char * p = new char[0]; 

está bien definido: obtiene un puntero válido, no nulo. Por supuesto, no puede usar el puntero para acceder a lo que apunta sin invocar un comportamiento indefinido.

En cuanto a por qué esto existe, es conveniente para algunos algoritmos, y significa que no necesita ensuciar su código con pruebas de valores cero.

+1

@Neil: 'malloc (0)' es el mismo en C99. –

+0

@Alok: eso es simplemente falso. C99 permite que 'malloc (0)' devuelva 0, y las buenas implementaciones lo hacen. –

+6

@R. Creo que el punto es que C99 no * requiere * implementaciones para devolver '0' o no' 'para' malloc (0) '. –

5

C99 estándar

Si el espacio no puede ser distribuido, se devuelve un NullPointer . Si el tamaño del espacio solicitado es cero, el comportamiento es definido por la implementación: se devuelve ya sea un puntero nulo, o el comportamiento es como si el tamaño eran algún valor distinto de cero, excepto que el regresado puntero no deberá ser utilizado para acceder a un objeto.

5

El comp.lang.c FAQ tiene the following decir:

El/ISO estándar ANSI dice que puede hacer cualquiera ; el comportamiento es definido en la implementación (vea la pregunta 11.33). El código portátil debe tener cuidado de no llamar a malloc (0), o ser preparado para la posibilidad de un retorno nulo.

Por lo tanto, probablemente sea mejor evitar el uso de malloc(0).

+1

Pero el mismo puntero devuelto por malloc (0), si no es NULO, puede ser utilizado por realloc() para señalar a una ubicación de memoria válida. Como realloc (malloc (0), 1024); –

+3

@Manav: en realidad también funciona con NULL, 'realloc (NULL, 1024)' es lo mismo que 'malloc (1024)' – Hasturkun

+0

¿Alguna vez malloc (0) y realloc (malloc (0), 1024) devuelven los mismos punteros? ?? if (malloc (0) == realloc (malloc (0), 1024) printf ("posible"); –

32

malloc(0) es Implementación Definido en lo que se refiere a C99.

De C99 [Sección 7.20.3]

El orden y la contigüidad de almacenamiento asignado por llamadas sucesivas a la calloc, malloc, y funciones REALLOC es no especi fi ed. El puntero devuelto si la asignación tiene éxito se alinea adecuadamente para que pueda asignarse a un puntero a cualquier tipo de objeto y luego se utiliza para acceder a dicho objeto o una matriz de tales objetos en el espacio asignado (hasta que el espacio explícitamente desasignado). La duración de un objeto asignado se extiende a desde la asignación hasta la desasignación. Cada asignación de este tipo arrojará un puntero a un objeto disjuntado de cualquier otro objeto. El puntero devolvió los puntos al inicio (dirección del octeto más bajo ) del espacio asignado. Si no se puede asignar el espacio, se devuelve un puntero nulo . Si el tamaño del espacio solicitado es cero, el comportamiento es implementación: definido: se devuelve un puntero nulo o el comportamiento es como si el tamaño fuera valor distinto de cero, excepto que el puntero devuelto no se debe usar para acceder a un objeto

+0

si se devuelve un puntero que no sea NULL, ¿de qué sirve? ¿Por qué implementarían dicho comportamiento? –

+1

no tiene mucho sentido devolver un puntero no nulo, pero también lo hace malloc (0); la especificación deja este abierto para que un compilador pueda elegir no manejar malloc (0) específicamente, de modo que todos los demás mallocs son un nanosegundo más rápido (porque se omite la verificación de 0) –

+0

Simplicidad. En algunos casos, puede simplificar la biblioteca de tiempo de ejecución subyacente si no tiene que tener un manejo especial de este caso especial :) :) – jalf

3

Ver C99, sección 7.20.3:

Si el tamaño del espacio solicitado es cero, el comportamiento es implementationdefined: o bien 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 .

Esto es válido para todas las tres funciones de asignación (es decir calloc(), malloc() y realloc()).

2

Creo que depende. he comprobado el Visual Studio 2005 fuentes y vi esto en la función _heap_alloc:

if (size == 0) 
    size = 1; 

creo que en muchos casos es posible que desee un puntero válido, incluso cuando pidiendo cero bytes. Esto se debe a que este comportamiento constante facilita la verificación de los punteros porque: si tiene un puntero no nulo, está bien; si tienes un puntero NULL, probablemente tengas un problema. Es por eso que creo que la mayoría de las implementaciones devolverán un puntero válido, incluso cuando piden cero bytes.

+0

A menudo he visto un ajuste general como 'size = (size + WORD_LEN)% WORD_LEN;' Esto mantiene los bloques en el montón alineados con palabras , que a menudo mejora el tiempo de acceso (especialmente para transferencias en bloque). – TMN

+1

Los bloques en el montón serán más que alineados por palabra en la mayoría de los sistemas; típicamente, todos los bloques se alinearán en un múltiplo de 8 bytes; muchos se alinearán en un múltiplo de 16 bytes. El valor de retorno debe estar lo suficientemente alineado para cualquier uso. Para muchos sistemas, eso significa que si se usa para un doble, debe alinearse en un múltiplo de 8 bytes; para algunos sistemas, un doble largo puede ser de 16 bytes, y así sucesivamente. Y a pesar de que los chips Intel pueden manejar datos desalineados, hay una penalización en el rendimiento al hacerlo que malloc() et al deben evitar (y lo hacen). –

0

Si malloc (0) devuelve puntero del maniquí, entonces, ¿cómo siguientes obras:

void *ptr = malloc(0);

printf("%p\n", realloc(ptr, 1024));

no sé qué quiere decir con "puntero ficticio" . Si malloc(0) devuelve un valor no nulo, entonces ptr es un puntero válido a un bloque de memoria de tamaño cero. La implementación malloc guarda esta información de una manera específica de la implementación.realloc conoce la forma (específica de la implementación) de averiguar que ptr apunta a un bloque de memoria de tamaño cero.

(Cómo malloc/realloc/free hacer esto es específico de la implementación. Una posibilidad es asignar 4 bytes más solicitado y almacenar el tamaño justo antes del bloque de memoria. En ese caso, ((int *)ptr)[-1] daría el tamaño del bloque de memoria, que es 0. Nunca debe hacer esto desde su código, es solo para uso por realloc y free).

+0

@ user9876: "Si malloc (0) devuelve no NULL, entonces ptr es un puntero válido para un bloque de memoria de tamaño cero." ... ¿Cómo se asigna un bloque de memoria válido de tamaño 'cero' como dijiste en tu publicación? –

+0

¿Cómo lo hace su aplicación de usuario? Llamas a malloc (0) (si estás en un sistema donde eso no es NULL). ¿Cómo implementa el sistema eso? Eso es un poco como preguntar cómo puede tener un archivo en el disco con un tamaño de cero. Sigue siendo un archivo válido en el disco, con todos los metadatos habituales (entradas de directorio, etc.), simplemente tiene un tamaño de cero. – user9876

3

Un punto que nadie se ha preocupado de hablar todavía, en su primer programa es que realloc con longitud 0 es lo mismo que free.

de la página man Solaris:

La función realloc() cambia el tamaño del bloque señaló a por ptr a size bytes y devuelve un puntero al bloque (posiblemente desplazado). El contenido no se modificará hasta el menor que el tamaño nuevo o antiguo. Si ptr es NULL, realloc() se comporta como malloc() para el tamaño especificado. Si size es 0 y ptr no es un puntero nulo, el espacio apuntado se hace disponible para su asignación adicional por la aplicación, aunque no se devuelve al sistema. La memoria se devuelve al sistema solo al finalizar la aplicación.

Si uno no sabe que puede ser una fuente de mala sorpresa (me pasó a mí).

+0

Es curioso, mencioné esto en la pregunta duplicada aquí ... http://stackoverflow.com/questions/2022335/whats-the-point-in-malloc0/2133585#2133585 – t0mm13b

0

Hemos implementado estructuras malloc para código incrustado con un encabezado (y un remolque opcional). El encabezado puede contener información de depuración adicional, como el identificador de tarea que lo asignó.Además, me gusta tener banderas/límites como 0xA5A5A5A5 y 0x5A5A5A5A para ayudar a detectar si alguien en algún lugar ha sobreescrito los límites de su (s) asignación (es) de memoria. Al mantener listas de bloques libres y usados, también se puede verificar periódicamente la integridad del montón y evitar operaciones (como libre() de memoria no asignada) que podrían hacer que las cosas "exploten".

Cuestiones relacionadas