¿Qué devuelve malloc(0)?
memcpy (10)
Si malloc (0) devuelve el puntero ficticio, ¿cómo funciona el siguiente?
void *ptr = malloc(0);
printf("%p/n", realloc(ptr, 1024));
No sé a qué te refieres con "puntero ficticio". Si malloc(0)
devuelve no NULL, entonces ptr
es un puntero válido a un bloque de memoria de tamaño cero. La implementación de 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 descubrir que ptr
apunta a un bloque de memoria de tamaño cero.
(Cómo malloc
/ realloc
/ free
hace esto es específico de la implementación. Una posibilidad es asignar 4 bytes más de lo solicitado y almacenar el tamaño justo antes del bloque de memoria. En ese caso, ((int *)ptr)[-1]
haría dar el tamaño de bloque de memoria, que es 0
Nunca debe hacer esto desde su código, es solo para uso con realloc
y free
).
Esta pregunta ya tiene una respuesta aquí:
- ¿Cuál es el punto en malloc (0)? 17 respuestas
¿Qué devuelve malloc(0)
? ¿La respuesta sería la misma para realloc(malloc(0),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 linux gcc:
manav@manav-workstation:~$ gcc -Wall mal.c
manav@manav-workstation:~$ ./a.out
0x9363008
(nil)
manav@manav-workstation:~$
La salida cambia constantemente 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?
EDITAR:
Si malloc(0)
devuelve el puntero ficticio, ¿cómo funciona el siguiente?
int main()
{
void *ptr = malloc(0);
printf("%p/n", realloc(ptr, 1024));
return 0;
}
EDITAR:
El siguiente código genera "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;
}
Creo que eso depende Revisé las fuentes de Visual Studio 2005 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 solicite 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.
En C89, malloc (0) depende de la implementación; no sé si C99 lo ha solucionado o no. En C ++, usando:
char * p = new char[0];
está bien definido: obtienes un puntero válido que no es 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.
Estándar C99
Si no se puede asignar el espacio, se devuelve un nullpointer. Si el tamaño del espacio solicitado es cero, el comportamiento está definido por la implementación: 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 debe usar para acceder a un objeto .
Hemos implementado estructuras malloc para código incrustado con un encabezado (y un avance 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".
La FAQ de comp.lang.c tiene lo siguiente para decir:
El Estándar ANSI / ISO dice que puede hacer cualquiera de los dos; el comportamiento está definido por la implementación (ver pregunta 11.33). El código portátil debe tener cuidado de no llamar a malloc (0) o estar preparado para la posibilidad de un retorno nulo.
Entonces, probablemente sea mejor evitar el uso de malloc(0)
.
Otros han respondido cómo funciona malloc(0)
. 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 pararealloc(malloc(0),0)
?
El estándar dice esto sobre realloc(ptr, size)
:
- si
ptr
esNULL
, se comporta comomalloc(size)
, - de lo contrario (
ptr
no esNULL
), desasigna el antiguo puntero aptr
y devuelve un puntero a un nuevo buffer asignado. Pero si elsize
es 0, C89 dice que el efecto es equivalente afree(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íaNULL
.
Entonces, hay dos casos:
-
malloc(0)
devuelveNULL
en una implementación. Entonces su llamada arealloc()
es equivalente arealloc(NULL, 0)
. Eso es equivalente amalloc(0)
desde arriba (y eso esNULL
en este caso). -
malloc(0)
devuelve noNULL
. Entonces, la llamada es equivalente afree(malloc(0))
. En este caso,malloc(0)
yrealloc(malloc(0), 0)
no son equivalentes.
Tenga en cuenta que aquí hay un caso interesante: en el segundo caso, cuando malloc(0)
devuelve un valor no NULL
en caso de éxito, aún puede devolver NULL
para indicar el error. 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 si la omisión en C99 es un descuido o si eso significa que en C99, realloc(ptr, 0)
para ptr
no es equivalente a free(ptr)
. Acabo de probar esto con gcc -std=c99
, y lo anterior es equivalente a free(ptr)
.
Editar : Creo que entiendo cuál es tu confusión:
Veamos un fragmento de tu 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 a 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 realloc(ptr, 1024)
, realloc()
básicamente te da un nuevo buffer que tiene el tamaño 1024, y el ptr
vuelve invá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 volverse verdadera. (Tenga en cuenta, sin embargo, mirar el valor de ptr
después de realloc(ptr, 1024)
puede ser un comportamiento indefinido).
Ahora la pregunta que hace es: 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 de retorno de malloc()
en el LHS no ha estado free()
d todavía, por lo que cualquier otro malloc()
, calloc()
o realloc()
puede que no devuelva ese valor. Esto significa que si escribió su condición como:
if (malloc(0) == realloc(malloc(0), 1024)
puts("possible");
no verá possible
en la salida (a menos que tanto malloc()
como realloc()
fallen y devuelvan 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
.
Un punto que a nadie le importaba hablar aún, en su primer programa es que realloc
con longitud 0 es lo mismo que free
.
de la página man de Solaris:
La función
realloc()
cambia el tamaño del bloque apuntado porptr
a bytes desize
y devuelve un puntero al bloque (posiblemente movido). Los contenidos no se modificarán hasta el menor de los tamaños nuevo y antiguo. Siptr
esNULL
,realloc()
comporta comomalloc()
para el tamaño especificado. Si elsize
es0
yptr
no es un puntero nulo, el espacio apuntado queda disponible para una asignación posterior por parte de 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í).
Ver C99, sección 7.20.3:
Si el tamaño del espacio solicitado es cero, el comportamiento está definido por la implementación: 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 usará para acceder a un objeto.
Esto es válido para las tres funciones de asignación (es decir, calloc()
, malloc()
y realloc()
).
malloc(0)
es Implementación Definida en lo que se refiere a C99.
De C99 [Sección 7.20.3]
El orden y la contigüidad del almacenamiento asignado por las llamadas sucesivas a las funciones calloc, malloc y realloc no están especificados . 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 utilizarse para acceder a dicho objeto o una matriz de dichos objetos en el espacio asignado (hasta que el espacio se desasigna explícitamente) . La duración de un objeto asignado se extiende desde la asignación hasta la desasignación. Cada una de esas asignaciones arrojará un puntero a un objeto disjuntado de cualquier otro objeto. El puntero devolvió los puntos al inicio (dirección de byte 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 se define en la implementación : 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 debe usar para acceder a un objeto .