[Windows] New in debug and release versions. Differences between the two modes

Source: Internet
Author: User

During the interview today, I was asked a question:

In the release version, a new object, A, is passed into the debug version library, and an error occurs? What are the possible causes?

In debug mode:

1: // new in debug mode
   2:  #define new DEBUG_NEW
   3:   
4: // debug_new:
   5:  #define DEBUG_NEW new(THIS_FILE, __LINE__)
   6:   
7: // for the new above, the compiler will look for the operator new defined below
   8:  void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine) {
   9:   return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
  10:  }
  11:   
12: //: Operator new is defined as follows:
  13:  void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine) {
  14:   …
15: presult = _ malloc_dbg (nsize, ntype, lpszfilename, nline); // actual memory allocation
  16:   if (pResult != NULL)
  17:    return pResult;
  18:   …
  19:  }
  20: 

In release mode:

1: // new in release Mode
   2:  #define new DEBUG_NEW
   3:   
4: // debug_new extension:
   5:  #define DEBUG_NEW new 
   6:   
7: // the new version of the call is
   8:  void* __cdecl operator new(size_t nSize) {  
   9:    … 
10: presult = malloc (nsize); // actual memory allocation
  11:   return pResult ; 
  12:  }

Of course, the corresponding delete operations are also different. Obviously, the final action of New in debug mode and release mode is different. When applying for data in debug mode, there will be some tracing information, file names, number of lines, and so on, that is to say, the memory actually allocated during memory application is larger than the applied memory. If an object applied in release mode is imported into the library of the debug version, an error may occur if the delete operation is called in the library of the debug version.

The answer to this question is actually largely dependent on the actual code experience. In fact, I came back and thought about the difference between the debug and release modes. Here I will summarize:

The debug version includes debugging information, so it is much larger than the release version (which may be several hundred kb to several Mb ). As to whether DLL support is required, we mainly look at the compilation options you use. If it is based on ATL, debug and release have similar requirements on DLL. If you use the dynamic MFC Library as the compilation option, you need to support mfc42d. dll and other libraries. The release version requires the support of mfc42.dll. Release build does not debug the source code without considering the MFC diagnostic macro. It uses the MFC release library to optimize the application speed during compilation, while the debug build is the opposite, it allows debugging of source code, defines and uses the diagnostic macro of MFC, and uses the debug library of MFC, which does not optimize the speed. Instead, a lot of debugging information is added.

1. essential differences between debug and release versions 

A debug version is usually called a debug version. It contains debugging information without any optimization, so that programmers can debug programs easily. The release version is called a release version. It is often optimized to optimize the code size and running speed, so that users can use it well.

In the VC compiler, the true secret of the debug and release versions lies in a set of compilation options. The following lists some of the main options for the two.
Debug version:

MDD,/MLD, or/MTD Debug Runtime Library (runtime function library of the debug version)
/OD Turn off the optimization Switch
/D "_ debug" Equivalent to # DEFINE _ Debug. Enable the compilation and debugging code switch (mainly for the assert function)
/Zi Create the edit and continue database, so that you do not need to re-compile if the source code is modified during debugging.
/GZ Helps capture Memory Errors
/GM Turn on the minimize reconnection switch to reduce the link time

Release Version:

/MD,/ml or/mt Use the runtime function library of the released version
/O1 or/O2 Optimize the switch to minimize or minimize the program
/D "ndebug" Turn off the Conditional compilation and debugging code switch (that is, do not compile the assert function)
/GF Merges duplicate strings and puts string constants in read-only memory to prevent modification.

In fact, there is no essential limit between the debug version and the release version. They are just a set of compilation options, and the compiler only acts according to the predefined options. In fact, we can even modify these options to get optimized debugging versions or release versions with trace statements.

2. under which circumstances will the release version go wrong?

1). Runtime Library Problems

The linked runtime function library usually only affects the program performance. The Runtime Library of the debug version contains debugging information and uses some protection mechanisms to help identify errors. Therefore, the performance is inferior to that of the release version. The Runtime Library provided by the compiler is usually stable and does not cause release errors. However, the debug Runtime Library enhances error detection, such as heap memory allocation, sometimes there may be debug errors but release is normal. It should be pointed out that if the debug is wrong, even if the release is normal, the program must have a bug, but it may be that a certain run of the release version is not shown.

2). Optimization Problems

This is the main cause of the error, because the source program is basically translated directly when optimization is disabled, and the compiler will make a series of assumptions after optimization is enabled. There are several types of errors:

A: frame pointer (FPO ),

During the function call process, all call information (return address, parameters) and automatic variables are stored in the stack. If the declaration and implementation of a function are different (parameters, return values, and call methods), an error occurs;

However, in the debug mode, stack access is implemented through the address saved in the EBP register. If no errors such as array out-of-bounds (or not many out-of-bounds) occur, the function can normally be executed;

In relase mode, the optimization will omit the base address pointer of the EBP stack, so that accessing the stack through a global pointer will cause the returned address error to be a program crash.

The strong type feature of C ++ can check the majority of such errors, but if forced type conversion is used, it will not work. You can force the/Oy-compilation option in the release version to disable frame pointer omitting to determine whether such errors are possible.

For example:

An error occurred while writing the message response function of MFC. Correct should be

Afx_msg lresult onmessageown (wparam, lparam );

On_message macro contains forced type conversion.

One of the methods to prevent this error is to redefine the on_message macro and add the following code to stdafx. H (after # include "afxwin. H"). An error will be reported during compilation of the function prototype errors.
# UNDEF on_message
# Define on_message (message, memberfxn) {message, 0, 0, afxsig_lwl,

(Afx_pmsg) (afx_pmsgw) (static_cast <lresult (afx_msg_call cwnd: *) (wparam, lparam)> (& memberfxn )},

B: Volatile Variables

Volatile tells the compiler that the variable may be modified by unknown methods (such as systems, other processes, and threads) outside the program ). To improve program performance, the optimizer usually places some variables in registers (similar to the Register keyword ).

But the value in the Register remains unchanged. If your program is multi-threaded, or you find that the value of a variable is inconsistent with the expected value and you are sure that the setting is correct, you may encounter such a problem. This

Errors sometimes occur because the program has the fastest optimization error and the smallest optimization is normal. Add volatile to the variables you think are suspicious.

C: Variable Optimization

The optimizer optimizes the variables based on their usage.

For example, a function has an unused variable. In the debug version, it may mask an array out of bounds. In the release version, this variable may be optimized, in this case, the array out-of-bounds will destroy the useful data in the stack.

For example:

   1:  void   fn(void)   { 
   2:  int   i;   
   3:  i   =   1;   
   4:  int   a[4];   
   5:   
   6:  { 
   7:     int   j; 
   8:     j   =   1; 
   9:  }
  10:     
11: A [-1] = 1; // Of course, the error is not so obvious. For example, the subscript is a variable.
  12:  a[4]   =   1;   
  13:  } 

J has an out-of-range scope when the array is out of bounds, but its space is not reclaimed, So I and j will cover up the out-of-bounds. However, due to I, j has no major effect and may be optimized, so that the stack is damaged.

3. _ debug and ndebug

When _ debug is defined, the assert () function is compiled, but ndebug function is not compiled. In addition, there are a series of assertion Macros in VC ++. This includes:

Ansi c assertions Void assert (INT expression );
C Runtime lib assertions _ Asserte (booleanexpression); _ assert (booleanexpression );
MFC assertions Assert (booleanexpression); Verify (booleanexpression); assert_valid (pobject); assert_kindof (classname, pobject );
ATL assertions Atlassert (booleanexpression );

In addition, the compilation of the trace () macro is also controlled by _ debug.
All these assertions are compiled only in the debug version, but are ignored in the release version. The only exception is verify (). In fact, these macros call the assert () function, but attach some library-related debugging code. If you add any program code to these macros, instead of a Boolean expression (for example, a value assignment or a function call that can change the value of a variable), the release version will not perform these operations, this may cause errors. It is easy for beginners to make such mistakes, and the search method is also very simple, because these macros are listed above, you only need to use the find in files function of VC ++ to find the place where these macros are used in all files of the project and then check them one by one. In addition, some experts may also add Conditional compilation such as # ifdef _ Debug. Pay attention to it.
By the way, it is worth mentioning that the verify () macro allows you to put program code in a Boolean expression. This macro is usually used to check the return values of Windows APIs. Some people may abuse verify () For this reason. In fact, this is dangerous because verify () violates the idea of assertions and cannot completely separate program code from debugging code, in the end, it may cause a lot of trouble. Therefore, experts suggest using this macro as little as possible.

4. Variable Initialization
The operations performed by debug and release when initializing variables are different. debug assigns each byte to 0xcc, the value assignment of release is similar to random (I think it is allocated directly from the memory and has not been initialized ). This makes it clear that if a variable in your program is referenced when it is not initialized, it is likely that an exception occurs: using it as a control variable will lead to inconsistent process orientation; using it as an array subscript will cause the program to crash; it is more likely to cause other errors due to inaccuracy of other variables. So it is the easiest and most effective way to initialize a default value for a variable immediately after declaring it. Otherwise, there is no place to find it when the project is too large.

The debug version is initialized to 0xcc because 0xcc is an int 3 single-step interrupt command in x86, so that the program will stop when it encounters 0xcc, this is the same as the JMP 0000 statement that fills in useless code space during Single-Chip Microcomputer Programming.

5. Custom message parameters in MFC
When declaring the function body of a custom message, you will often see the following statement:

Afx_msg lresult onmessageown ();

Debug usually does not have any problems, but when you use message transmission in release and multithreading or between processes, it will lead to errors such as invalid handles. The direct cause of this error is that the message body parameter is not added, that is, it should be written:

Afx_msg lresult onmessageown (wparam, lparam );

6. There is no error in release mode, but an error is reported in debug mode.

In this case, most of them are caused by Incorrect code writing. You can view the source code of MFC and find many assert statements (assertions). This macro is only valid in debug mode, it is clear that the release version does not report errors because it ignores errors rather than no errors. This may pose a major risk because it is easier to debug in debug mode, check your code, and I will not talk about it more.

FAQs

1. Data Overflow

   1:  char buffer[10]; 
   2:  int counter; 
   3:  lstrcpy(buffer, "abcdefghik");

In the debug version, the buffer's null overwrites the counter's high position, but unless counter> 16 m, there is no problem. However, in the release version, counter may be placed in the register, so that null overwrites the space in the buffer, which may be the return address of the function, leading to access error.

2. The memory allocation methods for debug and release versions are different. 

If you apply for 6 * sizeof (DWORD) = 24 bytes for ele in debug, you are actually allocated 32 bytes (32bytes for Debug ), in the release version, 24 bytes are allocated to you (8 bytes for the release version). Therefore, if you write ele [6] In the debug version, there may be no problems, in the release version, access violate exists.

3. Assert will not be compiled in the release version.

The assert macro is defined in this way.

   1:  #ifdef _DEBUG 
   2:  #define ASSERT(x) if( (x) == 0) report_assert_failure() 
   3:  #else 
   4:  #define ASSERT(x) 
   5:  #endif

Actually complicated, but irrelevant.

   1:  ASSERT(pNewObj = new CMyClass); 
   2:  pNewObj->MyFunction();

In this case, pnewobj in the release version is not allocated space. Therefore, when executing the next statement, the program reports that the program has performed illegal operations. In this case, you can use verify:

   1:  #ifdef _DEBUG        
   2:  #define VERIFY(x) if( (x) == 0) report_assert_failure()          
   3:  #else          
   4:  #define VERIFY(x) (x)          
   5:  #endif
 

In this way, the code can be executed in the release version.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.