- 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