一、两者的区别
1、MSVC
即Microsoft Visual C++ Compiler,即微软自己的编译器。
我们下载Windows下的OpenCV时,解压后里面有两个文件夹,一个是build,一个是source,build这个文件夹实际上是官方已经提前给我们编译好了的库,会带两个文件夹VC14,VC15(分别与Visual Studio的版本有对应关系),这两个文件夹下的库可以直接运行不需要编译将VS作为Qt的开发环境也是使用这个编译器的缘故,但是,使用的必须是msvc编译器,不能够跨平台。
2、MinGW
我们都知道GNU在Linux下面鼎鼎大名的gcc/g++,MinGW则是指Minimalist GNU for Windows的缩写它是将GNU开发工具移植到Win32平台下的产物,即一套Windows上的GNU工具集。
用其开发的程序不需要额外的第三方DLL支持就可以在Windows下运行,相对地,不使用动态库导致的就是编译出来的程序大很多。也是可以设置使用静态库的,mingw编译器需要自己手动编译source文件生成库文件,mingw这个编译器能够跨平台,能够在linux、arm等平台使用,这些平台使用的编译器都是g++、gcc。
二、两者优缺点
1、MSVC的优缺点
优点:
qtcreator的debuger有功能缺陷,经常会出现变量无法查看的问题,棘手的bug还是需要在VS环境下进行调试。很多人的开发模式就是qtcreator写代码,VS下面做调试。
缺点:
不能跨平台
编码的问题,QT5的Qstring默认是UTF8格式,QT5极力推介把源码用UTF8格式存储。 但是MSVC只支持带BOM的UTF8格式,qmake不支持带BOM的UTF8格式,逼我只能用GBK么。这样的话,用qt5,每次都要Qstring::fromLocal8bit(“我是中国人”);况且就算BOM问题解决了,源代码是UTF8了。MSVC的执行编码也是GBK
2、MinGW的优缺点
优点:
能跨平台
缺点:
MinGW无法利用生成的dump文件在windbg或VS下面定位到出错的代码
Scene3D在MinGW64位Release和MinGW32位Debug模式下运行直接闪退,而64位Debug和32位Release却正常运行。
动态删除继承QuickItem的对象,有几率导致非法内存访问,可以定位到问题在Qt源码中Renderer中的一处
三、两者的区别
1、编译器:
MinGW 使用GNU编译器集合(GCC)的变体,它是一个开源的编译器工具链,支持多种编程语言。MinGW 的编译器通常被称为 GCC 或 G++。
MSVC 使用 Microsoft 的专有编译器,该编译器是 Visual Studio 集成开发环境(IDE)的一部分,通常称为 cl.exe。
2、标准库:
MinGW 通常使用 GNU 的标准 C 库(glibc)或 MinGW-w64 中的 C 运行时库。它还可以使用 MinGW 专用的头文件和库,以便在 Windows 上开发。
MSVC 使用 Microsoft 的 C 运行时库(CRT),这意味着它具有与 Windows API 更好的集成,但在一些情况下可能不够跨平台。
3、开发环境:
MinGW 通常以类 Unix 的方式工作,可以使用命令行或与其他开发工具集成,通常是在 MSYS2 上开发
MSVC 通常与 Visual Studio IDE 一起使用,提供了一种集成的开发环境,具有丰富的图形用户界面和调试工具。
在编译产物上,它们都可以编译出 .exe 可执行程序,但是 MinGW 通常是依赖于 dll 动态库,因为静态库是 .a 文件,所以会在编译时将静态库链接到 exe 文件里,故 MinGW 编译出的 exe 文件通常比较大
而 MSVC 可以依赖 dll 动态库以及 lib 静态库,这就让编译出的 exe 文件可以比较小
下面是库依赖上的区别:
1、库文件格式:
MinGW 使用的是 GNU 工具链,因此它通常使用与 GNU 标准库兼容的库文件格式。这些库文件具有以 .a 为扩展名的静态库和以 .dll 为扩展名的动态链接库。
MSVC 使用 Microsoft 的 C/C++ 标准库格式,这些库文件通常以 .lib 为扩展名,但也可以包含 .dll 动态链接库文件。
2、库的兼容性:
MinGW 生成的库文件通常与 MSVC 生成的库文件不兼容。这意味着你不能将 MinGW 生成的对象文件与 MSVC 生成的库文件链接,反之亦然。
如果你使用 MinGW 编译你的应用程序,你需要确保你使用的库是专门为 MinGW 编译的,或者在构建库时采用兼容的编译选项。
同样,如果你使用 MSVC 编译,你需要使用与 MSVC 兼容的库文件。
3、第三方库支持:
一些第三方库可能会提供适用于 MinGW 和 MSVC 的不同版本。在选择库时,你需要确保选择与你的编译器兼容的版本。
有些库可能提供了 CMake 或其他构建工具的支持,这些工具可以帮助你在不同的编译器下进行构建。
四、库引用方式
1、MSVC编译的库是xxx.dll和xxx.lib,MingW编译的库是libxxx.dll和libxxx.dll.a
其中lib文件和.a文件一般都是对dll的引导;或者是包含了所有的源码,作用和dll一样
2、Qt调用msvc的dll
INCLUDEPATH += C:/Users/Administrator/Desktop/new/CTK-master/build_msvc2017/CTK-build/Libs/PluginFramework
INCLUDEPATH += C:/Users/Administrator/Desktop/new/CTK-master/build_msvc2017/CTK-build/Libs/Core
LIBS += -LC:/Users/Administrator/Desktop/new/CTK-master/build_msvc2017/CTK-build/bin/Debug -lCTKCore -lCTKPluginFramework
INCLUDEPATH +是头文件路径
LIBS +是lib库路径,这个路径下需要同时有dll和lib文件
3、Qt调用mingw的dll
INCLUDEPATH += C:/Users/Administrator/Desktop/new/CTK-master/build_mingw64/CTK-build/Libs/PluginFramework
INCLUDEPATH += C:/Users/Administrator/Desktop/new/CTK-master/build_mingw64/CTK-build/Libs/Core
LIBS += -LC:/Users/Administrator/Desktop/new/CTK-master/build_mingw64/CTK-build/bin/ -llibCTKCore
LIBS += -LC:/Users/Administrator/Desktop/new/CTK-master/build_mingw64/CTK-build/bin/ -llibCTKPluginFramework
LIBS += C:/Users/Administrator/Desktop/new/CTK-master/build_mingw64/CTK-build/bin/libCTKCore.dll.a
LIBS += C:/Users/Administrator/Desktop/new/CTK-master/build_mingw64/CTK-build/bin/libCTKPluginFramework.dll.a
五、最后的选择
到底选择哪个版本,MinGW 比较方便,配置易上手适合初学者,MSVC功能强大,适合进阶,但是具体项目上要根据依赖的第三方库选择版本。
如果你的第三方库是msvc的,能可能就必须msvc,如果第三方库是mingw的可能就必须mingw。
跨平台开发的话,用的库基本上一定是Linux能用的库,而Linux能用的库一定是在mingw下支持会比较好。(mingw甚至能 include unistd.h 之类的Linux专用头文件),那就优选mingw。
注意Qt版本:msvc和MinGW版本的区别。msvc使用的是vs的编译器,这个版本主要用于PC开发;MinGW版本使用MinGW编译器,主要用于跨平台开发。两个版本的Qt配置方式完全不同。因此在网上搜配置方法时候,要加上msvc或者MinGW这样的关键字一块搜索。网上大部分内容是关于MinGW版本的配置方法,我本地使用的是msvc版本。Msvc版本配置比较简单;MinGW版本需要下载cmake自行编译opencv。
六、Qt中的各种编译器
1.uic:UI编译器,将.ui文件转化为ui_*.h文件
2.rcc:资源编译器,将.qrc文件转换成qrc_*.h文件
3.moc:元对象编译器,将含有Q_OBJECT的头文件转换成标准.h文件
4.qmake : 把pro文件编译成makefile文件,然后可以调用make命令
5.MSVC:微软VC++编译器
6.MinGW:gcc编译器
七、使用QLibrary加载动态库
使用QLibrary可以在程序运行时加载动态链接库。一个QLibrary的实例作用于一个单一的共享库上。QLibrary提供了一种平台无关的方式访问库中的函数。可以在构建QLibrary的实例时将要加载的库文件传入,也可以在创建实例后使用setFileName()显式的设置要加载的文件名。当加载库文件时,QLibrary会搜索所有平台特定的库位置,除非传入的文件名具有绝对路径。
如果传入的文件名具有绝对路径,那么会首先尝试加载该目录。如果该文件找不到,QLibrary会使用不同的平台特定的文件前缀或后缀再次尝试,比如Unix和Mac平台的"lib"前缀,Unix平台的".so"后缀,Mac平台的".dylib",Windows平台的".dll"。如果文件名不是绝对路径,QLibrary会修改搜索顺序,首先尝试系统特定的前缀和后缀,紧接着是指定的文件路径。
所以,基于QLibrary对库文件的搜索机制,我们推荐在传入库文件时只传入该库文件的基名,不写前缀或后缀。这样一来,同一份代码可以在不同的操作系统上工作,并且该机制会保证进行最小次数的搜索。
该类中最重要的函数是load(),该函数动态的加载库文件,isLoaded()可以用来检查库文件是否成功加载,而resolve()函数则用来解析库中的符号地址,主要是函数地址。并且,resolve()函数在库文件未被加载时会隐式的尝试加载它。多个QLibrary实例可以可以访问同一个库文件。因为,库文件一旦被加载,就会驻留在内存中直到应用程序终止。当然,我们可以使用unload()函数来尝试卸载一个库文件,但是如果有其他的QLibrary实例正在引用同一个库文件,unload()会调用失败,并且该库文件会在所以实例都调用了unload()后才被卸载。
QLibrary库的典型用法是去解析一个库中的导出符号,并调用该符号表示的C函数。这被称为“显式链接”,而相对的“隐式链接”是在编译过程的链接阶段完成的。如下面的代码显示的那样,改代码先加载一个库,解析其中的"mysymbol"符号,并在成功的情况下,调用该函数。如果由于某种原因出错了,例如库文件不存在或该符号未被定义,那么相应的函数指针将被设为空,不会发生实际的调用:
QLibrary myLib("mylib");
typedef void (*MyPrototype)();
MyPrototype myFunction = (MyPrototype) myLib.resolve("mysymbol");
if (myFunction)
myFunction();
但该符号必须被导出为C函数。也就是说,如果该库是由C++编译器编译的,那么该函数必须被包在extern "C"块中。并且在Windows平台上,还需要使用dllexport宏来修饰该函数。
出于方便,该类还提供了一个静态的resolve()函数,我们可以使用该函数来解析并调用一个库中的方法,而不需要先加载该库。如下代码所示:
typedef void (*MyPrototype)();
MyPrototype myFunction =
(MyPrototype) QLibrary::resolve("mylib", "mysymbol");
if (myFunction)
myFunction();
其实,除了静态的resolve()函数,该类还提供了一个静态的isLibrary()函数,该函数可以根据特定平台来判断一个文件是否是可被加载的库。其所使用的规则如下:
八、QT MinGW 怎样调用 VS的DLL库
Qt 库用的是MinGW版本,g++编译器生成,与VC++编译器不是同个体系。所以工程不可以使用testdll.h头文件和testdll.lib引入库文件。
使用了在pro文件添加的方式 失败!
修改.lib为.a的方式 失败
添加静态库 也失败。在绝望时刻 发现了这个方法!!!太好了
对于调用DLL的方法,Qt本身就有相应的类来实现。
例1:
#include "dialog.h"
#include <QApplication>
#include <QLibrary>
**typedef int (*func_Add)(int a, int b); //定义函数指针**(一定要与要调用的库里面的参数类型及返回类型一致)
typedef int (*func_Sub)(int a, int b);
typedef int (*func_Mul)(int a, int b);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLibrary mylib("testdll.dll");//testdll.dll的存放路径与.exe一致
if (mylib.load())
{
**//"?Add@Ctestdll@@QAEHHH@Z"等字符串其实就是动态库函数int Add(int a, int b)的变体,请使用微软工具DEPENDS.EXE查看得到**(最重要的一步 从?开始的所有内容都要复制)
func_Add f1 = (func_Add)mylib.resolve("?Add@Ctestdll@@QAEHHH@Z");
func_Sub f2 = (func_Sub)mylib.resolve("?Sub@Ctestdll@@QAEHHH@Z");
func_Mul f3 = (func_Mul)mylib.resolve("?Mul@Ctestdll@@QAEHHH@Z");
int rt = 0;
if (f1 != NULL)
{
rt = f1(10, 12);
}
if (f2 != NULL)
{
rt = f2(10, 12);
}
if (f3 != NULL)
{
rt = f3(10, 12);
}
}`在这里插入代码片`
(完美运行!!!)
Dialog w;
w.show();
return a.exec();
}
例2:
ZLGLIB.h
#ifndef _ZLBLIB_
#define _ZLBLIB_
#pragma once
#include "ControlCAN.h"
extern VCI_OpenDevice OpenDevice;
extern VCI_CloseDevice CloseDevice;
extern VCI_InitCAN InitCAN;
extern VCI_ReadBoardInfo ReadBoardInfo;
extern VCI_ReadErrInfo ReadErrInfo;
extern VCI_ReadCANStatus ReadCANStatus;
extern VCI_GetReference GetReference;
extern VCI_SetReference SetReference;
extern VCI_GetReceiveNum GetReceiveNum;
extern VCI_ClearBuffer ClearBuffer;
extern VCI_StartCAN StartCAN;
extern VCI_ResetCAN ResetCAN;
extern VCI_Transmit Transmit;
extern VCI_Receive Receive;
BOOL ZLGLIB();
#endif
ZLGLIB.cpp
//
#include "stdafx.h"
#include "ZLGLIB.h"
#include <QLibrary>
VCI_OpenDevice OpenDevice;
VCI_CloseDevice CloseDevice;
VCI_InitCAN InitCAN;
VCI_ReadBoardInfo ReadBoardInfo;
VCI_ReadErrInfo ReadErrInfo;
VCI_ReadCANStatus ReadCANStatus;
VCI_GetReference GetReference;
VCI_SetReference SetReference;
VCI_GetReceiveNum GetReceiveNum;
VCI_ClearBuffer ClearBuffer;
VCI_StartCAN StartCAN;
VCI_ResetCAN ResetCAN;
VCI_Transmit Transmit;
VCI_Receive Receive;
QLibrary lib("ControlCAN.dll");
BOOL ZLGLIB()
{
OpenDevice = NULL;
CloseDevice = NULL;
InitCAN = NULL;
ReadBoardInfo = NULL;
ReadErrInfo = NULL;
ReadCANStatus = NULL;
GetReference = NULL;
SetReference = NULL;
GetReceiveNum = NULL;
ClearBuffer = NULL;
StartCAN = NULL;
ResetCAN = NULL;
Transmit = NULL;
Receive = NULL;
if(!lib.load())
{
return FALSE;
}
OpenDevice = (VCI_OpenDevice)lib.resolve("VCI_OpenDevice");
CloseDevice= (VCI_CloseDevice)lib.resolve("VCI_CloseDevice");
InitCAN= (VCI_InitCAN)lib.resolve("VCI_InitCAN");
ReadBoardInfo= (VCI_ReadBoardInfo)lib.resolve("VCI_ReadBoardInfo");
ReadErrInfo= (VCI_ReadErrInfo)lib.resolve("VCI_ReadErrInfo");
ReadCANStatus= (VCI_ReadCANStatus)lib.resolve("VCI_ReadCANStatus");
GetReference= (VCI_GetReference)lib.resolve("VCI_GetReference");
SetReference= (VCI_SetReference)lib.resolve("VCI_SetReference");
GetReceiveNum= (VCI_GetReceiveNum)lib.resolve("VCI_GetReceiveNum");
ClearBuffer= (VCI_ClearBuffer)lib.resolve("VCI_ClearBuffer");
StartCAN= (VCI_StartCAN)lib.resolve("VCI_StartCAN");
ResetCAN= (VCI_ResetCAN)lib.resolve("VCI_ResetCAN");
Transmit= (VCI_Transmit)lib.resolve("VCI_Transmit");
Receive= (VCI_Receive)lib.resolve("VCI_Receive");
if (OpenDevice == NULL || CloseDevice == NULL|| InitCAN == NULL
|| ReadBoardInfo == NULL || ReadErrInfo == NULL || ReadCANStatus == NULL
|| GetReference == NULL || SetReference == NULL || GetReceiveNum == NULL
|| ClearBuffer == NULL || StartCAN == NULL || ResetCAN == NULL
|| Transmit == NULL || Receive == NULL )
{
return FALSE;
}
return TRUE;
}