usar pointer como c linux gcc malloc

como - pointer malloc



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

Tal vez es diferente de una plataforma a otra, pero

cuando compilo usando gcc y ejecuto el código a continuación, 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í aunque hay calloc?

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

EDITAR: Oh, mi ejemplo anterior no iniciaba, pero pasó a usar bloque "nuevo".

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 eso). Claro que tiene que inicializarse a cero cuando se asigna un bloque nuevo o un bloque grande.


¿Por qué supone que malloc() inicializa a cero? Resulta que la primera llamada a malloc() da como resultado una llamada a las llamadas al sistema sbrk o mmap , que asignan una página de memoria del sistema operativo. El sistema operativo está obligado a proporcionar memoria inicializada cero por razones de seguridad (de lo contrario, los datos de otros procesos se vuelven visibles). Entonces, puede que piense allí: el sistema operativo pierde tiempo para poner a cero la página. ¡Pero no! En Linux, hay una página singleton especial en todo el sistema llamada ''página cero'' y esa página se mapeará como Copy-On-Write, lo que significa que solo cuando escriba en esa página, el sistema operativo asignará otra página y Inicialízalo. Así que espero que esto responda a su pregunta con respecto al rendimiento. El modelo de paginación de memoria permite que el uso de la memoria sea una especie de vago 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 sus listas libres, y cuando se llama nuevamente a malloc() , puede obtener esa misma región, pero sucia con los datos anteriores. Finalmente, free() puede devolver la memoria al sistema operativo llamando de nuevo a las llamadas al sistema.

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

malloc () asigna bytes de tamaño y devuelve un puntero a la memoria asignada.
La memoria no está borrada. Si el tamaño es 0, entonces malloc () devuelve NULL o un valor de puntero único que luego se puede pasar a free ().

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


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


De gnu.org :

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


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


El sistema operativo normalmente 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 malloc algo) a menudo será cero, pero si alguna vez reutiliza esa memoria (liberándola y mallocándola de nuevo, por ejemplo), entonces todas las apuestas estarán desactivadas.

Esta inconsistencia es precisamente por qué las variables no inicializadas son un error difícil de encontrar.

En cuanto a los gastos generales de rendimiento no deseados, evitar el comportamiento no especificado es probablemente más importante . Cualquier pequeña mejora 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 levemente los códigos (rompiendo suposiciones previas) o los puertos a otro sistema (donde las suposiciones podrían haber sido inválidas) en primer lugar).


Modifiqué tu ejemplo para que contenga 2 asignaciones idénticas. Ahora es fácil ver que malloc no inicializa la memoria a cero.

#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


Nunca cuente con ningún compilador para generar código que inicialice la memoria a nada. malloc simplemente devuelve un puntero a n bytes de memoria en algún lugar infernal que incluso podría estar en swap.

Si el contenido de la memoria es crítico, inícielo usted mismo.


Su código no demuestra que malloc inicializa su memoria en 0. Eso podría ser hecho por el sistema operativo, antes de que comience el programa. Para ver cuál es el caso, escriba un valor diferente en la memoria, libérelo y vuelva a llamar a malloc. Probablemente obtendrá la misma dirección, pero tendrá que verificar esto. Si es así, puede ver qué contiene. ¡Haznos saber!


Respuesta corta:

No, simplemente resulta ser cero en tu 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() , sucederá una de estas dos cosas:

  1. Se recicla la memoria que se asignó previamente y se liberó del mismo proceso.
  2. Solicita una nueva página (s) del sistema operativo.

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

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

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

Cuando el SO le da memoria, podría haber sido liberado de un proceso diferente. Para que la memoria pueda contener información confidencial, como una contraseña. Por lo tanto, para evitar que lea esos datos, el sistema operativo lo pondrá a cero antes de que se lo proporcione.

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

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

Como @R. menciona en los comentarios, esta puesta a cero es la razón por la que siempre debe usar calloc() 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 la FFT fuera de lugar ), debe asignar una gran cantidad de memoria de memoria temporal. Úselo para realizar cualquier algoritmo, luego libérelo.

En estos casos, la puesta a cero es innecesaria y equivale a una sobrecarga pura.

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 buffer de 48 GB. (Aproximadamente 30% de sobrecarga.) (De acuerdo: 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 con las interfaces establecidas. (especialmente si es parte de una rutina de biblioteca)