[Repost] Writing easy debugging VC code

zhaozj2021-02-12  188

Write easy-to-debug-up VC code

A program design

To avoid errors, first start with a good design. For program design, consider two features of the program:

1 Simply

Most common errors are derived from unnecessary complex ingredients in programming. A good design should reflect the requirements of the problem itself, without having to add unnecessary features to deliberately pursue "satisfying future needs". In fact, simple and elegant design causing future needs than those complex design.

2 coupling

Decoupling sexuality is used to measure the dependence between different objects. Loose coupling procedures are easy to understand and implement, easy to test and maintain, and this program contains miscible, and error is also easier to discover and clear.

Two-programming style

Programming style is personal problems, with great randomness. A good programming style not only makes the code easy to understand, but also easy to debug. Good programming style includes:

1 clearly write code

If there is no need, try not to use the advanced features in the language because these features are not easy to understand and debug. Writing code is not easy to make mistakes using most of the programmers understand the language ingredients and easy to understand and maintain.

2 Writing a structure good code

The most basic debug information that can be obtained when the program crashes is the source code file, the line number and a call stack in the line. The calling stack is the most helpful part of the debugger, which provides the context that appears, which is a function call sequence with parameters. The better the code structure you write, the more information you can give you.

3 use good identifier

A good name can make your code more easily understood and maintained. The popular Hungarian NOTATION actually combines the meaning of the identifier and the expression method. Now, Hungarian nomenclature has a lot of limitations. The Hungarian nomenclature is too valued. The expression of a variable is incomplete. It is actually not delivered to how much useful information, which makes code difficult to read, difficult to maintain. A good named tradition is a scope of the variable to check its definition when needed, and clearly indicate that a variable is a global, partial or member data. Dependency variables are more useful and reliable than dependent Hungarian prefix.

A good name can generalize the meaning of the entity represented by the identifier in a usual language. When selecting classes, functions, and variables, you can consider the following principles:

Take a simple descriptive name, a good name briefly summarizes the meaning of this identifier representative.

Avoid shorthabing, short-handed identifier is difficult to read and memorize, try to use a complete word of mixed case.

Avoid the text of the similarity to avoid confusion.

Avoid adopting general or random names, but should use the actual name. To take a bitmap button from the button class, take a cbitmapbutton instead of CMYButton.

4 use simple statement line

In the VC, one line can write multiple statements. But debugging is facing, too complicated, difficult to debug. Therefore, from the perspective of debugging, each statement should be in a row.

5 use unified arrangement

The unified arrangement makes the definition and statement of the class, variables more obvious.

6 Clear writing with parentheses

You don't necessarily remember the priority and combination law of various operators, and use excess parentheses does not affect the compiled code. So if you can't determine if you need parentheses, add it.

7 use good notes

Using a good comment allows your code to be incapable, and it is easy to read, easy to understand and maintain.

I should pay attention to the problem when writing programs

1 Take advantage of VC features

The following techniques can be used to take advantage of the characteristics of the VC compiler:

(1) Use const instead #define to create constants;

(2) Use enum instead #define to create a constant collection;

(3) instead of #define with the inline function;

These three techniques use C instead of C pre-pre-processed. The problem with pretreatment is that the compiler does not know if the compiler does something made by the preparation, so it cannot be used to check the errors and inconsistent places. The pre-processed name is not in the symbol table, so it cannot be used to check the pretreatment constant. Similarly, the preprocessing macro is compiled and cannot be tracked with debug tools. The compiler fully understands the const, enum, and inline statements, so that the problem will be warned when compiling. But preprocessing plays an important role in many debuggers. Debug code often needs to get different behaviors from non-debug code, and the most effective way is to let preprocesses create different code for debugging.

(4) Use new and delete instead of Malloc and Free;

In terms of creating objects, types of security and flexibility. Use new / delete better than Malloc / Free. In addition, New can be overloaded, providing greater flexibility.

(5) Instead of STDIO with an input and output stream (iostreams).

Use C input and output streams (<< and >>) without using C standard input and output libraries (PrintF / Sprintf and Scanf / SSCANF) are conducive to security and scalability. From the perspective of debugging, the biggest problem with standard input and output functions is that the compiler cannot detect any type detection on the control flow parameters, and any problem with the input and output stream can be detected at compile.

2 use header files

To declare all shared external symbols in the header file, and reserve the parameter name in the function prototype. Put all shared definitions in the header file, do not see the extern keyword in the .cpp file.

3 initialization variables

Be sure to initialize them before using variables. Use variables will definitely generate errors before initialization. You usually do not need to initialize the object, and the pair of data should be initialized in the constructor. It must be explicitly initialized for the arrays and data structures allocated in the stack and the stacks. For objects, each need to initialize the data should be initialized. Because the use of variables are checked by an optimizer, the local variables that are not initialized are detected, and the release version is better than the debug version.

4 use Boolean expressions

C Boolean type: Bool, value is True and False, size is one byte.

Windows programs typically use the BOOL type. Defined as follows:

Typedef int BOOL;

#define false 0

#define True 1

In C , a Boolean expression is false if it is 0, and others are true. Therefore, the Boolean expression should check if the counterfeit rather is true.

5 use handles and pointers

When you initialize a pointer, or let it point to an effective memory address, or set to 0 (empty pointer), avoid the pointer to the invalid address. Recycling this pointer when the object refers to the pointer, and processes it when the pointer is released before the pointer is released. The processing of the handle is the same as the pointer.

6 Use reference instead of pointer to do parameters

The parameters that use the pointer to function can pass an empty pointer, which is flexible, but it is also easy to forget to initialize the pointer. The reference is an alias of the object, it must be associated with a valid object, there is no reference to empty and no initialization. When you receive a reference parameter in a function, it is certain that this is a valid object. The quote for the program is more robust than the parameters for the pointer.

7 Mandatory Type Conversion (CAST)

When the mandatory type conversion of the data type, the corresponding constructor or conversion function will be called to create a new type of temporary object. The correct type conversion of the pointer eliminates a compilation error, but does not change the pointer. Forced type conversion destroys the functionality of the compiler for type inspection, which is the most effective mechanism for compilers to find errors. To ensure security, each forced type conversion requires manual type check. To try to avoid mandatory type conversion, you can: avoid using polymorphism data types; use a more wide base class; provide special access functions; let the compiler implicitly handled type conversion. 8 use constructor and destructor

The constructor needs to allocate memory, create a resource or open file, which is not always successful. The constructor does not return a value, and there is no way to display an error directly. A common method (used in many MFC classes) is to create object creation into two steps: first step, let constructor initialize the object in a way that will not be wrong; second step, let some initialization functions (such as INIT or OPEN is complete, this step may be wrong. Another method is to use an exception in the constructor: the first step is initialized in a way that does not have an error; the second step is to initialize the object with the code that may be erroneous in the TRY segment; the third step is processed in the CATCH code abnormal. If an exception occurs, the allocated resource will be cleared in the constructor and throw an exception again.

A key detail of exception handling is an exception thrown during the stack to terminate the entire application. When processing an exception, the destructive function is often called, so the destructive function is easily error, and must ensure that the exception of the destructive function is processed in the destructor. To ensure that the analytical function of the base class is a virtual function. In this way, even if the object is a pointer to the base class, the secting function of the derived class will also be called. Otherwise, resource leaks will be caused. Use debug speech in the VC program

In order to better debug program, you can use the following methods: use assertions, use tracking statements, use exceptions and return values.

First, assertion

1. Basic concept

As an assertion is a simple and effective technique that makes mistakes in the running of self-exposure. They help you find errors earlier, making the entire debugging process efficient.

As a Boolean trial statement, it is used to detect whether the value of a certain condition is always true when the program is running normally. It allows errors to expose in front of the programmer at runtime. The biggest advantage of using assertions is that you can discover errors in a place where the wrong origin is more resolved. As a result of the following characteristics:

The assertion is used to find the runtime error, and the discovered error is about the program implementation.

The Boolean expression in asserts displays an effectiveness of an object or state rather than the correctness.

As an assertion that exists only in the debug version after conditioning, not the release version.

As an assertion that cannot contain program code.

. As an assertion to provide information about the programmer rather than the user.

The most fundamental advantage of using assertions is to automatically discover the errors generated by many rules, but assertions cannot find all errors. As an assertion is the effectiveness of the program, not the correctness, and the error can be limited to a limited range by assertion. When assertion is fake, activation When the debugger displays the error code, you can use the call stack command to check the context, a small amount of related parameters, and the contents of the DEBUG table in the stack, usually, the cause of the assertion failed. _ASSERTE Macro (belonging to C running time library) can also display a failure assertion when asserting fails. Let's discuss the assertions in the MFC library.

2, the assertion in the MFC library

(1) ASSERT (Boolean expression)

It is best to choose an ASSERT macro when using MFC. It has the advantage that the assertion failure message box can be displayed even if a WM_QUIT message appears.

(2) Verify (Boolean expression)

The Boolean expression in the Verify macro is retained in the release version. The Verify macro simplifies the check of the function return value, generally used to check the return value of the Windows API. Because the Boolean expression in Verify is reserved in the release version, it is best not to use this macro to implement the full separation of the program code and debug code. (3) Assert_Valid (pointing to a pointer to the COBJECT derived class object)

Assert_Valid macros determine if the key of the COBJECT derived class object is valid by calling an overloaded AssertValid function. No matter when you get an object from the COBJECT derived class, you should call the Assert_Valid macro before doing anything to do this object.

(4) Assert_kindof (class name, pointing to the pointer of the COBJECT derived class)

This macro is used to verify that the pointer to the COBJECT derived class object is derived from a special class, call the assert_valid macro before calling it. It is only available in a very special occasion, such as the object type problem that the compiler may miss.

In addition, there are two species of Assert macros without official files: assert_pointer, assert_null_or_pointer (pointer, pointer type).

3. When is it as an assertion?

As a simple manufacturing fence, this fence can expose when it passes itself.

Check the input of the function

Check the output of the function

Check the current state of the object

Adhere to the rationality and consistency of logical variables

Check the invariant in the class

The public member function requires a more comprehensive assertion than the private and protected member functions.

Use the assertion inappropriately causes errors. As an assertion should detect a state that will never appear when the program is running normally. As an assertion is used to reveal the wrong, not to correct the runtime error.

4, assertion and defensive programming (Defensive Programming)

As an assertion reveals the runtime error (in the debug version) when debugging, and the defensive programming user can continue to work when an unexpected situation occurs when an unexpected situation occurs. In fact, the defensive programming requires the program to return a "secure" value when detecting accidents (such as the Boolean function returns false, pointer and handle return null value), an error code or throw an exception to solve the problem. Specific defensive programming techniques include: processing invalid function parameters and data, the program failed, check the error code returned by the critical function, and the processing exception. Standards that require defensive programming include: erroneous input data, memory, or hard disk space is not enough, can not open a file, the external device can not access, the network connection is not or even in the program, the purpose is to maintain the operation of the program . If your program is defensive, don't forget to use assertions. If you use the assertion, don't forget the defensive programming. These two techniques are preferably used together.

Second, tracking statements

1. Basic concept

Tracking statements (Trace Statements) enables the program to perform the programmer to view the variable values. They provide a program for observation and independently of an interactive debugger, but most features are commonly used to supplement the information provided by the debugger. In VC, tracking messages typically output the Debug tag in the output window, or re-output it into a file. The characteristics of the tracking statement are as follows:

Tracking statements are used to report important running events in the code.

The compilation of the tracking statement is usually conditional and only exists in the debug version, and is not compiled in the publish version.

Tracking statements cannot include program code or have indirect effects on program code.

The purpose of the tracking statement is to provide information to the programmer instead of to the user.

The tracking statement is also a debug statement, which can execute the program, and programmers can view the variables in the run. Tracking statements are very effective for programs that use interactive debuggers.

The difference between tracking statements and assertions is as follows:

The tracking statement is unconditional, assertion is a conditional Boolean statement. The tracking statement is used to display the program execution and variable values, not directly displaying bugs, asserts to show BUG.

The tracking statement outputs the information to the debug window or file, which can be arbitrarily ignored, asserting the execution of the interrupt program.

2, tracking statement in the MFC

In MFC, you can use Trace and AFXOutputDebugstring macros, COBject :: Dump virtual functions and AFXDUMPSTACK functions. The Trace Macro is implemented by AFXDUMP, and AFXDUMP is implemented by AFXOUTPUTDEBUGSTRING. AfxoutPutDebugstring Macro and AFXDUMPSTACK functions can be compiled in all versions, others can only be compiled in debug versions.

(1) Trace macro has the following form:

_Trace (ReportType, Format);

_Trace0 (ReportType, Format, Arg1);

_Trace1 (ReportType, Format, Arg1, Arg2);

_Trace2 (ReportType, Format, Arg1, Arg2, Arg3);

_Trace3 (ReportType, Format, Arg1, Arg2, Arg3, Arg4);

In the MFC, it is recommended to use Tracen Macros. When using Trace macro, you need to use the _T macro to format parameters to properly resolve Unicode corrections, while Tracen does not need.

One disadvantage in the MFC Trace macro is that the AFXTRACE function uses a 512-character fixed size buffer, which makes it useless when tracking the long string.

(2) COBject :: DUMP

The COBJECT class has a dump (DUMP) virtual function, and all the classes of the COBJECT can output their values ​​by overloading this function.

3, Visual C message PRAGMA

Message PRAGMA is actually a compiletable tracking statement, you can use it to warn the potential editing (Build) issues found during the pre-processing. Classic example:

#if (WinVer> = 0x0500)

#pragma Message ("NOTE: WINVER HAEN Defined as 0x0500 or Greater.")

#ENDIF

Message PRAGMA is very useful, especially in complex. However, if you want to detect a specific problem, not a potential problem, use #error pretreatment instead of interrupting compiles more directly.

Whenever you have errors in your program and you want more information, you should check the tracking message. Since the buffer of the VC output window is limited, if the speed generated by the tracking message data exceeds the speed of the output window processing, the message will be filled with the buffer, resulting in data loss. A simple way to avoid this problem is to call the Sleep API function when the code segment of the output of a large amount of data is output.

Three, abnormal

1. Basic concept

The error is a condition where additional processing is not performed, and the thread does not perform properly. An exception is used to handle errors. One obvious advantage of using an abnormality is that they can make the program code and the error handling code by issuing an error signal, and will not let the program ignore the error, and you don't have to keep check the return value of the function, so they simplify the program code . Another benefit is that they don't need a strict programming style.

Abnormal basic characteristics:

Abnormal is based on each process and processes.

Abnormal cannot be ignored by the thread, must be processed.

Unprocessed anomalies will end the process, not just end threads.

Abnormally comes to release all stack objects when the stack is released, avoiding the vulnerability of resources.

Abnormal processing requires a lot of additional operations, making it unsuitable for frequently running code.

Can throw any type of abnormal object, except for integers. If it is performed correctly, the exception handling has the following features:

Is an exception not a normal operation result, it is a special case.

The exception is used in the case where the return value is invalid.

Violent is reliable and cannot be ignored.

Abnormally simplifies the error handling, simplifies the program code, making the error handling more convenient.

By default by Visual C , the exception is handled in the debug version, and the release is not processed in the release version. Since the exception is also an error, the Windows exception code uses a bit mapping mode as the Windows error code, which is a 32-bit value, which is defined by Microsoft, and the maximum four digits of any abnormal code are always 1100 (binary), ie 16 The 0xc in the encyclopedia.

2, Windows structural exceptions and C abnormalities

Windows structural exceptions are thrown as a hardware abnormality (such as access illegal or zero-separated) or operating system abnormal results, C exception can only be thrown by the Throw statement. Windows structural exception handling cannot handle the resolution of the object, so you should use C exceptions in the C program. However, C abnormal cannot handle hardware and operating system exceptions, and your program needs to convert structural abnormalities to C exceptions. C exceptions do not throw from your program code but throw from the C runtime, so you need to call the stack window to return your code. In order to properly handle hardware and operating system exceptions, you can create your own anomaly class and use the _SET_SE_TRANSLATOR function to install a structure exception to C exceptions, but do not capture the structural exceptions that cannot be recovered.

3, the abnormality in the MFC

In the MFC, all anomaly object is derived from the CEXCeption base class (which has a very convenient GetErrorMessage and ReportError member functions). Most MFC abnormalities are dynamically allocated, and when they are captured, they must be deleted, and the MFC exceptions that are not captured are captured and deleted in the AFXCallWndProc function.

4, abnormal overhead

When the C exception is thrown, the function call chain will go backtrack the search and find a processor that can handle such exceptions. If you didn't find it, the process ended. If found, the call stack will be released, all automatic (local) variables will also be released, and the stack will be organized as the context-related device of the exception processor. Therefore, exception overhead is an automatic variable table for an exception processor directory and an active auto variable (it requires additional code, memory, and whether it is thrown, it will run), and have to add a search and automatic variable of the function call chain. The adjustment of the stack (it only needs to be performed when throwing an exception).

5, abnormal strategy

(1) Throw a timing

The timing of throwing an exception should be a function to find an error. If there is no special operation, the error can prevent the program from running normally, and this operation is not completed, or when the function is impossible to return value.

Use exception handling easier, more reliable, more efficient, can create a more robust code. However, it should only use an abnormal handling in an accident. If you think a pointer should be null, this value is checked directly in this condition without using an exception.

(2) When will he capture?

For this issue, there are some possible standards:

When a function knows how to handle this exception.

When this function can reasonably handle this exception and advanced functions do not know how to handle it.

When throwing an exception may cause the process to crash.

When a function can continue to execute its task. When you need to organize allocation resources.

A disadvantage of exception handling is that it may lead to the leakage of resources. Therefore, preventing resource leaks should be part of the maintenance process is safe. The stack is released automatically, and the local variable is automatically sorted, but does not include dynamically allocated variables. You can use a smart (SMART) pointer to protect your code without resource leakage without abnormalities.

(3) How to capture

Non-MFC C abnormalities should be captured by reference. Using reference capture exceptions do not need to delete an exception object (because the exceptions that use reference captures will be transmitted in the stack), and it retains polymorphism (so you captured the exception object is the exception object you thrown).

. MFC abnormality should be captured by a pointer. Use pointer to capture exceptions require you to delete objects. Because they are usually allocated from the heap, when you process anomalies, you need to call the DELETE member function to delete. You can't use the omission capture processor to capture the MFC exception, which will cause a memory leak. You must use the DELETE member function to delete the MFC exception without delete because some MFC exceptions are created for static objects.

Throw an exception during the release of the stack causes the process to terminate the process. The release stack involves calling the destructor, an exception can prevent calling the Delete operator, which will have resource leaks, so the exception is preferably not thrown from the destructive function. If you want to throw an abnormality in the destructive function, you must handle it to avoid resource leakage.

6, abnormal and defensive programming

Continue to execute procedures during an abnormality, which is important than performing a normal shutdown action. If possible, you should focus on continuing programs and close the program normally in a must-have. Maybe the most fundamental normal shutdown is a process that can restart your own when you crash, which is a technology used by the Windows Explorer.

If a C exception associated with an error is expected, if it occurs in the code of the non-critical, if it does not occur in the result of the program startup or end or an unrecoverable structure, this program can be Restore it from it.

Once your program can recover from an error related to the error, you should check the status of the program and its documentation. If the program and document have been destroyed, the process should also be terminated. Otherwise, the program needs to notify the client to determine the process of action. If the client agrees to execute, the program should recover errors and proceed.

Fourth, return value

Not in the case, the exception can be used, as in the use of Windows API programming or with COM programming, it is not used. When an exception is not suitable, use the return value is a good way.

Basic features of the return value:

The return value can indicate that the normal and abnormal functions are running, but the thread will continue to run.

The return value is easily ignored.

The return value is an integer in a typical case, typically maps that conforming to a predefined value.

The return value can transmit and receive efficiently.

Therefore, the return value is most suitable for use in the following:

. Status information for non-error

In most cases, it can be ignored freely without problems.

For more prone to errors in the cycle.

The error used in the intermediate language module, such as a COM component.

Use Visual C debugger debugging

First, debug version and release version

Sometimes the program can run in the debug version but cannot run on the release version, and it is possible. Generally speaking, a release version means some types of optimization, and a debug version is not optimized. Let's take a look at their differences:

1. Especially for the compilation options for debug versions

(1) / MDD, / MLD or / MTD

The debug version of the runtime library has debug symbols, using debug stacks, the purpose of the debug stack is to discover memory damage and memory leaks, and report the source code from which place to report the problem. characteristic:

The debug version of the runtime library is tracked for memory allocation, allowing users to check memory leaks.

Write the 0xcd byte mode in the just allocated memory, populate the originally assigned memory with 0xCD, helps discover errors that the data is not initialized. . In the released memory write 0xDD byte mode, helps discover the memory that has been released.

The four-byte protection data is allocated on both sides of the buffer, and is initialized with 0xFD byte mode to check the overflow and underflow of write memory.

The source code file name and line number are recorded in each memory allocation, which helps the user to position memory allocation in the source code.

(2) / OD

This option is used to turn off the optimization switch. Since the code that is not optimized directly corresponds to the source code, it is easier to read more than optimized code. Code compilation and links that are not optimized will be faster, there will be a shorter debug cycle. Due to optimization, the release version is not necessarily running more than the debug version, and the optimization code requires the compiler to do some hypothesis, remove redundancy, but sometimes this assumption is wrong, and the redundancy is also possible to hide errors. If the release version of the frame pointer (EBP register) omission (FPO) hides the error of the function prototype; in the synchronous exception mode (only by the throw statement, the compiler is default, the / gx compile option settings), exception The handler may be optimized, preventing the C exception processing code in the program, safely capturing the structure exception, in which case you must use asynchronous exception mode (taking any instructions, an exception mechanism, by / eHA Compile option settings ).

(3) / d "_debug"

Open the condition to compile the debug code switch. Only this symbol is defined, the debug code will be compiled, the MFC uses the _debug symbol to determine which version of the MFC class library is connected. In the debug version, inline defaults are turned off.

(4) / zi

Create a program database of Edit and Continue. This option opens / gf compile options, / GF compilation options will eliminate repeat strings and put strings in read-only memory. Editing Continuous Function The special information stored in the PDB file is obtained to make the code modification is valid for the debugger. If the information corresponding to the modified file is not in the PDB file, the editing continuation function cannot be performed, and the following prompt information will appear in any modification of the code during the debugging process "One or more Files Are Out of Date or Do Not Exist." .

(5) / gz

The debug version is used to find errors that those who have discovered in the release version. Its role is as follows:

Automatic (local) variables in 0xCC mode.

Check the stack pointer when modifying a function by a function pointer, confirming that there is a call rule does not match.

In the final inspection stack pointer is changed.

(6) / GM

Open minimize re-link switch, reduce link time.

2. Special for publishing options

(1) / MD, / ml or / MT

Use the release version of the runtime library.

(2) / O1 or / O2

Turn on the optimization switch so that the program will minimize or the speed will be the fastest, the optimizer may also discover potential errors in the code, and these errors may be built by the debug version.

(3) / d "ndebug"

Close condition compiles debug code switch.

(4) / GF

Eliminate repeat strings and put them in read-only memory to avoid being incorrectly modified.

(5) / zi

Create a program database that contains debug symbols.

If an error only occurs in the release, unless you are a compilation master, you need to debug symbols to prompt you what is wrong to the program, and debug symbols are saved in the program database file (PDB). Visual C AppWizard is not created for a debug symbol for the publishing version by default. To create a debug symbol, open the engineering setup dialog, select the win32 release, select the common class in the C / C tag, in debug information, if it is a release version Select Program Database, if it is a debug version Select Program Database for Edit and Continue ( Editing Continue options are incompatible with optimized links, not suitable for publishing. Select the Debug class in the Link tag, then select Debug INFO and Microsoft Format options, it is best not to select the Separate Types option, so all debug information will be merged into a separate PDB file. For published versions, select the LINK tab, the final plus "/ opt: ref" in the Project Options dialog, this option makes the function and data that are not referenced, and the data does not appear in the executable, avoiding the unnecessary increase of the file. . Do not use this option for the debug version, it will turn off the incremental link (INCREMENTAL LINKING). Second, the "Settings" menu of the Visual C editor

When you open or create a Workspace containing at least one project, the "settings ..." command in the Project menu of Visual C will be valid, select it, or press the hotkey Alt F7, you can adjust the project settings. Box, this option will affect the establishment and debugging process of the entire project, so it is important.

In this dialog, the drop-down list box above the left is used to select an engineering configuration, including Win32 Debug, Win32 Release, and All Configurations (together with the first two configurations), some options have different engineering configurations. The default value. The tree view on the left gives all the files and classification of the current project. Let's take a look at the respective functions and meaning of the four main tabs related to the work of Win32 Debug (a total of ten tabs):

1, General tab

This tab is relatively simple, and the first option from the upward is used to change the way using the MFC class library: DLL's way or static connection. We can switch between two ways. The second option is used to specify the memory of the intermediate file and output file generated during the compiled connection. For debug versions, the default directory is the "debug" subdirectory below. The third option is used to specify whether each engineering configuration has its own document dependencies (main point files), because the most engineering debug version and publish version have the same file dependencies, so there is usually no need to change This option is.

2, Debug tab

The DEBUG tab is some options related to debugging. Since the options are more, they are divided into several classes, we can select different categories from Category, and the tab will switch to display the appropriate options.

In the General category, you can specify the executable file name to debug. The other three options can specify the working directory for debugging, start debugging command line parameters sent by the program, and the path to executable files during remote debugging.

3, C / C tab

The C / C tab controls the compiler of Visual C , with more options. There is a Project Options edit box below, and the various command switches listed will be transferred to the Visual C compiler when starting compiling. These command switches will change with other options. In the General category, WARNING LEVEL is used to specify the level of the compiler to display the warning. If WARNINGS AS ERRORS is selected, then each warning that is displayed will cause an error, so that the connector cannot be activated after compiling. . Optimizations is used to set up code optimization. Optimization is mainly two types of running speed and reduce program volume, but sometimes these two goals are contradictory. In addition, in a very small number of cases, the program can operate normally, and the program will have some inexplicable issues after the optimization measures are opened. In fact, this is mostly a potential error in the program, and the optimization measures are often only temporarily solved. DEBUG INFO is used to specify the type of debug information generated by the compiler. In order to use Visual C ie, that is, you must select the debug information of the "Program Database for Edit and Continue" type. Preprocessor definitions is some pre-defined macro names.

The options in the C Language category involve some advanced features of the C language, including representations, abnormality processing, runtime type information, generally do not have to change them. The options in the Code Generation category involve how to generate target code, in general, keep the default value. In the Customize category, the meaning of the top six options is: whether to use Microsoft to the C extension; whether the function level is allowed; whether the duplicate string is eliminated; whether the minimized reconstruction is allowed; Increment compilation; whether the compiler is allowed to output its own version information to the Output window when the compiler is started.

In the Listing Files category, we can specify the compiler to generate a browsing information and list file (Listing file), the former can generate a browsing information file by the browsing information maintenance tool Bscmake, the latter contains the C / C source file after compiling the compilation instruction. The Optimizations category allows us to make more submissive control over optimization, select which item is optimized, in Inline Function Expansion we can specify how the inline function is extended. The PrecompileD Headers category is about some options for the pre-translated header file, and no change is used in general. The Preprocessor category is about some options for pre-processing.

4, LINK tab

The LINK tab controls the Visual C connector. In the General category, you can specify the file name of the output, and some additional library files or target files that need to be used during the connection, the meaning of the five options is: generate debugging information; ignore all default library files; Allows increment connection (this way can speed up the speed of the connection); generate a MAP file; allowing performance analysis. Selected User Program Database in Customize Allows the User Database. In the Debug category, we can specify the classes of the debugging information, or the Coff format, or both, or both, the connector will be subsequently put on the PDB file in the PDB file, which is connected to the PDB file in the PDB file. Some, but the speed of debugging will be slow. Input categories are some options related to the input library file, we can specify or do not use certain library files or target files here. In the Output category, some options related to the final output executable, generally do not change. Third, Visual C debugging tool

1, debug window

(1) Observation window (Watch)

When debugging the program, you can monitor variables and expressions using the observation window.

(2) Quick watch (Quick Watch)

The function and observation window are similar.

(3) Variable window (variables)

The variable window has three tags: Auto tags show the current statement and the previous statement, the local variable displays the local variable of the current function, and the THIS label displays the object executed by the THIS pointer.

(4) Register window (Register)

You can monitor registers, flag values, and floating-point stacks of CPUs.

(5) Memory window (Memory)

Virtual memory starting from a particular address can be displayed. The AddResS box allows you to specify which virtual memory address starts to display.

(6) Call Stack Window (Call Stack)

A range of functions that causing the current source code statement execution can be displayed, and the current function is at the top of the stack.

(7) Disassembly window (Disassembly)

You can view compilers generated by the compiler corresponding to the source code.

2, debug symbol

The program database file (.pdb) contains debugging information and program information required for the Visual C debugger. The debugging information contains the names and types of variables, function prototypes, source code line numbers, classes, and structural layouts, FPO debugging information (reconstructing stack frames), and information needed to increment links. For programs set up the Program Database for Edit and Continue option, the PDB also contains the information you need to perform editing proceeds.

3, use breakpoints

BreakPoint is a mechanism that runs you to describe the debugger and let the debugger set a mechanism. If there is no breakpoint, only one step in the program uses the debugger. In Visual C , you can set three types of breakpoints: code positioning breakpoint, data breakpoint, and message breakpoint.

Fourth, improve the error of the debugger

Try to check the compile time instead of the runtime check.

1. Use the highest compilation warning level / w4

IF (x = 2) This statement, the default warning level does not display any information, but it will appear "WANING C4706: Assignment WITHIN CONDitional Expression" when it is changed to the highest warning level / w4. / W4 gives some warnings that some / W3 can't give.

2. Use the / gz compile option in the debug version

The / gz option is used to find errors that are discovered in the release, including automatic (local) variables that are not initialized, stack errors, incorrect function prototypes. 3, use the #pragma warning compiler

You can use the #pragma warning compiler to prohibit the entire program, a specific header file, a specific code file, or a specific warning of a specific row code, which looks to where you put #pragma.

4, use the compilation rule / wx without warning

This compilation option treats all warnings as an error, only after the false warning is eliminated. Sometimes compiling warnings may be reasonable, and the core of processing compilation warning is to discover errors, rather than suppressing warnings themselves. This law is very helpful for big procedures development groups. The ultimate goal is to eliminate errors, not to eliminate warnings.

V. Memory space and allocation

1, memory allocation error

Dynamic memory allocation errors have two basic types: memory error and memory leakage.

(1) Memory error

When a pointer or the memory cell pointing to the pointer becomes an invalid unit, or when the data structure allocated in the memory is destroyed, the memory error is caused. The pointer is not initialized, the pointer is initialized to an invalid address, the pointer is unclearly modified, and the pointer is used after the memory area associated with the pointer (this pointer is called a dangling "pointer), these It will make the pointer into an invalid pointer. When written by an error pointer or a virtual resuscitation, or converting the pointer to a non-match data structure, or written the data, the memory itself is destroyed. Delete the uninitial pointer, delete the non-heap pointer, delete the same pointer multiple times, or covers the internal data structure of a pointer, will cause memory allocation system errors.

(2) Memory leakage

Memory leakage is generated when the memory is allocated. There are many situations that cause memory leaks, such as release memory in all execution paths of the program, not release all memory in the destructor. The longer running before the crash is collapsed, the larger the cause of the crash and the relationship between memory leakage.

Windows reclaims the leak memory at the end of the program, so memory leak is a temporary issue. But why must I eliminate memory leaks? First, memory leaks often lead to leakage of system resources. Dynamic allocation of memory often represents a storage area, but also represents certain types of system resources, such as files, windows, device contexts, GDI objects, and more. Second, high quality procedures and specific server programs must be able to run unlimited. Finally, memory leaks are often signs of other programs or bad programming habits.

Causes leading to internal ginseng leaks: forget to release memory; constructor failed; there is a destructive function for memory leakage; there is an exception handler for memory leaks; multiple returns; use the error form.

2, about memory initialization

In the debug version, the memory that is not initialized in the bunch is populated by 0xcd byte mode, and the memory released in the pile is filled with 0xDD byte mode. The memory in the stack is initialized by the 0xcc byte mode. In the debug version and the release version, the global memory that is not initialized is initialized to 0.

3, memory virtual address space

Windows uses a set of fixed ranges to divide the 4GB virtual address space of the process, so it is sometimes determined whether the pointer is valid by viewing the return value of the pointer.

(1) WINDOWS2000 virtual address space division

0 ~ 0xffffff (64KB): Cannot be used to detect empty pools (access conflicts)

0x10000 (64KB) ~ 0x7ffeffff (2GB-64KB): Win32 process private (non-reserved), used for program code and data

0x7fff0000 (2GB-64KB) ~ 0x7ffffff (2GB): Cannot be used to prevent overwriting OS partition (access conflicts) 0x800000000 (2GB) ~ 0xffffffff (4GB): Reserved for operating system, irrevacity (access conflict)

(2) Windows2000 virtual address space usage

0x00030000 ~ 0x0012FFFF: Thread Stack

0x00130000 ~ 0x003FFFF: Pile (sometimes heap here)

0x00400000 ~ 0x005FFFF: Executive code

0x00600000 ~ 0x0fffffffffffffff: Heap (sometimes you are here)

0x10000000 ~ 0x5ffffff: App DLLS, MSVCRT.DLL, MFC42.DLL

0x77000000 ~ 0xfffffffff: Advapi32.dll, ComctL32.dll, GDi32.dll, kernel32.dll, ntdll.dll, rpcrt4.dll, shell32.dll, user32.dll

Among them, 0x00400000 is the minimum base address that all versions of Windows can use.

Six, some debugging technology

1, debugging cycle

Use the break command under the Debug menu. In Windows2000, if the program has an input request, you can use the F12 key interrupt program, then check the window's call stack, or a single step tracking code to find the cause of the dead cycle.

2, with spy debugging and message issues

The best solution for debug messages is the SPY tool provided by Visual C . SPY allows programmers to view windows, messages, and threads. SPY default message output: The first column is displayed. The second column displays the handle that accepts the message. The "S" indication message in the third column is sent by sendMessage, "P" representative message is sent by PostMessage, "R" is the return value of the message handle. The fourth column gives a decoded message name, message parameter, or return value.

3, unconventional method

(1) Reparators your app

When your program shows an exception or unexpected behavior, or the Visual C compiler fails because a internal compiler error fails, it is best to delete the debug or release folder in the project, and re-edited from the beginning.

(2) Restart Visual C

Visual C has superior capabilities, but some features of the compiler also cause strange errors. If your program is very strange, you try to clear all breakpoints, close or hide the observation window, check the engineering setup dialog box to see what modifications have been made until you restart Visual C to eliminate the Visual C environment Abnormal behavior.

(3) Restart Windows

When you find that Windows or other programs exhibit an exception or unexpected behavior, you should restart Windows to eliminate interference from the operating system to debug.

(Finish)

[references]

EVERETT N.MCKAY, MIKE WOODRING, He Jianhui, Xu Jun, Dong Wei translated. Debugging Windows Programs (Windows Program Debugging). Beijing: China Electric Press. 2002

postscript:

转载请注明原文地址:https://www.9cbs.com/read-7621.html

New Post(0)