HeapFree()可能的崩溃原因

时间:2016-03-11 20:22:05

标签: c++ windows winapi

头文件中的

typedef

typedef struct tagMYSTRUCT {
    wchar_t mystr[40] = { 0 };
    DWORD threadId = NULL;
    HANDLE threadHandle = NULL;
    HWND receiverWnd = NULL;
} MYSTRUCT, *PMYSTRUCT;

线程创建:

MYSTRUCT ats = *(PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT));

wcscpy(ats.mystr, L"hello");

ats.threadHandle = CreateThread(NULL, 0, MyThread, &ats, 0, &(ats.threadId));

这是使用HeapFree()函数的线程。但它崩溃了。我相信这是不好的做法,但我想知道为什么。什么是背后的逻辑以及为什么HeapFree崩溃程序?

DWORD WINAPI MyThread(LPVOID lpParam) {

    MYSTRUCT ActiveStruct = *(PMYSTRUCT)lpParam;

    if (lpParam != NULL) {
        std::cout << "1" << std::endl;    // Gets printed.
        HeapFree(GetProcessHeap(), NULL, lpParam);
        std::cout << "2" << std::endl;    // Crashes before this line.
    }

    ...

}

4 个答案:

答案 0 :(得分:3)

你显然来自另一种语言,它将指针和引用的概念与C ++区别开来。您在C ++中的使用非常不合适。通过使用非标准函数(HeapAlloc()是特定于Windows的,而不是C ++等)来管理内存,你会进一步复杂化。

如果您要使用HeapAlloc()(非标准C ++,特定于Windows)或任何动态分配内存的标准函数,结果需要存储在指针中。

MYSTRUCT ats = *(PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT));

wcscpy(ats.mystr, L"hello");

ats.threadHandle = CreateThread(NULL, 0, MyThread, &ats, 0, &(ats.threadId));

这样做是将HeapAlloc()返回的指针转换为指向MYSTRUCT的指针,取消引用将该内存位置解释为MYSTRUCT的值的指针,并复制该值进入ats

至少这是一个内存泄漏 - 由HeapAlloc()分配的内存丢失(从未再次使用,它的地址没有存储在任何地方),并且您将ats的地址传递给线程功能

因此HeapAlloc()分配的内存与传递给线程函数的地址之间没有任何关系。

更糟糕的是线程函数本身,我在这里简化了

DWORD WINAPI MyThread(LPVOID lpParam)
{
     MYSTRUCT ActiveStruct = *(PMYSTRUCT)lpParam;

     if (lpParam != NULL)
     {
          std::cout << "1" << std::endl;    // Gets printed.
          HeapFree(GetProcessHeap(), NULL, lpParam);
          std::cout << "2" << std::endl;    // Crashes before this line.
     }
}

lpParam将包含创建该线程的函数传递的函数中ats的地址。

如果创建线程的函数已经返回(毕竟,线程并行运行),那么ats将不再存在。如果发生这种情况,lpParam将是一个悬空指针(就您的程序而言,对象的地址不再存在)。

ActiveStruct现在将成为一个本地对象,其中包含传递给函数的地址处的对象副本。换句话说,它是由func先前分配的ats的本地副本。如果ats已不复存在,并且传递的地址悬空,则创建ActiveStruct的简单行为会导致未定义的行为。

更糟糕的是,lpParam是({1}}的地址。如果ats仍然存在(即创建线程的函数未返回),则不会在堆上创建它,因此不应使用ats释放。如果它不再存在,则不应将其传递给HeapFree()。无论哪种方式,都要求HeapFree()释放未使用HeapFree()分配的内存。这几乎可以保证导致运行时错误。

至少,您需要将创建线程的代码更改为

HeapAlloc()

和线程函数

MYSTRUCT *ats = (PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT));    //  note changed position of *

wcscpy(ats->mystr, L"hello");    //   note usage of ats as a pointer

DWORD threadID;        // we need these since ats is being released by the thread function
HANDLE threadHandle;   // it is not a good idea for CreateThread() to use them

threadHandle = CreateThread(NULL, 0, MyThread, ats, 0, &(threadId));       // changes since ats is now a pointer

由于您对C ++内存模型做出了根本错误的假设,我认为代码中的其他内容(您没有显示)是错误的。但这应该让你开始。

答案 1 :(得分:1)

你对此感到困惑。您正在传递堆栈分配结构的地址,而您不打算这样做。我认为你打算将地址传递给堆分配的结构。当您尝试释放该结构时,调用HeapFree会遇到运行时错误,因为您将HeapFree未分配的内存地址传递给HeapAlloc

我将使用newdelete而非HeapAllocHeapFree向您展示如何完成此操作。这里真的没有必要使用HeapAlloc。使用标准C ++内存分配器。

MYSTRUCT *pats = new MYSTRUCT(); // zero initialise
wcscpy(pats->mystr, L"hello");
DWORD threadId;
HANDLE threadHandle = CreateThread(NULL, 0, MyThread, pats, 0, &threadId);

....

DWORD WINAPI MyThread(LPVOID lpParam) 
{
    MYSTRUCT ActiveStruct = *(PMYSTRUCT)lpParam;
    delete (PMYSTRUCT)lpParam;

    // if you want the thread ID, call GetCurrentThreadId
    // or if you want a thread handle call GetCurrentThread
}

请注意,我没有尝试将线程句柄和线程ID直接存储到结构中。这是因为理论上可以在调用CreateThread之前销毁该结构。我改用了局部变量。如果您的线程需要找到它的ID,或者获取自己的句柄,那么就会有API调用来执行此操作。

答案 2 :(得分:0)

1)你没有在转换和解除引用之前检查指针

2)您实际上是在堆栈上分配MYSTRUCT并从MYSTRUCT大小的堆分配缓冲区中复制零

3)堆分配的指针在复制分配后泄漏

4)你将指向堆栈分配的MYSTRUCT实例的指针传递给CreateThread,它在MYSTRUCT超出范围之后变得无效(这可能发生在新线程启动之前的任何时候,当它运行时,或者在它退出之后)

5)CreateThread和c ++运行时不能很好地协同工作

答案 3 :(得分:0)

如果您坚持使用HeapAlloc

,这是更正后的版本
PMYSTRUCT ats = (PMYSTRUCT)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MYSTRUCT));
if(ats == NULL)
    return; // exit, throw do something
wcscpy(ats->mystr, L"hello");

CreateThread(NULL, 0, MyThread, ats, 0, &(ats.threadId));

HeapAlloc返回指向已分配内存的指针,该指针放在指针中。然后使用指针操作分配的结构,最后将此指针传递给线程。并且不要将createthread的结果分配给线程所破坏的东西。