c linux pointers malloc realloc

¿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í:

¿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 para realloc(malloc(0),0) ?

El estándar dice esto sobre realloc(ptr, size) :

  • si ptr es NULL , se comporta como malloc(size) ,
  • de lo contrario ( ptr no es NULL ), desasigna el antiguo puntero a ptr y devuelve un puntero a un nuevo buffer asignado. Pero si el 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 .

Entonces, hay dos casos:

  • malloc(0) devuelve NULL en una implementación. Entonces su llamada a 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) 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 por ptr a bytes de size y devuelve un puntero al bloque (posiblemente movido). Los contenidos no se modificarán hasta el menor de los tamaños nuevo y antiguo. Si ptr es NULL , realloc() comporta como malloc() para el tamaño especificado. Si el size es 0 y ptr 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 .