堆内存损坏,但只有在笔记本电脑上编译时才出现。

10

我正在尝试编译一个在我的台式机上可以顺利编译的程序,但在我的笔记本电脑上虽然能够编译,但每次运行时都会出现以下错误:

Windows已在RR.exe中触发断点。

这可能是由于堆栈损坏引起的,这表明RR.exe或其加载的任何DLL中存在错误。

这也可能是由于用户在RR.exe获得焦点时按下F12。

输出窗口可能有更多诊断信息。

我已经注释掉了一些代码行,最终找到了导致错误的那一行:

if(glfwOpenWindow(width_, height_, 0, 0, 0, 0, 32, 0, GLFW_WINDOW) != GL_TRUE) {
    throw std::runtime_error("Unable to open GLFW window");
}

奇怪的是,如果我将width_height_替换为常量,例如800和600,它就不会出现堆栈破坏。另外,如果我仅使用构造函数设置的默认值而不传递任何值,则程序不会崩溃。

以下是完整的代码,上述内容位于Window构造函数中。

window.h

#pragma once

#include <iostream>
#include <GL\glew.h>
#include <GL\glfw.h>

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glew32.lib")
#pragma comment(lib, "GLFW.lib")

class Window {
public:
    Window(unsigned width = 800, unsigned height = 600);
    ~Window();

    void clear();
    inline void display() { glfwSwapBuffers(); }
    inline bool exit() { return !glfwGetWindowParam(GLFW_OPENED); }

private:
    unsigned width_, height_;
};

window.cpp

#include "window.h"

Window::Window(unsigned width, unsigned height) : width_(width), height_(height) {
    if(glfwInit() != GL_TRUE) {
        throw std::runtime_error("Unable to initialize GLFW");
    }

    if(glfwOpenWindow(width_, height_, 0, 0, 0, 0, 32, 0, GLFW_WINDOW) != GL_TRUE) { //crash
    //if(glfwOpenWindow(800, 600, 0, 0, 0, 0, 32, 0, GLFW_WINDOW) != GL_TRUE) { //no crash
        throw std::runtime_error("Unable to open GLFW window");
    }

    GLenum result = glewInit();
    if(result != GLEW_OK) {
        std::stringstream ss;
        ss << "Unable to initialize glew: " << glewGetErrorString(result);
        throw std::runtime_error(ss.str());
    }
}

Window::~Window() {
    glfwTerminate();
}

void Window::clear() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
}

main.cpp

#include "window.h"

int main() {
    Window wind(1024, 800); //crash
    Window wind(800, 600); //crash
    Window wind(); //works

    return 0;
}

3
感谢您提供完整而相对简短的测试用例,给您点赞(+1)。 - Oliver Charlesworth
1
你确定在这两台计算机上都安装了相同版本的系统/运行时DLL文件吗? - Péter Török
7
Window wind(); - 这是一个函数声明,不是类型为Window的默认构造变量。 - user396672
2
你确定glfwOpenWindow这一行代码导致了崩溃而不是暴露了问题吗?在调用时,width_和height_包含有效和正确的值吗? - Violet Giraffe
我已经成功通过将运行时库从多线程调试DLL更改为多线程DLL来停止堆损坏。我会在可以的时候将其发布为答案。 - Lerp
显示剩余7条评论
3个回答

6
问题似乎与glfw有关:
我猜您正在尝试使用动态链接的GLFW。请注意glfw头文件中的内容:
#if defined(_WIN32) && defined(GLFW_BUILD_DLL)

/* We are building a Win32 DLL */
 #define GLFWAPI      __declspec(dllexport)
 #define GLFWAPIENTRY __stdcall
 #define GLFWCALL     __stdcall
#elif defined(_WIN32) && defined(GLFW_DLL)

 /* We are calling a Win32 DLL */
 #if defined(__LCC__)
  #define GLFWAPI      extern
 #else
  #define GLFWAPI      __declspec(dllimport)
 #endif
 #define GLFWAPIENTRY __stdcall
 #define GLFWCALL     __stdcall

#else

 /* We are either building/calling a static lib or we are non-win32 */
 #define GLFWAPIENTRY
 #define GLFWAPI
 #define GLFWCALL

#endif

GLFW_BUILD_DLL 显然是在构建dll时设置的,并且它使用 __stdcall 调用转换定义了API函数。

但是,当使用库时,您没有定义 GLFW_DLL,因此您的代码假定使用 __cdecl 调用转换。一般来说,_cdecl__stdcall 之间的区别在于 调用函数 应该首先清理堆栈,而 被调用者 则应该在最后情况下清理。所以你清理了两次堆栈,这就是为什么你会得到堆栈损坏的原因。

在我定义 GLFW_DLL 后,在程序中包含 glfw 之前,它开始正常工作。还要注意,我使用了mingw,并且在定义 GLFW_DLL 后必须链接到 glfwdll.a 而不是 glfw.a


这对我也起作用了。我成功地通过将多线程调试DLL的运行时库更改为多线程DLL来使其工作。您能否解释一下为什么传递常量值而不是变量不会导致堆损坏?是否因为所有参数都是常量,所以它调用函数的方式不同? - Lerp
@Rarge,“多线程调试DLL运行时库到多线程DLL”似乎与一些WinCRT相关的东西有关,而不是GLFW。无论是设置常量值还是更改项目设置都不能防止堆栈损坏,只能隐藏它。 - Lol4t0

1

堆栈损坏错误几乎从不在它们最初发生的时候显现出来,这就是使它们如此难以诊断的原因。它在一个系统上工作而在另一个系统上不工作的事实意味着未定义的行为。

我在快速检查您的代码时没有看到任何明显的错误。如果您可以访问Windows的Purify,或者可以在Linux上编译,则可以使用valgrind。我相信这些工具比简单的代码检查具有更高的成功率。


0

我找到了另一个解决方案:

通过更改运行时库(项目属性 > C/C++ > 从多线程调试 DLL (/MDd) 到多线程 DLL (/MD),堆损坏不再发生。

我不知道为什么,也许有更多知识的人可以解释一下。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接