¿Por qué malloc inicializa los valores a 0 en gcc?


Tal vez es diferente de una plataforma a otra, pero

Cuando compilo usando gcc y corro el siguiente código, obtengo 0 cada vez en mi ubuntu 11.10.

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

int main()
{
    double *a = (double*) malloc(sizeof(double)*100)
    printf("%f", *a);
}

¿Por qué malloc se comporta así a pesar de que hay calloc?

¿No significa que hay una sobrecarga de rendimiento no deseada solo para inicializar los valores a 0, incluso si no desea que sea a veces?


EDITAR: Oh, mi ejemplo anterior no era inicializar, pero pasó a usar " fresco" bloque.

Lo que precisamente estaba buscando era por qué lo inicializa cuando asigna un bloque grande:

int main()
{
    int *a = (int*) malloc(sizeof(int)*200000);
    a[10] = 3;
    printf("%d", *(a+10));

    free(a);

    a = (double*) malloc(sizeof(double)*200000);
    printf("%d", *(a+10));
}

OUTPUT: 3
        0 (initialized)

Pero gracias por señalar que hay una razón de SEGURIDAD cuando mallocing! (Nunca pensé en ello). Seguro que tiene que inicializar a cero al asignar bloque fresco, o el bloque grande.

Author: Jeegar Patel, 2011-11-06

9 answers

Respuesta Corta:

No lo hace, simplemente pasa a ser cero en su caso.
(También su caso de prueba no muestra que los datos son cero. Solo muestra si un elemento es cero.)


Respuesta Larga:

Cuando llamas a malloc(), una de dos cosas sucederá:

  1. Recicla la memoria que fue previamente asignada y liberada del mismo proceso.
  2. Solicita nuevas páginas del sistema operativo.

En el primer caso, la memoria contendrá los datos sobrantes de asignaciones anteriores. Así que no será cero. Este es el caso habitual cuando se realizan pequeñas asignaciones.

En el segundo caso, la memoria será del sistema operativo. Esto sucede cuando el programa se queda sin memoria , o cuando solicita una asignación muy grande. (como es el caso en su ejemplo)

Aquí está el truco: La memoria proveniente del sistema operativo se pondrá a cero por razones de seguridad.*

Cuando el sistema operativo te da memoria, podría haber sido liberado de un proceso diferente. Así que la memoria podría contener información sensible como una contraseña. Así que para evitar que la lectura de tales datos, el sistema operativo se cero antes de que se lo da a usted.

*Observo que el estándar C no dice nada sobre esto. Esto es estrictamente un comportamiento del sistema operativo. Por lo tanto, esta reducción a cero puede o no estar presente en sistemas donde la seguridad no es una preocupación.


Para dar más de un fondo de rendimiento a esto:

As @R. menciones en los comentarios, esta reducción a cero es la razón por la que siempre debe usar calloc() en lugar de malloc() + memset(). calloc() puede aprovechar este hecho para evitar un memset() separado.


Por otro lado, esta reducción a cero es a veces un cuello de botella de rendimiento. En algunas aplicaciones numéricas (como el fuera de lugar FFT), necesita asignar una gran cantidad de memoria de scratch. Úsalo para realizar cualquier algoritmo, luego libéralo.

En estos casos, la reducción a cero es innecesario y equivale a gastos generales puros.

El ejemplo más extremo que he visto es una sobrecarga de cero de 20 segundos para una operación de 70 segundos con un búfer de cero de 48 GB. (Aproximadamente 30% de gastos generales.) (Concedido: la máquina tenía una falta de ancho de banda de memoria.)

La solución obvia es simplemente reutilizar la memoria manualmente. Pero eso a menudo requiere romper las interfaces establecidas. (especialmente si es parte de una rutina de biblioteca)

 163
Author: Mysticial,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:25

El sistema operativo generalmente borrará las páginas de memoria nuevas que envía a su proceso para que no pueda ver los datos de un proceso anterior. Esto significa que la primera vez que inicializa una variable (o algo malloc) a menudo será cero, pero si alguna vez reutiliza esa memoria (liberándola y malloc-ing de nuevo, por ejemplo), entonces todas las apuestas están canceladas.

Esta inconsistencia es precisamente la razón por la que las variables no inicializadas son un error tan difícil de encontrar.


En cuanto a los gastos generales de rendimiento no deseados, evitar comportamientos no especificados es probablemente más importante. Cualquier pequeño aumento de rendimiento que pueda obtener en este caso no compensará los errores difíciles de encontrar con los que tendrá que lidiar si alguien modifica ligeramente los códigos (rompiendo las suposiciones anteriores) o los traslada a otro sistema (donde las suposiciones podrían haber sido inválidas en primer lugar).

 20
Author: hugomg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:10:36

¿Por qué asumes que malloc() se inicializa a cero? Resulta que la primera llamada a malloc() resulta en una llamada a sbrk o mmap llamadas al sistema, que asignan una página de memoria desde el sistema operativo. El sistema operativo está obligado a proporcionar memoria cero inicializada por razones de seguridad (de lo contrario, los datos de otros procesos se hacen visibles!). Así que podría pensar que hay - el sistema operativo pierde tiempo poniendo a cero la página. Pero no! En Linux, hay una página singleton especial para todo el sistema llamada 'página cero' y que page se asignará como Copy-On-Write, lo que significa que solo cuando realmente escriba en esa página, el sistema operativo asignará otra página e inicializará. Así que espero que esto responda a su pregunta sobre el rendimiento. El modelo de paginación de memoria permite que el uso de la memoria sea algo perezoso al admitir la capacidad de mapeo múltiple de la misma página más la capacidad de manejar el caso cuando se produce la primera escritura.

Si llama a free(), el asignador glibc devolverá la región a su libre listas, y cuando se llama de nuevo a malloc(), puede obtener la misma región, pero sucia con los datos anteriores. Eventualmente, free() podría devolver la memoria al sistema operativo llamando a las llamadas del sistema nuevamente.

Observe que el glibc la página de manual en malloc() dice estrictamente que la memoria no se borra, por lo que por el "contrato" en la API, no se puede asumir que se borra. Aquí está el extracto original:

Malloc () asigna bytes de tamaño y devuelve un puntero a la memoria.
La memoria no se borra. Si size es 0, entonces malloc () devuelve NULL, o un valor de puntero único que más tarde se puede pasar con éxito a free().

Si lo desea, puede leer más sobre esa documentación si está preocupado por el rendimiento u otros efectos secundarios.

 15
Author: Dan Aloni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:32:17

Modifiqué su ejemplo para contener 2 asignaciones idénticas. Ahora es fácil ver que malloc no pone cero la memoria de inicialización.

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

int main(void)
{
    {
      double *a = malloc(sizeof(double)*100);
      *a = 100;
      printf("%f\n", *a);
      free(a);
    }
    {
      double *a = malloc(sizeof(double)*100);
      printf("%f\n", *a);
      free(a);
    }

    return 0;
}

Salida con gcc 4.3.4

100.000000
100.000000
 13
Author: Praetorian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:19:24

El estándar no dicta que malloc() debe inicializar los valores a cero. Simplemente sucede en su plataforma que podría estar establecido en cero, o podría haber sido cero en el momento específico que lee ese valor.

 2
Author: Muggen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:09:28

Su código no demuestra que malloc inicializa su memoria a 0. Eso podría ser hecho por el sistema operativo, antes de que se inicie el programa. Para ver que shich es el caso, escribe un valor diferente a la memoria, libérala, y llama a malloc de nuevo. Probablemente obtendrá la misma dirección, pero tendrá que verificar esto. Si es así, puede mirar para ver lo que contiene. Háganoslo saber!

 2
Author: TonyK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:09:50

De gnu.org:

Los bloques muy grandes (mucho más grandes que una página) se asignan con mmap (anónimo o a través de /dev/zero) por esta implementación.

 2
Author: TomaszK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:15:56

¿Sabes que definitivamente se está inicializando? ¿Es posible que el área devuelta por malloc() tenga con frecuencia 0 al principio?

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:09:26

Nunca nunca cuente con cualquier compilador para generar código que inicialice la memoria a cualquier cosa. malloc simplemente devuelve un puntero a n bytes de memoria en algún lugar infierno que incluso podría estar en intercambio.

Si el contenido de la memoria es crítico, inicialícelo usted mismo.

 0
Author: FlyingGuy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-06 19:15:36