- 2020/6/21
序言
-
IDE工具
- VSCode
- 语法检测:头文件(include头文件) + 编译器内置的语法
- 添加头文件检测:ctrl + shift + p->C++配置编辑,添加头文件所在目录;
- VSCode
-
控制终端
- 命令
- 环境变量
- PATH: echo %PATH%
- set指令显示所有环境变量
- PATH:window执行程序所搜索的路径;每个路径使用
;
分号分隔 - 有可视化的设置的
-
VisualStudio 开发环境的设置
- 使用脚本的文件来设置,脚本是vcvars64.bat
- 设置vcvars64.bat的目录到PATH环境变量;
PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build
- 使用可视化设置环境变量,需要重启终端;
-
开发工具
- cl 编译器
- link 连接器 (PE格式)
- lib 库归档工具(静态库:目标文件的归档)
- dumpbin PE与目标文件格式分析工具
工程组织与编译器
-
编译器
-
cl 源代码
编译/链接为执行文件/EHsc
/MD
-
/utf-8
/source-charset:utf-8
/execution-charset:utf-8
- 头文件目录
-
-
说明:
- 第一次输出,汉字可能是乱码
- 使用chcp命令改变终端的代码页。utf-8的code page= 65001
chcp 65001
- 在区域/语言中直接设置编码(整个系统设置为utf-8)
- opencv不识别中文目录;(系统设置编码)
- 使用chcp命令改变终端的代码页。utf-8的code page= 65001
- 第一次输出,汉字可能是乱码
-
链接器
- link
- cl负责编译 : 检测语法,生成目标文件
- link负责链接:负责生成PE格式文件,需要信息动态库信息
- link 选项 目标文件s
/out
/MACHINE:X64
- 第三方的库的库目录
- 第三方的库名
- link
静态库
- 前提:lib/dll这两个文件解释清楚;
- lib:静态库
- dll:动态库
实现一个库函数
gk_math.h
#ifndef GK_MATH_H
#define GK_MATH_H
extern int gk_add(int, int);
#endif
gk_math.cpp
#include "gk_math.h"
int gk_add(int p1, int p2){
return p1 + p2;
}
编译成静态库
@rem 静态库的编译
@rem 编译
@cl /c /EHsc /MD /nologo /source-charset:utf-8 /execution-charset:utf-8 /Fo:gkmath.obj gk_math.cpp
@rem 静态库链接
lib /MACHINE:X64 /nologo /OUT:gkmath.lib gkmath.obj
代码的组织
使用shell脚本或者bat处理脚本,比较麻烦的是多个操作需要写成多个bat文件;实际引入一个专门的工程组织脚本Makefile;
-
工程组织的方式:
- 通用
- Makefile
- CMake
- QMake
- 个性化:
- Visual Studio
- Qt Creator
- QMake
- Eclipse C++
- C++ Builder
- 通用
-
Makefile脚本的语法:Makefile
- 定义变量
- 任务(Task)
- 依赖(任务依赖另外一任务)
- 指令
Makfile例子
CL_FLAGS = /c \
/EHsc \
/MD \
/nologo \
/source-charset:utf-8 \
/execution-charset:utf-8
LINK_FLAGS = /MACHINE:X64 \
/nologo
OBJS = gkmath.obj
SOURCES = gk_math.cpp
TARGETS = gkmath.lib
main:$(TARGETS) main.cpp
@ cl /nologo /MD /Fe:main.exe main.cpp $(TARGETS)
$(TARGETS):$(OBJS)
@lib $(LINK_FLAGS) /OUT:$(TARGETS) $(OBJS)
$(OBJS): gk_math.h gk_math.cpp
@cl $(CL_FLAGS) /Fo:$(OBJS) $(SOURCES)
clean:
@del *.exe *.obj *.lib 2>/Nul
使用静态库
- 在链接的时候使用静态库
#include <stdio.h>
#include "gk_math.h"
int main(int argc, char **argv, char **arge){
printf("C++程序编程!静态库调用结果:%d\n", gk_add(45, 55));
return 0;
}
- 编译脚本
CL_FLAGS = /c \
/EHsc \
/MD \
/nologo \
/source-charset:utf-8 \
/execution-charset:utf-8
LINK_FLAGS = /MACHINE:X64 \
/nologo
OBJS = gkmath.obj
SOURCES = gk_math.cpp
TARGETS = gkmath.lib
main:$(TARGETS) main.cpp
@ cl /nologo /MD /Fe:main.exe main.cpp $(TARGETS)
$(TARGETS):$(OBJS)
@lib $(LINK_FLAGS) /OUT:$(TARGETS) $(OBJS)
$(OBJS): gk_math.h gk_math.cpp
@cl $(CL_FLAGS) /Fo:$(OBJS) $(SOURCES)
clean:
@del *.exe *.obj *.lib 2>/Nul
- 在代码中使用静态库
#include <stdio.h>
#include "gk_math.h"
#pragma comment(lib, "gkmath.lib")
int main(int argc, char **argv, char **arge){
printf("C++程序编程!静态库调用结果:%d\n", gk_add(45, 55));
return 0;
}
// cl /nologo /MD /Fe:main.exe main_lib.cpp
- 编译命令:
cl /nologo /MD /Fe:main.exe main_lib.cpp
- 回顾
- 开发工具
-
cl编译器 (mac:clang/g++,posix:gnu g++,hp:acc: intel:cc, sun:cc)
- 默认是调用link默认链接
- /link 后面直接包含link选项
- 默认是调用link默认链接
-
link连接器(posix:ld)
- link步骤很多编译器中默认自动调用
-
lib(ar)
- 静态库
-
dumpbin(nm)
- 分析目标文件与PE执行文件
-
nmake(make)
- nmake task
- nmake task -f makefile文件
-
- vcvars64.bat / vcvars32.bat (mac/linux不需要单独的设置,默认在系统设置)
- 空格转义: "C:\Program Files (x86)\Microsoft Visual Studio"
- makefile的语法
- 任务目标:依赖
- 指令(使用tab开始)
- 伪任务目标:
- 文件不存在
- 任务目标:依赖
- 开发工具
动态库
实现代码
- gkmath.h文件
#ifndef GK_MATH_H
#define GK_MATH_H
extern int gk_add(int, int);
#endif
- gkmath.cpp文件
#include "gkmath.h"
int gk_add(int p1, int p2){
return p1 + p2;
}
编译动态库
-
准备:link选项
- /DLL:不需要main入口
- /IMPLIB : 指定链接的时候产生导入的符号,使用lib静态库的方式存放;
- /EXPORT : 指定哪些函数可以被别人调用
= /DEF:DEF导出函数的描述文件
- /MACHINE:指定CPU结构X64/X86/ARM/ARM64/EBC
- /OUT:指定输出文件名,dll输出名字
编译脚本
# 编译选项设置一个变量
CL_ARGS=/EHsc /MD /source-charset:utf-8 /execution-charset:utf-8 /nologo
# 链接选项设置一个变量
LINK_ARGS=/MACHINE:X64 /NOLOGO /DLL
# 文件设置成变量
SOURCES = gkmath.cpp
OBJS = gkmath.obj
OUTLIBS = gkmath.lib
OUTDLLS = gkmath.dll
# 目标指令实现
$(OUTDLLS):$(SOURCES)
@cl /c $(CL_ARGS) /Fo:$(OBJS) gkmath.cpp
@link /MACHINE:X64 /NOLOGO /DLL /OUT:$(OUTDLLS) /IMPLIB:$(OUTLIBS) /EXPORT:gk_add $(OBJS)
clean:
@del *.obj *.lib *.dll *.ilk *.exe *.exp 2>/Nul
main1:call_auto_dll.cpp
@cl /c $(CL_ARGS) /Fo:main1.obj call_auto_dll.cpp
@link /OUT:main1.exe main1.obj
动态库的调用方式1
直接使用dll调用函数(lib根本不需要,只需要dll) 【不推荐】
-
准备技术:
- HMODULE = LoadLibraryA(LPCSTR dllfilename): 加载动态库到内存
- FARPROC = GetProcAddress(HMODULE hModule, LPCSTR functioname)
- 函数类型转换
- 调用
- 释放dll空间:BOOL FreeLibraray(HMODULE)
- 代码实现
#include <stdio.h>
#include <windows.h>
// typedef int(*type_f)(int,int);
int main(int argc, const char**argv){
// 加载dll模块
HMODULE h = LoadLibraryA("gkmath.dll");
if (h == NULL){
printf("加载失败!\n");
return -1;
}
printf("加载成功!");
// 查找函数
FARPROC f = GetProcAddress(h, "gk_add"); // ?gk_add@@YAHHH@Z
printf("%p\n", f);
// 类型转换
// type_f myfunc = (type_f)f;
int (*myfunc)(int, int) = (int(*)(int, int))f;
// 调用
printf("结果:%d\n", myfunc(45,55));
// 释放模块
FreeLibrary(h);
}
- /I : 指定头文件的路径
@cl /c /utf-8 /nologo /MD /Fo:call_manual_dll.obj call_manual_dll.cpp
@link /NOLOGO /OUT:main.exe /DYNAMICBASE Kernel32.lib call_manual_dll.obj
动态库的调用方式2
在编译的时候调用函数(根本不需要dll,只需要lib,但是运行的时候需要dll,不需要lib)
代码实现:
#include <stdio.h>
#include "gkmath.h"
#pragma comment(lib, "gkmath.lib") // 强调,不推荐使用
int main(int argc, const char*argv[]){
printf("调用结果:%d\n", gk_add(55,55));
return 0;
}
- 调用动态库基地址
main1:call_auto_dll.cpp
@cl /c $(CL_ARGS) /Fo:main1.obj call_auto_dll.cpp
@link /OUT:main1.exe main1.obj
Qt编译环境设置
-
掌握的重点:
- GUI (Qt应用 + QtWidgets(QDialog))
- QApplication
Qt程序绘制简单窗体
#include <iostream>
// Qt GUI模块:QtWidgets
// Qt 底层模块:QtCore
// Qt 图形的绘制模块QtGui
#include <QtWidgets/QApplication>
#include <QtWidgets/QDialog>
int main(int argc, char **argv){
// 1. 构建Qt应用:QApplication
QApplication app(argc, argv);
// 2. 窗体创建QDialog
QDialog dlg;
// 窗体的属性(函数对getter/setter)
dlg.setWindowTitle("Qt开发");
dlg.resize(640, 480);
dlg.move(200,200);
dlg.setFixedSize(dlg.width(), dlg.height()); // 不允许改变窗体大小
dlg.show();
// 3. 消息循环处理int QApplication.exec()
int status = app.exec(); // block函数(消息循环)
// 4. 退出程序,返回状态码给系统0-255(-1=255)
return status;
}
// dll所在的路径,必须设置到PATH环境变量
- 编译脚本
INCLUDES = /I "D:/Qt/Qt5.13.0/5.13.0/msvc2017_64/include"
LIBS = /LIBPATH:"D:/Qt/Qt5.13.0/5.13.0/msvc2017_64/lib" \
/DYNAMICBASE \
"Qt5Widgets.lib" \
"Qt5Gui.lib" \
"Qt5Core.lib"
CL_ARGS = /EHsc \
/MDd \
/source-charset:utf-8 \
/execution-charset:utf-8 \
/nologo
LINK_ARGS = /MACHINE:X64 /NOLOGO
main:qmain.cpp
@cl /c $(CL_ARGS) /Fo:main.obj $(INCLUDES) qmain.cpp
@link $(LINK_ARGS) $(LIBS) /OUT:main.exe main.obj
clean:
@del *.exe *.obj *.exp 2>/Nul
- 问题
如果出现 qt.qpa.plugin: Could not find the Qt platform plugin "windows" in""
解决方法:windeployqt main.exe
OpenCV的环境
- 打开cmake;
- 将opencv目录和处理结果存放目录依次键入;
- Configure后调试无误;
- 单击Generate在指定的存放目录生成项目工程;
- 在项目工程目录下找到OpenCV.sln;
- 用VS2019打开OpenCV.sln文件,加载项目;
- 将BUILD_ALL与INSTALL重新生成。
作业
-
写Qt程序,并编译链接成执行文件,且能执行成功;
- 动态的使用
- 编译器/连接器
提交作业
#include <iostream>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDialog>
int main(int argc, char **argv){
// 1. 构建Qt应用
QApplication app(argc, argv);
// 2. 窗体创建
QDialog dlg;
// 窗体的属性设置
dlg.setWindowTitle("作业1");
dlg.resize(1000, 800);
dlg.move(300,300);
dlg.setFixedSize(dlg.width(), dlg.height()); // 不允许改变窗体大小
dlg.show();
// 3. 消息循环处理
int status = app.exec(); // block函数(消息循环)
// 4. 退出程序,返回状态码给系统
return status;
}
- 作业所用编译脚本
INCLUDES = /I "D:/Qt/Qt5.13.0/5.13.0/msvc2017_64/include"
LIBS = /LIBPATH:"D:/Qt/Qt5.13.0/5.13.0/msvc2017_64/lib" \
/DYNAMICBASE \
"Qt5Widgets.lib" \
"Qt5Gui.lib" \
"Qt5Core.lib"
CL_ARGS = /EHsc \
/MDd \
/source-charset:utf-8 \
/execution-charset:utf-8 \
/nologo
LINK_ARGS = /MACHINE:X64 /NOLOGO
main:qmain.cpp
@cl /c $(CL_ARGS) /Fo:main.obj $(INCLUDES) qmain.cpp
@link $(LINK_ARGS) $(LIBS) /OUT:main.exe main.obj
clean:
@del *.exe *.obj *.exp 2>/Nul