Red de conocimiento informático - Problemas con los teléfonos móviles - La diferencia entre la versión de lanzamiento y la versión de depuración en VC

La diferencia entre la versión de lanzamiento y la versión de depuración en VC

La depuración generalmente se denomina versión de depuración, que contiene información de depuración pero no está optimizada de ninguna manera para facilitar a los programadores la depuración del programa. La versión de lanzamiento se denomina versión de lanzamiento, que a menudo se optimiza de varias maneras para hacer que el tamaño del código del programa sea más pequeño; y velocidad para que los usuarios puedan utilizar bien el programa.

El verdadero secreto para depurar y publicar reside en el conjunto de opciones de compilación. Las opciones de compilación para cada versión se enumeran a continuación (por supuesto, hay otras opciones como /Fd /Fo, pero la diferencia no es importante y generalmente no causan errores en las compilaciones de lanzamiento y depuración)

Significado del parámetro

/MDd /MLd o /MTd la versión de depuración utiliza la biblioteca de tiempo de ejecución de depuración

/Od desactiva el interruptor de optimización

/ D "_DEBUG " es equivalente a #define _DEBUG, activando el interruptor para compilar y depurar código (utilizado principalmente para funciones de aserción)

/ZI crea una base de datos para editar y continuar, de modo que si modifica el código fuente durante la depuración, no habrá no es necesario volver a compilar

/GZ ayuda a detectar errores de memoria

/Gm para activar el interruptor de reenlace minimizado para reducir el tiempo de enlace

Versión de lanzamiento

Significado del parámetro

/MD /ML o /MT para usar la versión de lanzamiento del tiempo de ejecución

Versión de lanzamiento

Significado

/MD /ML o /MT para usar la versión de lanzamiento de la biblioteca en tiempo de ejecución

/Cambio de optimización /O1 o /O2 para hacer que el programa sea más pequeño o más rápido

/D "NDEBUG "Cambiar, desactivar la compilación condicional del código de depuración (por ejemplo, no compilar funciones de aserción), no compilar funciones de aserción)

/GF Fusionar cadenas duplicadas y colocar constantes de cadena en la memoria de solo lectura para evitar Se modifican

De hecho, no existe un límite intrínseco entre depuración y liberación. Las versiones no son límites per se; son solo colecciones de opciones de compilación que el compilador simplemente opera según lo previsto. De hecho, incluso puede modificar estas opciones para obtener una compilación de depuración optimizada o una compilación de lanzamiento con declaraciones de seguimiento.

Errores que pueden ocurrir en la versión de distribución

Dicho esto, entendamos estas opciones una por una y veamos cómo ocurren los errores de distribución

1. bibliotecas: la biblioteca de tiempo de ejecución con la que se vincula generalmente solo tiene un impacto en el rendimiento de su programa. La versión de depuración del tiempo de ejecución contiene información de depuración y utiliza una serie de mecanismos de protección para ayudar a encontrar errores, por lo que su rendimiento no es tan bueno como el de la versión de lanzamiento. La biblioteca de tiempo de ejecución proporcionada por el compilador generalmente es estable y no causará errores en la versión de lanzamiento; por el contrario, debido a que la biblioteca de tiempo de ejecución de depuración tiene una detección mejorada de errores como la asignación de memoria dinámica, a veces pueden ocurrir errores en la versión de depuración; La versión de lanzamiento es normal. Cabe señalar que si ocurre un error de depuración, incluso si la versión es normal, debe haber un error en el programa, pero es posible que la versión de versión no se muestre en una ejecución determinada.

2. Optimización: esta es la principal causa de errores, porque cuando la optimización está desactivada, el programa fuente básicamente se traduce directamente, pero cuando la optimización está activada, el compilador hará una serie de suposiciones. Los principales tipos de errores de este tipo son los siguientes:

1. Omisión del puntero de trama (FPO para abreviar): durante el proceso de llamada a la función, toda la información de la llamada (dirección del remitente, parámetros) y las variables automáticas se colocan en la pila. Si la declaración de una función difiere de su implementación (parámetros, valores de retorno, métodos de llamada), se producirá un error. Sin embargo, en el modo de depuración, el acceso a la pila se implementa a través de la dirección almacenada en el registro EBP. Si no hay errores como matrices fuera de límites (o "no muchos" errores fuera de límites), la función. generalmente se puede ejecutar normalmente en el modo de lanzamiento, la optimización omitirá EBP. En el modo de lanzamiento, las optimizaciones omiten el puntero base de la pila EBP, por lo que acceder a la pila a través de un puntero global puede provocar un error de dirección de retorno y bloquear el programa.

La potente función de escritura de C++ comprueba la mayoría de estos errores, pero no incluye conversiones. Puede forzar la opción /Oy-compile para desactivar el puntero de marco que falta en las compilaciones de lanzamiento para determinar si existe dicho error. Los errores típicos de este tipo incluyen errores de escritura de la función de respuesta de mensajes MFC. La función correcta debe ser:

afx_msg LRESULT OnMessageOwn

(WPARAM wparam, LPARAM lparam);

La macro ON_MESSAGE contiene una conversión. Una forma de evitar este error es redefinir la macro ON_MESSAGE y agregar el siguiente código en stdafx.h (después de #include "afxwin.h"), que informará un error si la función está en el formato incorrecto.

#undef ON_MESSAGE

#define ON_MESSAGE(mensaje, memberFxn) /

{

mensaje, 0, 0, AfxSig_lwl, /

(AFX_PMSG)(AFX_ PMSGW)

(static_cast< LRESULT (AFX_MSG_CALL /

CWnd::*)(WPARAM, LPARAM) > (&memberFxn)

},

2.Variable de tipo volátil: volátil le dice al compilador que la variable puede modificarse de formas desconocidas fuera del programa (por ejemplo, el sistema, otros procesos y subprocesos). Los optimizadores a menudo colocan ciertas variables en los registros (como palabras clave de registro) para que el programa se ejecute mejor. Otros procesos solo pueden modificar la memoria donde se encuentran las variables, mientras que el valor en el registro permanece sin cambios. Es probable que encuentre este problema si su programa tiene múltiples subprocesos o si descubre que el valor de una variable no es el que esperaba, pero está seguro de que la variable está configurada correctamente. Este error a veces se manifiesta como un programa que. falla en la optimización más rápida pero funciona bien en la optimización más pequeña. Intente agregar volátiles a las variables que cree que son sospechosas.

3. El optimizador optimizará la variable según su uso. , si hay una variable no utilizada en la función, en la versión de depuración, puede enmascarar la matriz fuera de los límites, mientras que en la versión de lanzamiento, cuando la matriz se sale de los límites, destruye los datos útiles en la pila. La variable se puede optimizar. Por supuesto, la situación real es más complicada que esto. Los errores relacionados con esto son acceso ilegal, incluidos límites de matriz, errores de puntero, etc.

void fn(void). )

{

int i;

i = 1;

int a[4];

{

int j;

j = 1;

}

a[-1] = 1;

/

a[4] = 1;

}

j Aunque j no está en el alcance cuando la matriz cruza el límite, su espacio no se recuperará. Por lo tanto, el cruce de bloques i y j tiene poco efecto, la versión de lanzamiento puede optimizarse, destruyendo así la pila

3. , la función afirmar () se compilará, pero la definición de NDEBUG no. Además, la compilación de la macro TRACE() también está controlada por _DEBUG.

Todas estas afirmaciones solo se compilan en compilaciones de depuración y se ignoran en compilaciones de lanzamiento. La única excepción es VERIFICAR(). De hecho, todas estas macros llaman a la función afirmar() y simplemente agregan algún código de depuración relacionado con la biblioteca.

Si agrega cualquier código de programa a estas macros, que no sean solo expresiones booleanas (como asignaciones, llamadas a funciones que cambian el valor de una variable, etc.), la versión de lanzamiento no realizará estas operaciones, lo que generará un error. Los principiantes pueden cometer este error fácilmente, y el método de búsqueda también es muy simple, porque estas macros se enumeran arriba. Simplemente use la función "Buscar en archivos" de VC ++ para buscar y usar estas macros en todos los archivos del proyecto. y luego revísalos uno por uno. Además, algunos expertos pueden agregar compilación condicional como #ifdef _DEBUG, así que preste atención.

Como nota al margen, existe la macro VERIFY(), que le permite poner el código del programa en una expresión booleana. Esta macro se utiliza normalmente para comprobar los valores de retorno de las API de Windows. Algunas personas pueden abusar de VERIFY () debido a esto, lo cual en realidad es muy peligroso, porque VERIFY () viola el concepto de aserción y no puede separar completamente el código del programa y el código de depuración, lo que eventualmente puede causar muchos problemas. Por ello, los expertos recomiendan utilizar esta macro con precaución.

4. Opción /GZ: Las funciones de esta opción incluyen:

1. Inicializar memoria y variables. Esto incluye inicializar todas las variables automáticas con 0xCC (datos borrados), inicializar la memoria asignada en el montón (es decir, memoria asignada dinámicamente, como nueva) y llenar la memoria del montón liberada (es decir, en nueva) con 0xDD (memoria muerta). se agrega antes y después de la memoria asignada dinámicamente para evitar el acceso fuera de límites), donde las palabras entre paréntesis son mnemónicos recomendados por Microsoft. La ventaja de esto es que los valores son lo suficientemente grandes como para que no puedan ser punteros (en sistemas de 32 bits, los punteros rara vez tienen valores impares y, en algunos sistemas, los punteros impares generarán errores de tiempo de ejecución) y los valores numéricos rara vez son encontrado y es fácil de identificar, por lo que es excelente para detectar errores en las compilaciones de depuración que solo se encuentran en las compilaciones de lanzamiento. En particular, mucha gente cree que el compilador inicializa las variables a 0, lo cual es incorrecto (lo cual es muy perjudicial para encontrar errores).

2. Al llamar a una función a través de un puntero de función, se verificará el puntero de la pila para verificar si la llamada a la función coincide. (Evita la falta de coincidencia de primitivas)

3. Verifique el puntero de la pila antes de que la función regrese para confirmar que no ha sido modificado. (Para evitar el acceso fuera de límites y la falta de coincidencia de primitivas, junto con el segundo elemento, puede simular aproximadamente el puntero del marco omitiendo el FPO) Normalmente, la opción /GZ provocará un error en la versión de depuración, mientras que la versión de lanzamiento será normal, debido a que las variables no inicializadas en la versión de lanzamiento son aleatorias, lo que puede hacer que el puntero apunte a una dirección válida y oculte el acceso ilegal. De lo contrario, opciones como /Gm/GF causarán menos errores y sus efectos serán perceptibles y más fáciles de detectar.

Cómo "depurar" la versión de lanzamiento de un programa

Obviamente es frustrante si la depuración tiene éxito pero la versión de lanzamiento falla y, por lo general, no hay nada que puedas hacer al respecto. Si lee el análisis anterior y lo combina con las manifestaciones específicas del error, podrá encontrar el error rápidamente, eso es genial. Pero si no puede encontrarlo en este momento, aquí hay algunas estrategias que puede utilizar en tales situaciones.

1. Como se mencionó anteriormente, Debug y Release son solo un conjunto de opciones de compilación y sus definiciones en realidad no se distinguen. Podemos modificar las opciones de compilación de la versión Release para limitar el alcance del error. Como se mencionó anteriormente, puede cambiar las opciones de lanzamiento una por una a las opciones de depuración correspondientes, como cambiar /MD a /MDd, /O1 a /Od o cambiar la optimización del tiempo de ejecución a la optimización del tamaño del programa. Cabe señalar que solo puede cambiar una opción a la vez. Vea qué opción desaparece el error cuando la cambia y luego realice una búsqueda específica de errores relacionados con esta opción. Estas opciones se pueden seleccionar directamente desde la lista en Proyecto/Configuración.... Estas opciones se pueden seleccionar directamente desde la lista en Proyecto/Configuración... y generalmente no es necesario modificarlas manualmente. Dado que el análisis anterior es relativamente completo, este método es el más eficaz.

2. Durante el proceso de programación, siempre debes prestar atención a probar la versión de lanzamiento para evitar demasiado código y poco tiempo.

3. Utilice el nivel de advertencia /W4 en las compilaciones de depuración para obtener la mayor información de error del compilador, por ejemplo, si (i = 0) generará una advertencia /W4. No ignores estas advertencias, normalmente son causadas por errores en el programa. Pero a veces /W4 trae mucha información redundante, como advertencias de parámetros de funciones no utilizadas, y muchos controladores de mensajes ignoran ciertos parámetros. Podemos usar

#progma advertencia(disable: 4702)

//disable

//...

#progma advertencia (predeterminado: 4702)

//Vuelva a permitir para desactivar temporalmente la advertencia, o use

#progma advertencia(push, 3)

// Establecer la advertencia El nivel es /W3

/...

#progma advertencia(pop)

// Restablecer a /W4

Para cambiar temporalmente el nivel de advertencia, a veces puede usar /W4 solo para partes del código que considere sospechosas.

4. Al agregar símbolos de depuración, también puede depurar la versión de lanzamiento como una versión de depuración. En Proyecto/Configuración...Proyecto/Configuración..., seleccione Configuración para "Versión Win32", seleccione la pestaña C/C++, seleccione "General" para "Categoría", seleccione "Programa" para "Base de datos de información de depuración" y luego agregue "/OPT:REF" (sin comillas) al final de la pestaña Enlaces de Opciones del proyecto. Esto permitirá que el depurador utilice símbolos de depuración en el archivo pdb.

Pero al depurar, encontrará que los puntos de interrupción son difíciles de establecer y las variables son difíciles de encontrar. Hemos optimizado estos problemas. Afortunadamente, la ventana de la pila de llamadas todavía funciona bien y, aunque el puntero del marco se ha optimizado, todavía se puede encontrar la información de la pila (especialmente la dirección del remitente). Esto es útil para encontrar errores.