首页 > 编程笔记 > Qt笔记

Qt编译和链接错误

为了演示 Qt Creator 的报错信息,我们刻意制造一个小 Bug。依然使用《Qt Creator使用教程(简明版)》一节中创建的项目和代码,双击 HelloWorld.pro,或者在 Qt Creator 主菜单中选择“文件 --> 打开文件或项目”,或者按下 Ctrl+O 快捷键都可以打开 HelloWorld 项目。

编译错误

修改 widget.cpp,添加一行新代码和一个新头文件,如下所示:
#include "widget.h"
#include "ui_widget.h"
#include <QtTest/QTest>  //new header file

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    qsleep(1000);  //new code
}

Widget::~Widget()
{
    delete ui;
}
<QtTest/QTest> 是 Qt 单元测试模块的头文件,它里面有一个睡眠函数 void QTest::​qSleep(int ms),让当前程序睡眠参数指定的 ms 毫秒。上面示范故意写错了函数名,S 大写变成了小写 s 。

点击运行按钮,或者按下 Ctrl+R 快捷键,就可以看到如下图所示的编译错误:
Qt Creator报错信息

下面的输出面板自动显示了编译时的错误, qsleep 没有在当前作用域中声明;在源代码编辑器 qsleep(1000) 这一行的行首有红色背景的感叹号,标出了这行有错误发生。

接着我们把睡眠函数改成正确的 qSleep,再次点击运行按钮,或者按下 Ctrl+F5 快捷键,发现还有错误:
Qt Creator报错信息

还是提示 qSleep 没有声明,但这回给出的错误描述不一样,在问题面板的 “note” 字样一行可以看到提示 “QTest::qSleep”,这次出错是因为 qSleep 函数在名字空间 QTest 里面声明的,如果要用这个函数得加名字空间前缀,或者用 using 语句引入名字空间。我们这里直接给出错的行加名字空间前缀,变成:

QTest::qSleep(1000);

对于编译时错误,常见的就是写错函数名或变量名、类名,没有包含正确的头文件,没有使用正确的名字空间或类前缀等。如果是 Qt 自己的模块或类,直接包含相应的头文件就可以了。

如果是非 Qt 库的头文件,可以在 pro 文件里添加 INCLUDEPATH 变量,比如:

INCLUDEPATH += "E:/mylibs/extra headers"
INCLUDEPATH += "/home/username/extra headers"

上面第一个是 Windows 路径包含,第二个是 Linux/Unix 路径包含。添加包含路径之后,可以在项目源代码里直接包含头文件名:

#include <extra.h>

QtCreator 编辑器支持包含的文件名补全,可自动搜索 Qt 库标准路径和额外包含路径里的头文件。

链接错误

按照上面的说明修改好代码之后,我们就来验证一下吧。再次点击运行按钮,或者按下 Ctrl+R 快捷键,竟然还会有报错信息:
Qt Creator链接错误

这回也是撞见鬼了,就改了一行代码,还是往正确了改,怎么蹦出十几条错误?最后一条错误描述说明链接器 ld 返回错误了,这是链接目标文件和库文件时出了错误,无法生成最终的可执行程序。问题面板列的十几条错误,就最后倒数第二个是指向 widget.cpp 里的睡眠函数的,报的错误是error: undefined reference to `_imp___ZN5QTest6qSleepEi' ,这是未找到真实的函数引用(定义)的意思,`_imp___ZN5QTest6qSleepEi' 是函数 QTest::qSleep 在编译之后的库文件里的引用名字,链接时找不到引用,通常是没有链接正确的 *.a 库声明文件(Linux 上直接链接到 *.so )。

之前提到 qSleep 是 Qt 单元测试模块里的函数,需要为当前项目添加对应的 Qt 模块,编译链接时 qmake 才会为项目添加正确的头文件目录和链接库文件。上面源代码实际没有问题,出问题的是 HelloWorld.pro 文件。在左边项目视图打开 HelloWorld.pro 文件,编辑如下(注释部分已去掉):

QT       += core gui testlib

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = HelloWorld
TEMPLATE = app

SOURCES += \
        main.cpp \
        widget.cpp

HEADERS += \
        widget.h

FORMS += \
        widget.ui

上面就是为项目添加了 Qt 模块 testlib ,使用单元测试模块,就应该在 pro 文件里的QT +=一行里添加 testlib ,然后保存项目文件。

QtCreator 会稍微花些时间重新解析 pro 文件,然后我们点击主菜单菜单“构建 --> 重新构建项目 HelloWorld”,对项目进行重新构建。一般如果修改了 pro 文件,需要手动重新构建项目,避免一些莫名其妙的构建错误(如找不到 WinMain 函数引用)。另外,删除构建目录如 build-HelloWorld-Desktop_Qt_5_9_0_MinGW_32bit-Debug ,然后点击构建或运行按钮,也能实现重新构建项目。如果遇到莫名其妙的构建问题,可以尝试这两种方法。

运行正确编译链接的目标程序,主界面弹出之前会明显感觉到有 1 秒多的迟钝,这是因为我们在主界面构造函数里睡眠了 1000 毫秒,也就是 1 秒。

为 Qt 添加库文件

对于链接时错误,最常遇到的就是本小节里的 undefined reference to **** 链接错误,通常是没有链接到正确的库文件,对于 Qt 自己的库模块,只需要在 pro 文件里QT += 一行末尾加上对应的模块名字,qmake 就会在编译链接时为项目添加正确的包含路径和链接库文件。

对于非 Qt 模块的库文件,通常是在项目文件 pro 里添加 LIBS 变量行,一般添加规则是:

LIBS += 库文件路径全名
LIBS += -L库文件路径  -l库文件短名

这两种写法都可行。库文件短名是指去掉打头的 lib 和扩展名,只留下中间的,比如 libextra.a 或 libextra.so 都写成 -lextra (Linux 里优先链接动态库 .so,Windows 里不用管这些)。

下面是一些例子:

LIBS += "C:/mylibs/extra libs/extra.lib"
LIBS += "C:/mylibs/extra libs/libextra.a"
LIBS += "-L/home/user/extra libs"  -lextra

上面示范的是有空格的路径,必须加双引号才能找到正确位置,如果没空格可以省去双引号。因此不要在库的全路径里出现空格或特殊字符,那纯粹是找麻烦。

示例的第一个 是 VC 库文件用法,第二个是 MinGW 动态链接库引用库 .a 文件或静态库 .a 文件用法,第三个是 Linux/Unix 里的链接库用法,MinGW 也可以采用这种写法。

这些东西在以后的章节里会有些涉及,对于纯 Qt 程序目前不用操心的,这里只是提点一下,等到用的时候再查文档就行了。

所有教程

优秀文章