2010-06-23 16 views
14

Viniendo de un fondo C, siempre he supuesto que los tipos POD (por ejemplo, ints) nunca se inicializaron automáticamente en C++, ¡pero parece que esto estaba completamente equivocado!¿Cuándo los tipos de C++ POD se inicializan en cero?

Tengo entendido que solo los valores de POD no estáticos "desnudos" no se llenan a cero, como se muestra en el fragmento de código. ¿Lo he acertado, y hay otros casos importantes que me he perdido?

static int a; 

struct Foo { int a;}; 

void test() 
{ 
    int b;  
    Foo f; 
    int *c = new(int); 
    std::vector<int> d(1); 

    // At this point... 
    // a is zero 
    // f.a is zero 
    // *c is zero 
    // d[0] is zero 
    // ... BUT ... b is undefined  
} 
+0

¿Estás seguro de que es C++ y no, por ejemplo, el sistema operativo? Me imagino (pero no lo he comprobado) que cuando un SO asigna memoria a su proceso, se pone a cero, al menos cuando toca la página de memoria. Ni C ni C++ requieren este comportamiento, pero si el sistema operativo le entrega páginas de memoria con lo que sea que el último proceso haya insertado en ellas, sería un gran agujero de seguridad. Podría haber información de inicio de sesión o claves privadas allí si, por ejemplo, ssh fuera el último proceso para usar esa página de memoria física. –

+0

Parecería un poco inútil para el sistema operativo mantener a cero la memoria cuando se asigna. – Alan

+2

También puede leer el excelente, puesto relacionado por Michael Burr en respuesta a [¿Los paréntesis después del nombre del tipo marcan la diferencia con el nuevo?] (Http://stackoverflow.com/questions/620137/do-the-parentheses- after-the-type-name-make-a-difference-with-new/620402 # 620402) –

Respuesta

16

Suponiendo que no haya modificado a antes de llamar al test(), a tiene un valor de cero, porque los objetos con una duración de almacenamiento estática se inicializan en cero cuando se inicia el programa.

d[0] tiene un valor de cero, debido a que el constructor invocado por std::vector<int> d(1) tiene un segundo parámetro que toma un argumento predeterminado; ese segundo argumento se copia en todos los elementos del vector que se está construyendo. El argumento por defecto es T(), por lo que su código es equivalente a:

std::vector<int> d(1, int()); 

Tiene razón en que b tiene un valor indeterminado.

f.a y *c ambos tienen valores indeterminados también. Para valorar inicializarlas (que para este tipo de POD es la misma que la inicialización de cero), puede utilizar:

Foo f = Foo();  // You could also use Foo f((Foo())) 
int* c = new int(); // Note the parentheses 
+0

Mayormente acordado; sólo que no estoy seguro de por qué 'Foo f' no llama al constructor sintetizado, y si lo hace, ¿por qué eso sería diferente de' Foo f = Foo(); '... – xtofl

+0

Gracias. ¡Y doble gracias por el increíble enlace en tu comentario! – Roddy

+1

@xtofl: Si no hay un inicializador presente (como es el caso de 'Foo f;') para un objeto de tipo POD, entonces ese objeto se deja sin inicializar. 'Foo f = Foo();' crea un 'Foo' inicializado en valores (eso es lo que hace la parte' Foo() ') y luego lo usa para inicializar' f'. –

1

En realidad algunos de los valores que son cero puede ser debido a que tratando este código en la versión de depuración de la aplicación (si ese es el caso).

Si no me equivoco, en el código:

  • una debe ser inicializado.
  • b debe desinicializar
  • c debe apuntar a una nueva (no inicializado) int
  • d debe ser inicializado a [0] (como usted adivinado correctamente)
+0

Creo 'a' se aclararía a' 0' en los sistemas operativos más Unix hoy. Técnicamente, 'a' estaría en el segmento' .bss', que generalmente se establece en todos los 0 antes de que se llame a 'main()'. –

+0

Sí, pero mi punto era que él no debería confiar en eso incluso si el valor aparece como cero en el código. La inicialización explícita es el camino a seguir aquí. – utnapistim

+1

A menos que 'a' se haya modificado antes de llamar' test() ', tendrá un valor de cero. Los objetos con una duración de almacenamiento estática se inicializan en cero cuando se inicia el programa. –

0

Para mí, tipos de POD se inicializan en función de la parte de la memoria que se colocan. Su static int a está asignado en el segmento de datos, por lo que tiene un valor predeterminado al inicio. Sin embargo, creo que f no está incializado en su ejemplo ...

0

No lo hacen. Las versiones de bits de depuración pueden hacer esto, pero por lo general solo se coloca en la memoria y se inicializa a lo que sucedió como el valor en la memoria.

1

Tenga en cuenta que la inicialización a cero realizada por el sistema operativo como característica de seguridad generalmente solo se realiza la primera vez que se asigna la memoria. Con eso me refiero a cualquier segmento en las secciones de montón, pila y datos. Las secciones de pila y datos son típicamente de tamaño fijo y se inicializan cuando la aplicación se carga en la memoria.

El segmento de datos (que contiene datos y códigos estáticos/globales) normalmente no se "reutiliza", aunque puede no ser el caso si carga dinámicamente el código en tiempo de ejecución.

La memoria en el segmento de pila se vuelve a utilizar todo el tiempo. Las variables locales, los marcos de pila de funciones, etc. se utilizan y reutilizan constantemente, y no se inicializan cada vez, solo cuando se carga la aplicación por primera vez.

Sin embargo, cuando la aplicación hace que las solicitudes de memoria de almacenamiento dinámico, el administrador de memoria normalmente cero a inicializar los segmentos de memoria antes de conceder la petición, pero sólo para los nuevos segmentos. Si realiza una solicitud de memoria Heap, y hay espacio libre en un segmento que ya se ha inicializado, la inicialización no se realiza por segunda vez. Por lo tanto, no hay garantía de que si su aplicación vuelve a usar ese segmento de memoria en particular, se reiniciará a cero.

Así, por ejemplo, si asigna un Foo en el montón, asigne su campo un valor, eliminar la instancia de Foo, y luego crear una nueva Foo en el montón, existe la posibilidad de que el nuevo Foo se asignará en la misma ubicación de memoria exacta que el antiguo Foo, por lo que su campo tendrá inicialmente el mismo valor que el antiguo campo de Foo.

Si se piensa en ello, esto tiene sentido, debido a que el sistema operativo sólo está inicializando los datos para evitar una aplicación acceda a los datos de otra aplicación. Hay menos riesgos al permitir que una aplicación acceda a sus propios datos, por lo que, por motivos de rendimiento, la inicialización no se realiza todas las veces, solo la primera vez que un segmento particular de la memoria está disponible para la aplicación (en cualquier segmento).

A veces, cuando ejecuta una aplicación en modo de depuración, algunos tiempos de ejecución del modo de depuración inicializan los datos de la pila y del montón en cada asignación (así que su campo Foo siempre se inicializará). Sin embargo, diferentes tiempos de ejecución de depuración inicializan los datos en diferentes valores. Algunos se inicializan en cero y algunos se inicializan en un valor de "marcador".

El punto es - nunca utilizar valores sin inicializar cualquier parte del código. No hay absolutamente ninguna garantía de que se inicializarán en cero. Además, asegúrese de leer el artículo vinculado anteriormente con respecto a los paréntesis y la inicialización de valores predeterminados frente a valores, ya que esto afecta la definición de un valor "no inicializado".

+0

No * absolutamente * es una garantía de que 'a' en el ejemplo del OP se inicializará en cero. –

Cuestiones relacionadas