Unusual memory bit patterns

219 篇文章 ¥19.90 ¥99.00
本文详细介绍了在Windows C/C++环境中,使用Visual Studio等编译器时,调试构建中常见的内存位模式,如0xcccccccc、0xbaadf00d、0xdeadbeef等,这些模式与内存分配、初始化、释放有关。出现这些位模式通常表示内存错误,如未初始化的栈内存、已分配但未初始化的堆内存、已释放的内存等。防止这类问题的方法包括正确初始化变量、使用匹配的内存分配与释放函数、在释放后将指针设为NULL等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Unusual memory bit patterns

Software development is an intellectual challenge. Sometimes the process is interrupted by software failures and/or crashes. A lot of the time the reason for the failure is self evident and easily fixable. However the reason for some crashes is less obvious and often the only clue is an unusual bit pattern that is typically present each time the crash happens. This article will describe some of the common bit patterns that have been used in Windows C/C++ software.

All of these bit patterns apply for the Microsoft C/C++ compiler that ships with Visual Studio (6.0 through 2010) and any compatible compilers (such as the Intel compiler). These bit patterns are found in debug builds. Release builds do not use special bit patterns.

These bit patterns evolve and change as the C runtime internals change from managing memory themselves to handing that management over to the Win32 heap family functions HeapAlloc(), etc.

Some of the allocators appear to know if they are running with a debugger present and change their behaviour so that with a debugger present you will see the bit patterns, and without the debugger present you will not see the bit patterns. HeapAlloc(), LocalAlloc(), GlobalAlloc() and CoTaskMemAlloc() both exhibit this behaviour.

0xcccccccc

The 0xcccccccc bit pattern is used to initialise memory in data that is on the stack.

void uninitFunc_Var()
{
	int	r;

	// here, r has the value 0xcccccccc

	...
}
Also, consider this C++ class.
class testClass
{
public:
	testClass();

	DWORD getValue1();

	DWORD getValue2();

private:
	DWORD	value1;
	DWORD	value2;
};

testClass::testClass()
{
	value1 = 0x12345678;

	// whoops!, forgot to initialise "value2"
}

DWORD testClass::getValue1()
{
	return value1;
}

DWORD testClass::getValue2()
{
	return value2;
}
When an object of type testClass is created on the stack, its data member "value2" is not initialised. However the debug C runtime initialised the stack contents to 0xcccccccc when the stack frame was created. Thus if the object is used, its data member "value2" will have a value 0xcccccccc, and "value1" will have a value of 0x12345678.
void uninitFunc_Object()
{
	testClass	tc;

	// here, tc.value1 has the value 0x12345678
	// here, tc.value2 has the value 0xcccccccc	because it was erroneously not initialised in the constructor

	...
}

If you are seeing the 0xcccccccc bit pattern it means that you are reading memory that is on the current thread stack that has not been initialised.

0xbaadf00d

The 0xbaadf00d bit pattern is the bit pattern for memory allocated with HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED).

If you are seeing the 0xbaadf00d bit pattern it means that you are reading memory that has been allocated by HeapAlloc() (or reallocated by HeapReAlloc()) and which has not been initialised by the caller of HeapAlloc (or HeapReAlloc, LocalAlloc, GlobalAlloc).

0xdeadbeef

The 0xdeadbeef bit pattern is the bit pattern for memory deallocated using HeapFree(), LocalFree(), GlobalFree().

If you are seeing the 0xdeadbeef bit pattern it means that you are reading memory that has been deallocated by HeapFree(), LocalFree() or GlobalFree().

0xabababab

The 0xabababab bit pattern is the bit pattern for the guard block after memory allocated using HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED) or CoTaskMemAlloc().

If you are seeing the 0xabababab bit pattern it means that you are reading memory after a memory block that has been allocated by HeapAlloc(), LocalAlloc(LMEM_FIXED), GlobalAlloc(GMEM_FIXED) or CoTaskMemAlloc().

0xbdbdbdbd

The 0xbdbdbdbd bit pattern is the guard pattern around memory allocations allocated with the "aligned" allocators.

Memory allocated with malloc(), realloc(), new and new [] are provided with a guard block before and after the memory allocation. When this happens with an aligned memory allocator, the bit pattern used in the guard block is 0xbdbdbdbd.

If you are seeing the 0xbdbdbdbd bit pattern it means that you are reading memory before the start of a memory block created by an aligned allocation.

0xfdfdfdfd

The 0xfdfdfdfd bit pattern is the guard pattern around memory allocations allocated with the "non-aligned" (default) allocators.

Memory allocated with malloc(), realloc(), new and new [] are provided with a guard block before and after the memory allocation. When this happens with an non-aligned (default) memory allocator, the bit pattern used in the guard block is 0xfdfdfdfd.

If you are seeing the 0xfdfdfdfd bit pattern it means that you are reading memory either before the start of a memory block or past the end of a memory block. In either case the memory has been allocated by malloc(), realloc() or new.

0xcdcdcdcd

The 0xcdcdcdcd bit pattern indicates that this memory has been initialised by the memory allocator (malloc() or new) but has not been initialised by your software (object constructor or local code).

If you are seeing the 0xcdcdcdcd bit pattern it means that you are reading memory that has been allocated by malloc(), realloc() or new, but which has not been initialised.

0xdddddddd

The 0xdddddddd bit pattern indicates that this memory is part of a deallocated memory allocation (free() or delete).

If you are seeing the 0xdddddddd bit pattern it means that you are reading memory that has been deallocated by free() or delete.

0xfeeefeee

The 0xfeeefeee bit pattern indicates that this memory is part of a deallocated memory allocation (free() or delete).

If you are seeing the 0xfeeefeee bit pattern it means that you are reading memory that has been deallocated by free() or delete.

What can you do to prevent the cause of these problems?

Depending on the nature of the problem, it may be trivial to identify why you have an uninitialised variable or why your pointer is pointing to deallocated memory. The rest of the time you can rely on a few helpful rules and the judicious use of some software tools.

  • When you have a crash, try to identify the cause of the crash.
    • If you are in a debugger, the debugger may be able to show you variable names, thus you will be able to identify which variable is in error.

    • If you are in a debugger you can view the register window to see if a register has one of these special bit patterns.

    • If you have an exception report, you can view the crash address, the exception address and any registers to see if any of these special bit patterns are present.

    At the start of a C++ method, the "this" pointer is stored in the ECX register. If the ECX register contains one of these bit patterns you have a good indicator that a dangling pointer to a deleted object or an uninitialised pointer is being used. Note that depending on what the compiler does the ECX register may remain valid during the method or may take on other values and thus be unreliable as to whether it still represnts the "this" pointer.

    When a method or function returns, the return value is in the EAX register.

  • Always initialise all data members in all your object constructors.

  • Always initialise all data that is used that is not data members of class definitions.

  • Always use the correct form of delete, even if you are working with intrinsic types. It is not unknown to change the type of a data member as software evolves. If you always use the correct form of delete, then a switch from (for example) "int" to a class type will not result in objects of the class type failing to be deleted.

     

    • If you allocate using new, deallocate with delete.
    • If you allocate using new [], deallocate with delete [].
    • If you allocate using malloc() or realloc(), deallocate with free().

  • Always set pointers to NULL after dellocating the memory pointed to by the pointer.
    	delete [] accounts;
    	accounts = NULL;
     

    Do this in object destructors and anywhere else you deallocate memory. The reason you do this in object destructors is because in release code, the memory deallocator will not overwrite the contents of the deleted object. Thus if any erroneous code is still pointing to the deleted object, it will find a NULL pointer to accounts rather than a pointer to a deleted accounts object.


  • In object destructors, even if you are not deleting any objects, but you have pointers to objects, set these to NULL as well. The reason is the same reason as above - defensive programming, minimise the likelihood things can fail.

  • Always check pointers are non-NULL before using them. This goes hand in hand with setting pointers to NULL when you are finished using them.

  • If possible, try to make one location in your software responsible for the management of a particular type of object. For example you may create a manager class that is responsible for creating, managing and destroying particular objects. If you then get a failure with a related object type, you can focus your attentions on the manager class, perhaps adding additional tracing code or analysing with a software tool.

  • If your software is structured so that some class implementations are in certain DLLs and other DLLs rely on those class implementations (either by usage or by derivation/inheritance) then you need to consider the following issues.

    If you change the size of a class object then you will need to rebuild all other DLLs that use that object. In theory the Developer Studio build system should keep everything up to date. But Developer Studio can only work with the information you give it. If you have forgotten to add appropriate header files to a project the project will not consider changes to those header files. Typically we have found the simplest and easier solution is a full rebuild of any affected DLLs after a change in size of a class object.

    Such changes include:

    • Change of a method from normal to virtual or virtual to normal.
    • Addition or removal of a virtual method.
    • Addition or removal of a data member.
    • Change in type of an object that is a data member of the class (for example from int to double or from weeble to wobble).
    • Change in size of an object that is a data member of the class (for example data member of type testObject changes size).
    • Change of #pragma byte packing for some or all of the class definition.
    • Addition or removal of base classes which the class derives from.

    A good indicator that you have forgotten to do the above, is looking at the class object in the debugger and noticing that the fields all appear OK in one DLL, but when looked at from a function in a different DLL, the object fields appear to have different data. This is a sure sign that one or more DLLs have not been compiled with the same object definition.


I've tried all the above, what other options are there?

If all else fails, then you will probably need to turn to a software tool to analyse your memory usage and identify the cause of the deleted memory that the pointer is pointing to (has the memory been deleted too early? Or is the pointer being used in error after the memory was deleted?).

In that case you may want to consider a software tool like Memory Validator.

.equals(Obje​​ct)方法在Java中用于比较两个对象是否相等。它检查两个对象的内容是否相同而不是引用是否相同。通常,我们可以使用"=="运算符来比较基本数据类型的值或判断两个对象引用是否相等。但是,对于比较复杂的对象,比如自定义的类对象,它们可能具有相同的属性值,但却不被认为是相等的,因为它们不是同一个对象的实例。 .equals(Obje​​ct)方法为我们提供了一种自定义比较两个对象内容的方式。这个方法是从Object类继承而来的,因此在所有的类中都可用。某些类,比如String类、Integer类等,已经重写了.equals(Obje​​ct)方法,以便实现比较它们的内容。但是,对于自定义的类,如果不重写.equals(Obje​​ct)方法的话,将继承Object类的默认实现,即比较两个对象的引用是否相等。 与"=="运算符相比,.equals(Obje​​ct)方法的作用更灵活、更具体。它可以根据具体的比较规则来决定两个对象是否相等。但是,由于.equals(Obje​​ct)方法的默认实现比较对象的引用,所以在自定义类中使用.equals(Obje​​ct)方法时,需要注意重写该方法以实现我们自己的比较逻辑。 综上所述,.equals(Obje​​ct)方法在Java中是相对不寻常的,因为大部分时候我们可以使用"=="运算符来比较对象引用是否相等。然而,在比较复杂对象内容时,.equals(Obje​​ct)方法提供了一种更具体、更灵活的比较方式。这是Java中为了满足不同需求而提供的一种对象比较工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值