为什么我的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));
这也消除了将数组条目初始化为 false
的 for
循环的需要,因为无论如何这将是默认值。
这不是最好的方法,因为它是一个向量的向量,但这很可能会绕过您现在看到的崩溃。
此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));
这也消除了将数组条目初始化为 false
的 for
循环的需要,因为无论如何这将是默认值。
这不是最好的方法,因为它是一个向量的向量,但这很可能会绕过您现在看到的崩溃。