为什么我的Qt程序中未执行的代码会导致程序崩溃?

Why does the unexecuted code in my Qt program cause the program to crash?

此Qt程序中有一段代码导致程序崩溃但没有执行

程序由main.cpp、mainwindow.cpp、mainwindow.h、mainwindow.ui和CMakeLists.txt组成,问题主要出在MainWindowclass.

mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPainter>
#include <QMouseEvent>
#include <QColorDialog>
#include <vector>
#include <cmath>
#include <cstdio>
#define Pi 3.1415926535897932
#define MAXHEIGHT 4320
#define MAXWIDTH 7680
using namespace std;

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

enum State{NONE,RECT,CIRCLE,POLYGON,CUBE,BEZIER};

struct Circle
{
    int x;
    int y;
    double r;
};

struct Pos
{
    int x;
    int y;
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

protected:
    void paintEvent(QPaintEvent*);
    void mousePressEvent(QMouseEvent*);
    void mouseReleaseEvent(QMouseEvent*);
    void mouseMoveEvent(QMouseEvent*);

    void DDALine(QPainter* painter, int x1, int y1, int x2, int y2);
    void BresenhamCircle(QPainter* painter, int x, int y, int r);
    void Polygon(QPainter* painter, vector<Pos> cpolygon);

    State state=NONE;
    vector<QRect> rects;
    vector<Circle> circles;
    vector<vector<Pos>> polygons;
    vector<Pos> currentPolygon;
    QPoint* movePoint=nullptr;
    vector<Pos> curPaintPolygon;
    int rectX1=0;
    int rectY1=0;
    int circleX1=0;
    int circleY1=0;
    QColor penColor=Qt::black;
    QImage img;
};
#endif // MAINWINDOW_H

mainwindow.cpp:

#include "mainwindow.h"
#include "./ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    if(!(img.load("/Users/lijiabo/Documents/GitHub/CGWORK0528/0528.png")))
        throw "Image loading failed!";

    //connect menu actions
    connect(ui->actionDrawRect,&QAction::triggered,this,[=](){
        this->state=RECT;
    });
    connect(ui->actionDrawCircle,&QAction::triggered,this,[=](){
        this->state=CIRCLE;
    });
    connect(ui->actionSetColor,&QAction::triggered,this,[=](){
        penColor=QColorDialog::getColor(penColor,this,"设置颜色");
        update(rect());
    });
    connect(ui->actionDrawPolygon,&QAction::triggered,this,[=](){
        this->state=POLYGON;
    });
    connect(ui->actionSetColor_2,&QAction::triggered,this,[=](){
        penColor=QColorDialog::getColor(penColor,this,"设置颜色");
        update(rect());
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setPen(penColor);
    //DEBUG
    painter.drawText(rect(),QString("Width: "+QString::number(rect().width())+" Height: "+QString::number(rect().height())));

    switch(state)
    {
    case RECT:
    case CIRCLE:
        for(QRect rect:rects)
        {
            DDALine(&painter,rect.topLeft().x(),rect.topLeft().y(),rect.topRight().x(),rect.topRight().y());//top
            DDALine(&painter,rect.bottomLeft().x(),rect.bottomLeft().y(),rect.bottomRight().x(),rect.bottomRight().y());//bottom
            DDALine(&painter,rect.topLeft().x(),rect.topLeft().y(),rect.bottomLeft().x(),rect.bottomLeft().y());//left
            DDALine(&painter,rect.topRight().x(),rect.topRight().y(),rect.bottomRight().x(),rect.bottomRight().y());//right
        }
        for(Circle c:circles)
            BresenhamCircle(&painter,c.x,c.y,(int)c.r);
        break;
    case POLYGON:
        if(currentPolygon.size()>=1)
        {
            //把currentPolygon复制给curPaintPolygon
            curPaintPolygon.clear();
            for(Pos point:currentPolygon)
            {
                curPaintPolygon.push_back({point.x,point.y});
            }
            if(movePoint!=nullptr)
                curPaintPolygon.push_back({movePoint->x(),movePoint->y()});
            Polygon(&painter,curPaintPolygon);
        }
        if(polygons.size()>0)
        {
            for(vector<Pos> polygon:polygons)
            {
                Polygon(&painter,polygon);
            }
        }
        break;
    case CUBE:
        break;
    case BEZIER:
        break;
    default:
        ;
    }
}

void MainWindow::mousePressEvent(QMouseEvent *event)
{
    switch(state)
    {
    case RECT:
        rectX1=event->pos().x();
        rectY1=event->pos().y();
        rects.push_back(QRect(rectX1,rectY1,0,0));
        break;
    case CIRCLE:
        circleX1=event->pos().x();
        circleY1=event->pos().y();
        circles.push_back({circleX1,circleY1,1});
        break;
    case POLYGON:
        /*
        if(currentPolygon==nullptr)
        {
            currentPolygon=new QPolygon;
            currentPolygon->append(QPoint(event->pos().x(),event->pos().y()));
        }
        else
        */
        movePoint= nullptr;
        currentPolygon.push_back({event->pos().x(),event->pos().y()});
        break;
    case CUBE:
        break;
    case BEZIER:
        break;
    default:
        ;
    }
    update(rect());
}

void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    switch(state)
    {
    case RECT:
        break;
    case CIRCLE:
        break;
    case POLYGON:
        if(event->button()==Qt::LeftButton)
        {
            if(currentPolygon.size()>1)
                polygons.push_back(currentPolygon);
            currentPolygon.clear();
            if(movePoint!=nullptr)
                delete movePoint;
            movePoint=nullptr;
        }
        break;
    case CUBE:
        break;
    case BEZIER:
        break;
    default:
        ;
    }
    update(rect());
}

void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    switch(state)
    {
    case RECT:
        rects.back().setCoords(rectX1,rectY1,event->pos().x(),event->pos().y());
        break;
    case CIRCLE:
        circles.back().r=sqrt((event->pos().x()-circleX1)*(event->pos().x()-circleX1)+(event->pos().y()-circleY1)*(event->pos().y()-circleY1));
        break;
    case POLYGON:
        if(movePoint==nullptr)
            movePoint=new QPoint(event->pos().x(),event->pos().y());
        else
            *movePoint=event->pos();
        break;
    case CUBE:
        break;
    case BEZIER:
        break;
    default:
        ;
    }
    update(rect());
}

void MainWindow::DDALine(QPainter* painter, int x1, int y1, int x2, int y2)
{
    double dx, dy, e, x, y;
    dx = x2 - x1;
    dy = y2 - y1;
    e = (fabs(dx) > fabs(dy)) ? fabs(dx) : fabs(dy);
    dx /= e;
    dy /= e;
    x = x1;
    y = y1;
    for (int i = 1; i <= e; i++)
    {
        painter->drawPoint((int)(x + 0.5), (int)(y + 0.5));
        x += dx;
        y += dy;
    }
}

void MainWindow::BresenhamCircle(QPainter* painter, int x, int y, int r)
{
    int edgeX,edgeY,p;

    edgeX=0;
    edgeY=r;
    p=3-2*r;
    for(;edgeX<=edgeY;edgeX++)
    {
        painter->drawPoint(x+edgeX,y+edgeY);
        double dT=0;
        for(int dTNC=1;dTNC<8;dTNC++)
        {
            dT=dTNC*0.25*Pi;
            painter->drawPoint( x + r*cos(acos(edgeX/double(r))+dT), y + r*sin(asin(edgeY/double(r))+dT) );
        }
        if(p>=0)
        {
            p+=4*(edgeX-edgeY)+10;
            edgeY--;
        }
        else
            p+=4*edgeX+6;
    }


}

void MainWindow::Polygon(QPainter* painter, vector<Pos> cpolygon)
{
    if(cpolygon.size()<=1)
        return;
    
    vector<Pos>::iterator prevPoint = cpolygon.begin();
    for(vector<Pos>::iterator it=cpolygon.begin()+1;it!=cpolygon.end();it++,prevPoint++)
    {
        DDALine(painter,prevPoint->x,prevPoint->y,it->x,it->y);
    }
    DDALine(painter,prevPoint->x,prevPoint->y,cpolygon.begin()->x,cpolygon.begin()->y);
    
    //fill------------------------------

    int imgWidth=img.width();
    int imgHeight=img.height();
    int x1=rect().left();
    int x2=rect().right();
    int y1=rect().bottom();
    int y2=rect().top();
    bool mask[MAXHEIGHT][MAXWIDTH];
    for(int y=y1;y<=y2;y++)
        for(int x=x1;x<=x2;x++)
            mask[y][x]=false;
    for(vector<Pos>::const_iterator it=cpolygon.cbegin();it!=cpolygon.cend();it++)
    {
        int xs=it->x;
        int dxs=((it+1)->x-it->x)/((it+1)->y/it->y);
        int dys=abs((it+1)->y-it->y)/((it+1)->y-it->y);
        for(int ys=it->y;ys!=(it+1)->y;ys+=dys)
        {
            int Ixs=int(xs+0.5);
            mask[ys][Ixs]=!mask[ys][Ixs];
            xs+=dys*dxs;
        }
    }
    QPen initialPen = painter->pen();
    for(int y=y1;y<=y2;y++)
    {
        bool inside=false;
        for(int x=x1;x<=x2;x++)
        {
            if(mask[y][x])
                inside=!inside;
            if(inside)
            {
                painter->setPen(img.pixel(x%imgWidth,y%imgHeight));
                painter->drawPoint(x,y);
            }
        }
    }
    painter->setPen(initialPen);
    //----------------------------------
}

在mainwindow.cpp的最后一个函数Polygon()中,当“填充”部分存在时,当触发actionDrawPolygon时程序会崩溃,我点击鼠标左键(所以mousePressEvent()是被调用),没有这部分程序不会崩溃。

但是当我在这部分添加printf("TEST")函数时,没有打印"TEST",所以这部分根本没有执行,那为什么导致程序崩溃?

OS 版本:macOS Monterey 12.0.1

IDE:CLion 2021.2.3

如前所述,您有一个本地 bool 数组,其大小为 std::sizeof(bool) * MAXHEIGHT * MAXWIDTH。根据 sizeof bool,大小大于 30 兆字节。

由于堆栈大小有限(可能为 1 兆字节,通常为 8 兆字节),程序堆栈将无法处理该大小的数组。

相反,试试这个:

std::vector<std::vector<bool>> mask(MAXHEIGHT, std::vector<bool>(MAXWIDTH));

这也消除了将数组条目初始化为 falsefor 循环的需要,因为无论如何这将是默认值。

这不是最好的方法,因为它是一个向量的向量,但这很可能会绕过您现在看到的崩溃。