随机分段错误?

时间:2014-03-25 21:07:18

标签: c++ qt

注意,问题已经确定,请参阅第二部分了解最新信息,第三部分是最可能的原因,以及如何解决问题

我在Qt写了一个程序,几天前我因为分段故障问题而停止了。它似乎抱怨没有分配内存。我四处搜索,无法弄清楚为什么会出现分配问题,我休息一下。现在,再看一遍,分段错误仍然存​​在,但它处于完全不同的,大部分不相关的功能中。什么可能导致这些随机分段错误?

对于某些具体情况,在这种情况下,我目前在这里遇到分段错误:

Q_DECL_CONSTEXPR inline int QSize::height() const
{ return ht; }

在我评论出线条(最后三行)

之前,我在这里得到它
qDebug() << QString("---SETTINGS DEBUG---")+QString("\r\n")<<
            "netProcPage: "+netProcPage.url()+"\r\n" <<
            "mapSize.width(): "+QString::number(mapSize.width())+"\r\n"<<
            "mapSize.height(): "+QString::number(mapSize.height())+"\r\n"<<
            "mapZoom: "+QString::number(mapZoom)+"\r\n"<<
            "mappxSize.width(): "+QString::number(mappxSize.width())+"\r\n"<<
            "mappxSize.height(): "+QString::number(mappxSize.height())+"\r\n"<<
            "UserCoords[0]: "+QString::number(UserCoords[0])+"\r\n"<<
            "UserCoords[1]: "+QString::number(UserCoords[1])+"\r\n"<<
            "mapCoordOffsets[0]: "+QString::number(mapCoordOffsets[0])+"\r\n"<<
            "mapCoordOffsets[1]: "+QString::number(mapCoordOffsets[1])+"\r\n"<<
            "getWindowSize.width(): "+QString::number(getWindowSize.width())+"\r\n"<<
            "getWindowSize.height(): "+QString::number(getWindowSize.height())+"\r\n"<<
            "mappxOffsets[0]: "+QString::number(mappxOffsets[0])+"\r\n"<<
            "mappxOffsets[1]: "+QString::number(mappxOffsets[1])+"\r\n"<<
            QString("---END SETTINGS DEBUG---")+QString("\r\n");

在此之前,没有改变任何东西(只是等了几天,之后又重新开始了几次),它就在这里:

mkeMap.genMap(QString("Map1"), tempmapSize, tempmapZoom, mapDisp->ui);

MainWindow类构造函数中抱怨tempmapSize,其定义如下:

QSize tempmapSize;
tempmapSize = settings->mapSize; //<--- The error might be coming from here, is there an alternative?

这是与细分错误相关联的地方。这是设置类:

#include "settings.h"

Settings::Settings(QWidget *parent)
{

    netProcPage = "http://localhost:81";

    // Max image size is 32767x32767 pixels (divided by 4*mapZoom since 4 panes are used at mapZoom zoom)
    // If max mapZoom is 20, max size of map is 409x409, or 408x408 to keep it even
    mapSize.setWidth(250);
    mapSize.setHeight(250);

    mapZoom = 10;
    mappxSize.setWidth(mapSize.width()*mapZoom);
    mappxSize.setHeight(mapSize.height()*mapZoom);

    //downloadMap(netProcPage,"getMap","Username","Password");
    //makeMap("bingbong",mapSize,mapZoom);
    UserCoords[0] = 0;
    UserCoords[1] = 0;
    mapCoordOffsets[0] = UserCoords[0] + .5 * mapSize.width();
    mapCoordOffsets[1] = UserCoords[1] + .5 * mapSize.height();
    //getWindowSize.setWidth(parent->width());
    //getWindowSize.setHeight(parent->height());
    getWindowSize.setWidth(500);
    getWindowSize.setHeight(500);
    mappxOffsets[0] = UserCoords[0]*mapZoom + .5 * getWindowSize.width() - .5 * mappxSize.width();
    mappxOffsets[1] = UserCoords[1]*mapZoom + .5 * getWindowSize.height() - .5 * mappxSize.height();
}

void Settings::debug()
{
    qDebug() << QString("---SETTINGS DEBUG---")+QString("\r\n")<<
                "netProcPage: "+netProcPage.url()+"\r\n" <<
                "mapSize.width(): "+QString::number(mapSize.width())+"\r\n"<<
                "mapSize.height(): "+QString::number(mapSize.height())+"\r\n"<<
                "mapZoom: "+QString::number(mapZoom)+"\r\n"<<
                "mappxSize.width(): "+QString::number(mappxSize.width())+"\r\n"<<
                "mappxSize.height(): "+QString::number(mappxSize.height())+"\r\n"<<
                "UserCoords[0]: "+QString::number(UserCoords[0])+"\r\n"<<
                "UserCoords[1]: "+QString::number(UserCoords[1])+"\r\n"<<
                "mapCoordOffsets[0]: "+QString::number(mapCoordOffsets[0])+"\r\n"<<
                "mapCoordOffsets[1]: "+QString::number(mapCoordOffsets[1])+"\r\n"<<
                "getWindowSize.width(): "+QString::number(getWindowSize.width())+"\r\n"<<
                "getWindowSize.height(): "+QString::number(getWindowSize.height())+"\r\n";//<<
                //"mappxOffsets[0]: "+QString::number(mappxOffsets[0])+"\r\n"<<
                //"mappxOffsets[1]: "+QString::number(mappxOffsets[1])+"\r\n"<<
                //QString("---END SETTINGS DEBUG---")+QString("\r\n");
}

QSize* Settings::getmapSize()
{
    return &mapSize;
}

int Settings::getmapZoom()
{
    return mapZoom;
}

---

这是问题(已确定)

我按照建议对代码进行了重构,并确定了确切的问题,但我不知道如何修复它。

void makeMap::genMap(QString name, QPointF* inSize, int* zoom, Ui::MapDisp* ui)
{
    QVector<QString> mapvector;
    QPointF mapSize = *inSize; // <--- The problem is right here

...
}

解除引用先前QPointF实例中找到的Settings对象时发生问题,该实例已发送到genMap(...)

呼叫是这样完成的:

QPointF* tempmapSize;
tempmapSize = settings->getmapSize();
int* tempmapZoom = settings->getmapZoom();
mkeMap.genMap(QString("Map1"), tempmapSize, tempmapZoom, mapDisp->ui);

无论我在哪里移动取消引用(*Settings::inSize**inSize),都会发生错误(在调试器中)。一切都编译得很好。

运行程序时,它会因此错误崩溃:

Starting C:\program-debug.exe...
ASSERT failure in QVector<T>::operator[]: "index out of range", file ../../../../../Qt/5.2.0/mingw48_32/include/QtCore/qvector.h, line 369
Invalid parameter passed to C runtime function.
Invalid parameter passed to C runtime function.
C:\program-debug.exe exited with code 3

QVector<T>的引用稍后在genMap

中引用
void makeMap::genMap(QString name, QPointF* inSize, int* zoom, Ui::MapDisp* ui)
{
    QVector<QString> mapvector;
    QPointF mapSize = *inSize; //<---Here's the segmentation fault
    /* Using this instead of the above works, as well as replacing zoom which causes another segmentation fault when dereferenced
    QPointF mapSize;
    mapSize.setX(250);
    mapSize.setY(250);
    int zoom0 = 10;
    */

    QFile file(name+"_"+QString::number(mapSize.x())+"x"+QString::number(mapSize.y())+".rtsmap");
    file.open(QIODevice::WriteOnly | QIODevice::Text);
    QTextStream out(&file);
    mapvector.resize(mapSize.x() * mapSize.y());
    for(int x = 0; x < mapSize.x(); x++){
        for(int y = 0; y < mapSize.y(); y++){
            uint decimalcolor = (((qSin(x)+1) + (qSin(y)+1))/4)>1?16777215:(((qSin(x)+1) + (qSin(y)+1))/4)*16777214;
            QString hexadecimalcolor;
            hexadecimalcolor.setNum(decimalcolor,16);
            mapvector[index(x, y, mapSize)] = "#" + hexadecimalcolor;
            //drawRect(x*10,y*10,10,10,"#"+hexadecimalcolor,zoom);
            out << "#" << hexadecimalcolor+'\n';
        }
    }
    file.close();
    drawMap(mapvector,zoom0,ui,mapSize);
}


简而言之

我认为这个问题是一个悬垂的指针。更具体地说,当我将设置指针传递给类构造函数时,在这里:

MapCtrl::MapCtrl(QWidget *parent, Settings *settingsIn) :
    QWidget(parent),
    ui(new Ui::MapCtrl)
{
    ui->setupUi(this);
    mapDisp = new MapDisp(parent, settingsIn);
    addMap();
    settings = settingsIn;
}

指针settingsIn可能会在构造函数的末尾被删除,settings仍指向那里,所以稍后当我从settings取消引用某个值时,它就不存在了,导致分段错误。所以,问题是,如何防止名为settingsIn的指针在构造函数的末尾被删除?


请求的代码

在这里调用MapCtrl construtor MapCtrl::MapCtrl并实例化Settings

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    wScr = new WelcomeScreen(this);
    Settings *settings = new Settings(this);
    mapCtrl = new MapCtrl(parent,settings);
    ...
}

解决方案(感谢Kuba Ober的指导和大量有用的C ++知识)

除了检查悬空指针并修复一些可能的原因外,最后一步是改变这个:

MapCtrl::MapCtrl(QWidget *parent, Settings *settingsIn) :
    QWidget(parent),
    ui(new Ui::MapCtrl)
{
    ui->setupUi(this);
    mapDisp = new MapDisp(parent, settingsIn);
    addMap();
    qDebug() << "bingbong!!" << settingsIn->mapSize.x();
    settings = settingsIn;

}

到此:

MapCtrl::MapCtrl(QWidget *parent, Settings *settingsIn) :
    QWidget(parent),
    ui(new Ui::MapCtrl)
{
    ui->setupUi(this);
    mapDisp = new MapDisp(parent, settingsIn);

    qDebug() << "bingbong!!" << settingsIn->mapSize.x();
    settings = settingsIn;
    addMap();
}

settings是在需要设置settings的函数之后设置的。

1 个答案:

答案 0 :(得分:3)

在整个代码库中:

  1. 通过指针删除返回Qt容器(QSizeQList等)的地方,并将其替换为按值返回。

  2. 声明访问器方法const。

    E.g。将Settings::getmapSize()更改为以下惯用代码:

    QSize Settings::getmapSize() const
    {
        return mapSize;
    }
    
  3. 使用QPointF代替坐标,而不是裸阵列。通过值或const引用将它们传递给方法/函数;在现代硬件和编译器上,前者实际上可能会更快一些。

    E.g。将构造函数中的代码更改为:

    mapCoordOffsets = UserCoords + (toPoint(mapSize) * .5);
    

    其中

    static QPointF toPoint(const QSize & size) {
      return QPointF(size.width(), size.height());
    }
    
  4. 摆脱C风格的输出参数,例如

    void makeMap::genMap(..., QPointF* inSize, int* zoom, ...)
    

    如果您要返回某些内容,只需返回一个被称为MapParams的结构,或者某些结果:

    MapParams makeMap::genMap(...);
    

    如果你正在采取一个结构并可能修改它,只需修改它并返回修改过的结构:

    MapParams makeMap::genMap(..., MapParams params) {
       ...
       params.size = ...;
       params.zoom = ...;
       ...
       return params;
    }
    

    我担心的是,你传递虚假指针或指针的某个地方正在混淆一生的问题。现代C ++中的裸指针非常令人怀疑,而且你的代码似乎完全没有任何理由使用它们。使用指针实现的输出参数是在现代C ++中没有位置的C-ism。

  5. 另一个常见错误可能是:您在MainWindow类中有一个Settings * settings成员。然后,您很高兴在Settings *构造函数中创建MainWindow局部变量,但MainWindow::settings成员仍然未初始化。这就是为什么你应该总是在m_之前添加成员名称,这样他们就更难与局部变量混淆。理想情况下,您应该使用初始化列表来初始化构造函数中的成员。

  6. 在进行调试时,当您看到有关越界访问的断言时,您需要调试其原因。调试器应该在这样的断言上停止,如果没有,那么你会得到一些错误的配置(你的确切平台和工具集是什么?)。如果它是写入访问,那么之后发生的任何事都是没有意义的,因为内存已被覆盖,并且此时你正在追逐松鼠。

    做这些事情可以减少您在代码中可能犯的一大堆错误。它可能无法解决您的问题,但可以帮助您找到解决方案的良好轨迹。