2011-01-13 14 views
8

en este ejemplo, a pesar de que nunca voy a usar el WNDCLASSEX las variables, x, y, CX, CY, van a seguir utilizando la memoria cuando estoy en el mensaje de bucle:Al declarar variables en {} ámbito, ¿seguirán usando memoria después?

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow) 
    { 
    WNDCLASSEX wc; 
    ... 
    RegisterClassEx(&wc); 

    const int cx = 640; 
    const int cy = 480; 
    // center of the screen 
    int x = (GetSystemMetrics(SM_CXSCREEN) - cx)/2; 
    int y = (GetSystemMetrics(SM_CXSCREEN) - cy)/2; 

    CreateWindow(..., x, y, cx, cy, ...); 

    MSG msg; 

    while (GetMessage(&msg, NULL, 0, 0) > 0) 
    { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return 0; 
    } 

pero me pregunto , si los pongo en un ámbito, ¿seguirían usando memoria durante el ciclo de mensajes? p.ej.

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow) 
{ 
{ 
    WNDCLASSEX wc; 
    ... 
    RegisterClassEx(&wc); 

    const int cx = 640; 
    const int cy = 480; 
    // center of the screen 
    int x = (GetSystemMetrics(SM_CXSCREEN) - cx)/2; 
    int y = (GetSystemMetrics(SM_CXSCREEN) - cy)/2; 

    CreateWindow(..., x, y, cx, cy, ...); 
} 

MSG msg; 

while (GetMessage(&msg, NULL, 0, 0) > 0) 
{ 
    TranslateMessage(&msg); 
    DispatchMessage(&msg); 
} 
return 0; 
} 

o tal vez si los puse en dos funciones y los llamé en winmain p. Ej.

wnd_register(hInst); 
wnd_create(hInst); 

¿Qué les impediría usar la memoria?

+0

buena pregunta +1 Aunque, sería fácil de probar como dice nightcracker. ¡Podría haber pasado menos tiempo probándolo tú mismo que escribiendo esta pregunta! –

+1

¿Por qué declararías variables que no usas? – Falmarri

+0

Esto es realmente una micro-optimización inútil. Los subprocesos en Windows de forma predeterminada tienen una pila de 1 MB. Le preocupa desperdiciar menos de una centésima de un porcentaje del espacio de pila disponible. – Michael

Respuesta

6

El compilador tiene mucha libertad de acción para manejar locales sencillos, como lo ha hecho en sus ejemplos. Pueden vivir en la pila, solo pueden existir como valores inmediatos en el código de la máquina, o simplemente pueden vivir en registros. El espacio de pila generalmente se asigna al ingresar a una función. El compilador restará algún valor del puntero de pila para hacer espacio para todos los locales. Al regresar la función, el puntero de la pila vuelve a su valor original. Esto no suele hacerse al salir de diferentes bloques de alcance. La mayoría de los compiladores intentarán reutilizar agresivamente el espacio de pila tan pronto como las variables ya no se usen. En su ejemplo, sería perfectamente legal que x y msg tengan exactamente la misma dirección en la pila, ya que su uso no está superpuesto.

Mi respuesta a this question entra en más detalles sobre cómo se asignan las variables locales en la pila.

En sus ejemplos, las constantes, cx y cy, muy probablemente no tengan memoria que las respalde en el tiempo de ejecución, y solo serán valores inmediatos en el código generado. x e y muy probablemente vivirán en registros hasta que necesiten ser empujados a la pila para la llamada a CreateWindow. wc y msg casi definitivamente estarán en la pila.

No debe preocuparse por las micro-optimizaciones en este nivel: deje que el compilador asigne espacio para las variables locales como lo considere oportuno. Usted tiene una pila de 1 MB por defecto, la cantidad de datos consumidos por estas variables ni siquiera se registraría como ruido. Dedique su tiempo a preocuparse por problemas más interesantes en su lugar.

+0

+1, aunque en realidad el compilador probablemente NO se puede superponer a wc yx, ya que wc tiene su dirección tomada y se pasa a una función, por lo que se puede acceder en una llamada posterior mientras está activa, como CreateWindow. Digo probablemente como el compilador podría hacer el análisis de función cruzada, pero la mayoría no lo hace –

+0

Sí, tienes razón, tomar la dirección evita que el compilador reutilice el espacio. El compilador no puede hacer análisis de funciones cruzadas aquí ya que RegisterClassEx y CreateWindow viven en un módulo diferente. – Michael

0

Oh dios no, cuatro enteros en la memoria mientras el programa se está ejecutando, ¡qué desperdicio!

  1. Pruébelo, un simple mensaje que intenta imprimirlos debería ser suficiente (creo).
  2. No importa.
+0

en realidad wndclassex tiene aproximadamente 10 miembros: P incluye una cadena – Kaije

+0

Lata incluso haz eso. Obtendrá un error de compilación, 'x' no se declara –

+0

La cadena en wndclass probablemente sea una constante y no salga del espacio en la pila para los locales. – Michael

3

Probablemente no, pero eso es un detalle de implementación. Sin embargo, se habrán destruido (se habrán realizado llamadas al destructor si hay alguna que hacer). La norma no especifica cuándo y cuándo el sistema recupera la memoria utilizada para el almacenamiento automático. La mayoría lo devuelve casi de inmediato afaik.

+0

Cualquier memoria dinámica utilizada dentro de objetos de clase, como en cadenas, debe ser liberada inmediatamente por el destructor del objeto. Me pregunto si hay compiladores que retienen la memoria de la pila hasta el final de la función por razones de velocidad, a pesar de que generalmente es solo 1 instrucción para liberarla. –

+1

La mayoría de los compiladores reutilizarán agresivamente el espacio de la pila. Sería perfectamente legal que msg y wc tuvieran exactamente la misma dirección en sus dos ejemplos. – Michael

+0

@Michael, puedo ver cómo es posible para el segundo caso, pero para el primero el compilador no puede llamar al destructor para 'wc' hasta el final de la función, por lo que no puede compartir memoria con nada. –

0

Las variables declaradas dentro de los {} saldrán del alcance y se perderán. De hecho, obtendrá un error de compilación si intenta usarlos fuera del bloque: 'x' no está declarado. Sin embargo, este es descuidado. Simplemente haga una función para este código, como dijo en su edición. Mantener su principal() tan pocas líneas como sea posible es una buena práctica de programación.

1

Bueno, no estoy seguro de si usan memoria o qué dicen las normas al respecto.

Lo que sí sé es que al final de un bloque de memoria {} se llamará al destructor y las variables serán inalcanzables. Esto podría significar que, si bien no se libera, al menos puede reutilizarse.

Ejemplo:

struct Foo { 
    Foo(void) { std::cout << "Hi!"; } 
    ~Foo(void) { std::cout << "Bye!"; } 
}; 

int main(int argc, char * argv[]) 
{ 
    { 
     Foo bar; // <- Prints Hi! 
    } // <- Prints Bye! 

    // Memory used by bar is now available. 
} 

Edit: Gracias Tomalak Geret'kal;)

+0

"Esto podría significar que, aunque no se libera, al menos se puede reutilizar". - No tiene forma de controlar a dónde van sus variables automáticas. No habría forma de "reutilizar" la memoria no devuelta todavía. –

+0

Tenga en cuenta que, dado que tiene un constructor explícito que ejecuta código, el compilador DEBE ejecutarlo al salir del bloque. Esto no dice nada sobre el espacio de pila subyacente. – Michael

+0

Te faltan llaves en tu ejemplo de código. –

0

Ellos no lo hará. Solo vivirán hasta el final de su bloqueo.

1

Un consejo mágico: Confíe en su compilador. Optimiza. Es inteligente Optimiza mejor que la mayoría de nosotros podría.

Si no está seguro, utilice un generador de perfiles o examine la salida del ensamblador del compilador después de las optimizaciones. Pero recuerde: las optimizaciones triviales es algo que debe hacer en su código , no, ya que no tiene sentido y solo daña la legibilidad de su código.

Algunas variables (especialmente las constantes) no usarán ninguna memoria en la pila porque se mapearán en los registros de la CPU o se incorporarán directamente en una instrucción de ensamblador.

Esto implica que los códigos:

func(123+456*198*value); 

y

int a = 123; 
int b = 56; 
int c = 400; 
int d = b+c; 
int e = d*198; 
e *= value; 
e += a; 
func(e); 

compile a exactamente lo mismo (si las variables nunca vuelvan a utilizarse).

En serio, no te molestes. Si desea optimizar, optimice desde el punto de vista algorítmico, no sintáctico.

0

Si los coloca en un ámbito anidado dentro de la función (su primera opción), cuando el control llega al final, las variables se vuelven inaccesibles (un error de compilación si los usa directamente o un comportamiento indefinido de tiempo de ejecución) si guarda un puntero a uno de ellos), se ejecutan sus destructores (si hay destructores), y la implementación puede reutilizar su espacio de almacenamiento dentro del marco de la pila. Pero los estándares no requieren para reutilizar el espacio.

Si divide su función en dos (su segunda opción) ... en términos de pelos estándar, ¡no hay diferencia! Cuando la función retorna, las variables se vuelven inaccesibles, se ejecutan sus destructores y la implementación puede reutilizar su espacio de almacenamiento, pero no es obligatorio. Y hay tienen implementaciones serias estado - aunque no de C/C++ - que no reciclar inmediatamente que la memoria: ver el más famoso el documento "Cheney on the M.T.A."

Sin embargo, todas las implementaciones de C/C++ que yo Ahora sé de do recicle la memoria asignada para las variables locales de una función cuando se devuelve una función. El reciclaje de memoria para variables de ámbito local anidado es mucho menos cierto. En cualquier caso, como varias otras personas han mencionado, no vale la pena preocuparse por unas pocas decenas de bytes de espacio de pila en este contexto.

Personalmente, dividiría su código en dos funciones solo porque de esa manera cada función hace una sola tarea. En general, es mejor para el mantenimiento a largo plazo.

0

Generalmente sí, si la variable reside en la pila, se tomará espacio para la pila durante todo el tiempo que dure la función adjunta. Los compiladores generalmente calculan la cantidad máxima de espacio que las variables de la función podrían ocupar y luego hacen que la función asigne todo a la vez cuando se ingresa por primera vez a la función. Sin embargo, los constructores y los destructores seguirán siendo llamados al entrar y salir de los ámbitos internos. El espacio para las variables en un ámbito puede volver a utilizarse para representar variables de un ámbito separado.

Cuestiones relacionadas