env
gcc/g++
- 区别:
gcc 只要是 GCC 支持编译的程序代码,都可以使用 gcc 命令完成编译(根据后缀名)
gcc -xc++ xxx.cpp
-x
手动指定代表编译方式
g++ 无论目标文件的后缀名是什么,该指令都一律按照编译 C++ 代码的方式编译
C++ 标准和 C 语言标准的语法要求有区别 C++更严格
单纯的 gcc 命令是无法自动链接标准库文件
gcc -xc++ demo.cpp -lstdc++ -shared-libgcc
使得 gcc 在编译 C++ 程序时可以链接必要的 C++ 标准库
可以认为 g++ 指令等同于gcc -xc++ -lstdc++ -shared-libgcc
指令 - 命令:
无选项编译链接
gcc hello.c
未指定输出文件,Linux上默认输出为a.out,window中默认为a.exe选项 -o(理解成output)
gcc -o hello hello.c
对生成的目标进行重命名,linux下为hello.out,windows下为hello.exe选项 -E
gcc -E hello.c -o hello.i
把源代码预处理输出为hello.i文件选项 -S
gcc -S hello.i
把预处理输出的文件hello.i汇编成hello.s文件选项 -c
gcc -c hello.s
把汇编成hello.s文件编译输出为hello.o文件,输出文件为二进制目标文件无选项链接
gcc hello.o -o hello1
把hello.o目标文件链接成最终可执行文件hello1.exe,其实是调用ld命令进行链接选项 -O
gcc -O3 hello.c -o hello2
使用编译优化级别,1~3,级别越大优化效果越好,但编译的时候会长选项 -M
gcc -M hello.c
查看生成文件关联信息,包含目标文件所依赖的所有源代码,该命令会在预处理阶段中执行(选项 -MM,与-M相比,会忽略#include文件的依赖关系)多个文件一起编译/多个.o二进制目标文件编译后链接
gcc hello.c multi.c -o multi
gcc multi.o hello.o -o multi1
选项 -include
gcc hello.c -include multi.c -o hello3
相当于在文件中加入#include选项 -Idir
当你使用#include “file”的时候,会先到你定制的目录里面查找选项 -L
gcc -o hello hello.c -L/home/hello/lib
定制编译的时候使用的库,/home/hello/lib作为第一个寻找库文件的目录选项 -l
gcc -o hello hello.c -ltest
定制编译的时候使用的库,寻找动态链接库文件libtest.so,如果加上-static,表示寻找libtest.a静态链接库选项 -w
gcc -w multi.c hello.c -o multi
表示不生成任何警告的信息 (-Wall:生成所有警告信息)选项 -g
gcc -g hello.c -o hello5
在编译的时候加入debug调试信息,用于gdb调试,文件会比一般大一点选项 -share
尽量的使用动态库,所以生成文件比较小,但是必须是系统有动态库选项 -shared
生成共享目标文件,通常用在建立共享库
vscode工程配置
单一文件小工程
c_cpp_properties.json
配置编译器环境1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/include",
"/usr/include/**",
"/usr/local/**",
"/usr/lib/**",
"${vcpkgRoot}/x64-windows/include"
],//头文件路径 指明了C/C++标准库、用户头文件以及vcpkg库所在位置
"defines": [],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}tasks.json
添加构建(编译、链接等)任务 CMakeLists.txt可以替代1
2
3
4
5
6
7
8
9
10
11{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "shell",
"command": "g++",//选择什么编译器
"args":["-g", "${file}", "-std=c++11", "-o", "${fileBasenameNoExtension}.out"]//编译的时候用到的参数
}
]
}launch.json
调试时的配置文件 单纯只是想生成可执行文件,其实这个文件没有也可以1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"version": "0.2.0",
"configurations": [
{
"name": "gcc build and debug active file",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}.out",//生成的可执行文件名 要与tasks.json文件中指定生成的文件名相同
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "build", //要与tasks.json文件中的label一致
"miDebuggerPath": "/usr/bin/gdb"
}
]
}
大型项目(Cmake)
项目结构:
- build
- include
- src
- bin
- lib
- CMakeLists.txt
cmake
基于CMakeLists.txt生成makefile文件make
编译makefile文件,生成可执行文件
推荐编写c_cpp_properties.json,该文件中includePath参数可以很好指定头文件目录,这样就算没有CMakeLists.txt文件,我们在vscode编写程序的时候它也自动包含了头文件,这样就可以具有代码提示功能 。而且,有了CMakeLists.txt以后,即使写了c_cpp_properties.json文件,也并不影响项目脱离vscode单独在shell/cmd里编译运行
调试
加入tasks.json 和 launch.json
build后在build文件夹下生成了result可执行文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28//tasks.json
{
"options": {
"cwd": "${workspaceFolder}/build"
},
"tasks": [
{
"label": "cmake",
"command": "cmake",
"args": [
"-DCMAKE_BUILD_TYPE=Debug",
".."
]
},
{
"label": "make",
"command": "make"
},
{
"label": "CMake Build",
"dependsOn": [
"cmake",
"make"
]
}
],
"version": "2.0.0"
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
//"program": "enter program name, for example ${workspaceFolder}/a.out",
"program": "${workspaceFolder}/build/result",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"preLaunchTask": "CMake Build"
}
]
}CMakeLists
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58# 指定最低版本
cmake_minimum_required(VERSION 2.8)
# 指定项目名称
project(demo)
# 为当前路径以及子目录的源文件加入由-D预编译定义
# add_definitions(-DFOO -DDEBUG ...)
# 设置C++编译参数(CMAKE_CXX_FLAGS是全局变量)
# 设置指定的C++编译器版本是必须的,如果不设置,或者为OFF,则指定版本不可用时,会使用上一版本。
# 指定为C++11 版本
set(CMAKE_CXX_STANDARD 11)
if (MSVC)
add_compile_options(/W4 /WX)
else()
add_compile_options(-Wall -Wextra -pedantic -Werror)
endif()
# 设置变量
set(ROOT_DIR /home/tf)
# 头文件查找目录
include_directories(
${ROOT_DIR}/include
${ROOT_DIR}/include/third_party
)
# 库文件查找目录
link_directories(${ROOT_DIR}/lib)
# sub_dir指定包含CMakeLists.txt和源码文件的子目录位置
# binary_dir是输出路径, 一般可以不指定
add_subdirecroty(sub_dir [binary_dir])
# 把src目录下所有源文件写入变量DIR_SRCS
aux_source_directory(./src DIR_SRCS)
# 以DIR_SRCS为源文件生成目标文件a.out
add_executable(a.out ${DIR_SRCS})
# 创建库文件
# add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] source1 source2 ... sourceN)
# 目标文件链接的库
target_link_libraries(a.out
pthread
boost_thread
boost_system
)
- PROJECT_SOURCE_DIR:包含PROJECT()命令的最近一个CMakeLists.txt文件所在的文件夹路径
- CMAKE_SOURCE_DIR:最外层CMakeLists.txt所在目录
- _SOURCE_DIR:CMakeLists中没有这个预定义的变量名
CMAKE_CURRENT_SOURCE_DIR 当前处理的CMakeLists.txt所在的目录。
PROJECT_BINARY_DIR/CMAKE_BINARY_DIR/_BINARY_DIR: 运行cmake命令的目录,即工程编译发生的路径
EXECUTABLE_OUTPUT_PATH: 指定最终的可执行文件的位置
LIBRARY_OUTPUT_PATH: 设置库文件的输出目录
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/../bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/../bin)
上面两条语句分别设置了Debug版本和Release版本可执行文件的输出目录set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 开启后,其生成的文件compile_commands.json,包含所有编译单元所执行的指令
visual studio (MSVC) 解决方案目录结构解析
默认目录结构:
1 | Solution // $(SolutionDir) |
stdafx.h & stdafx.cpp & .pch
.pch 用于保存预编译好的文件,一般会将工程中较稳定的不会经常修改的代码预编译好放在其中,以后不再编译这部分,直接使用预编译结果,从而提高编译的速度。.pch 通过编译 stdafx.cpp 生成,编译器通过 stdafx.h 使用 .pch,所有在指令 #include “stdafx.h” 前的代码都是预编译的,编译器将跳过跳过该编译指令,转而编译之后的代码。
将解决方案和项目放在同一目录中则将上述Project文件夹释放, sourcecode和sln在一起, 两个x64-Debug合并1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30Solution
├─.vs
│
├─Project.sln
├─Project.vcxproj
├─Project.vcxproj.filters
├─Project.vcxproj.user
├─source.cpp
│
└─x64
└─Debug
│ Project.exe
│ Project.exe.recipe
│ Project.ilk
│ Project.log
│ Project.pdb
│ Project.vcxproj.FileListAbsolute.txt
│ vc143.idb
│ vc143.pdb
│ vcpkg.applocal.log
│ source.obj
│
└─Project.tlog
CL.command.1.tlog
CL.read.1.tlog
CL.write.1.tlog
link.command.1.tlog
link.read.1.tlog
link.write.1.tlog
Project.lastbuildstate
lib&dll 动态链接静态链接:
静态库:在链接中,连接器将从静态库文件取得所需的代码,复制到生成的可执行文件中,可执行文件中包含了库代码的一份完整拷贝;缺点是被多次使用就会有多份冗余拷贝。即静态库中的指令都全部被直接包含在最终生成的 EXE 文件中了。在vs中新建生成静态库的工程,编译生成成功后,只产生一个.lib文件
动态库:动态链接库是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。在vs中新建生成动态库的工程,编译成功后,产生一个.lib文件和一个.dll文件
静态库中的lib包含函数代码本身(包括函数的索引也包括实现),编译时直接将代码加入程序
动态库中的lib包含了函数所在的DLL文件和文件中函数位置的信息(索引),函数实现代码由运行时加载在进程空间中的DLL提供
lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。
生成动态库:
调用动态库:
- 隐式链接:
- 将dll放到工程的工作目录
- 设置项目属性->vc++目录->库目录设为lib所在的路径
- 将lib添加到项目属性—链接器—输入—附加依赖项(或者直接在源代码中加入#pragma comment(lib, “**.lib”))
#pragma comment(lib,"..\\debug\\TestLib.lib")
;其中的反斜杠要用双反斜杠,因为它是程序解释的双引号包括的字符串,需要转义一下,要区别include,#include “..\TestVideoApplication.h”中并不是由程序解释的字符串,所以不用转义 - 在源文件中添加.h头文件 (头文件包含lib中说明输出的类或符号原型或数据结构。应用程序调用lib时,需要将该文件包含入应用程序的源文件中)
- 显示链接:
略
生成静态库:
静态库项目没有main函数 创建项目后添加.h文件,添加相应的导出函数、变量或类
使用静态库:
- 设置项目属性->vc++目录->库目录为lib所在的路径
- 将lib添加到项目属性—链接器—输入—附加依赖项(或者直接在源代码中加入#pragma comment(lib, “**.lib”))
- 在源文件中添加.h头文件
静态链接LIB的优点是免除挂接动态链接库,缺点是EXE大,版本控制麻烦
动态链接DLL的优点是文件小,版本更换时换DLL就行,缺点是多了点文件。动态链接若是被多个进程使用,会更加方便和节省内存
https://www.cnblogs.com/TenosDoIt/p/3203137.html
vs 项目属性页中一些宏定义/选项解释:
调试栏目下的所有选项都是为了调试服务的,如果不用调试按钮,这些选项就不起作用。VC++目录以及C/C++栏目是给编译器起作用的,如告诉编译器在哪里寻找头文件、库文件之类,或者设置其他一些编译器选项
$(Configuration) / $(ConfigurationName)
配置名 release、debug、simulation等$(IntDir)
编译器使用的中间目录,产出obj文件$(OutDir)
链接器使用的输出目录 产出中间生成的链接器如.ilk和.pdb文件等 值由VS项目的“输出目录”属性决定 “输出目录”是界面上的提示用于接收用户输入的配置信息,然后把这个具体的配置信息存入系统内容的变量$(OutDir)中$(TargetDir)输出文件所在的目录
在生成exe文件后自动赋予值为exe文件所在位置 最终决定exe文件所在的位置TargetFileName 目标输出文件名,包括扩展名 TargetName 目标输出名,不包括扩展名
TargetPath
目标输出文件的全路径名 $(TargetDir)和$(TargetPath)的值由VS项目的“输出文件”属性决定(.exe) 输出文件默认 = $(OutDir)$(TargetName)$(TargetExt) 默认情况下“输出目录”OutDir和TargetDir两个属性对应的目录是一样的,如果两个不一样,则中间生成的链接器如.ilk和.pdb文件等在输出目录,而最终生成的.exe文件在“输出文件”属性设置的目录中
“调试”中的“工作目录”
即工程目录项 这个属性默认情况下是空的,但默认就是工程配置文件.vcproj所在目录 “工作目录”属性作用是程序运行后唯一识别的默认目录,即工作后只认识这个目录 (可以将所依赖的lib和dll库文件所在目录设为工作目录,但一般是把lib放在解决方案下的Lib目录中,把dll放在解决方案下的Bin目录中)程序运行时生成的未指定绝对路径的文件默认就是从工作目录开始的
工作目录也是程序运行过程中默认读取的目录。对于dll,如果是程序运行前就进入内存像静态链接,dll就可以放入exe所在的执行目录,如果dll是运行中动态加载的一般放在工作目录,比如插件。工作目录就是运行期间唯一能识别的默认目录,工作目录与执行目录可以不同 生成的exe文件和工作目录无关
vs中工作目录的设置是给调试(包括Debug和release)用的,启动调试后,启动一个新进程,自动把这个新进程的工作目录设置为vs项目属性中的工作目录,然后新进程启动对应的exe程序。但是如果不使用vs的调试启动exe,而是直接双击exe文件启动一个新进程时,会自动把这个新进程的工作目录设置为exe文件所在的目录。如果发布的时候不把工作目录内的东西拷到exe所在的目录内,就会运行出错.
“调试”栏目中的“命令(Command)”属性项:
表示调试器要启动的exe文件的位置 $(TargetPath) 一般情况下它代表的值就等于“输出文件”属性代表的值 人为更改后若命令所在目录没找到命令所指则报错“链接器”栏目下的“输入”选项下的“附加依赖项”项:
程序链接时使用的静态库。相当于链接已经编译好了的“代码” (这里只需要库名称即可,搜索路径在其他地方设置)
“附加依赖性的设置”等同于在代码中写“#pragma comment(lib, “库名称.lib”) ”语句
总结:
常规->输出目录 决定outdir决定中间产生的链接器位置, 常规->中间目录决定indir决定编译的中间文件obj位置
调试->命令决定调试时该去哪找exe文件, 调试->工作目录决定程序调试运行时找/生成东西的默认路径(单点exe为exe本身路径)
VC++->库目录凡是有带lib就要加lib路径
链接器->常规->输出文件决定targetpath决定exe位置
链接器->输入->附加依赖项有lib则加(可代码替代)
范例
Solution: GMA 动态链接库: ChocolateMilk 应用程序: PureMilk 第三方库(动态): log4cxx
- GMA解决方案目录
- PureMilk和ChocolateMilk是项目目录(头文件和源文件)
- Lib目录用于存放导入库或者静态库(包括第三方库和自己的项目)
- Include用于存放第三方库的头文件(第三方库所有内容分布在Lib、Include和Bin中)
- Bin目录存放所有动态链接库和执行档,包括自己的产出和第三方库,区分Release和Debug两个版本。另外,程序运行过程中需要外部的数据文件和启动时需要的配置文件等等都可放于该目录
- Temp用于存放临时生成文件,其中Compile存放编译器编译时生成的obj文件,Link存放链接器的输出文件。
pros:
制作安装包时我们只需将“/GMA/Bin/Release/”目录下的所有文件打包
发布和转移源码的时候我们可以打包除了Temp目录以外“/GMA/”下面的所有文件和目录(如果不需要执行档,也可不包括Bin)
项目配置:(首先将配置改成All Configuration(全部配置),可以同时修改Debug和Release的部分)
设置ChocolateMilk:
- Output Directory(输出目录,链接器)栏位填入:
$(SolutionDir)\Temp\Link\$(ProjectName)\$(ConfigurationName)
- Intermediate Directory(中间目录,编译器)栏位填入:
$(SolutionDir)\Temp\Compile\$(ProjectName)\$(ConfigurationName)
- 构建结束后拷贝动态链接库到“/GMA/Bin/Release/”和“/GMA/Bin/Debug/”,拷贝导入库到“/GMA/Lib/”(若不设置,此时生成的dll和lib都在上面设置的输出目录中)通常都会在Debug版本的输出库后面加上字母“d”以表示这是Debug版本
链接器->高级->Import Library导入库填$(TargetDir)$(TargetName)d.lib
生成事件->生成后事件->命令行填脚本程序移动
Debug配置:copy $(TargetPath) $(SolutionDir)\Bin\$(ConfigurationName)\;
copy $(TargetDir)$(TargetName)d.lib $(SolutionDir)\Lib\;
Release配置:copy $(TargetPath) $(SolutionDir)\Bin\$(ConfigurationName)\;
copy $(TargetDir)$(TargetName).lib $(SolutionDir)\Lib\;
设置应用程序项目PureMilk:
1,2同上
- 构建结束后拷贝执行文件到“/GMA/Bin/Release/”或“/GMA/Bin/Debug/”
在Command Line中填入,All配置下:copy $(TargetPath) $(SolutionDir)\Bin\$(ConfigurationName);
- 调试时运行“/GMA/Bin/Debug/”或“/GMA/Bin/Release/”下面的执行文件,并以“/GMA/Bin/Debug/”或“/GMA/Bin/Release/”为工作目录
Command 命令填:$(SolutionDir)\Bin\$(ConfigurationName)\$(TargetFileName)
Working Directory 工作目录填:$(SolutionDir)\Bin\$(ConfigurationName)\
至此便可编译并调试
推荐链接lib及dll文件方式
直接把 dll 所在目录加到 PATH 里,则会有潜在冲突的危险;
直接拷贝到 VS 目录下,测试工程太多且有新版本的动态链接库更新时,需要更新若干次,多次拷贝、粘贴
直接添加到系统的 PATH 变量里:
最简单直接,但会影响全局的 PATH 设置,尤其是包含着大量测试用的 dll 时在 Visual Studio 全局设置里,把 dll 所在目录添加到 PATH 里:
通过 Visual Studio 菜单 ==> 工具 ==> 选项 ==> 项目和解决方案 ==> VC++目录,在下拉框里选择”可执行文件”,然后把 dll 所在路径添加进去 (? 存疑)直接把所有 dll 拷贝到 Visual Studio 工程目录下,或是拷贝到生成可执行文件的文件夹(默认情况下是 Debug 或 Release 目录)下:
很简单,但是当你有若干个工程时,每次更新 SDK 及其 dll 文件,就要把所有的工程都更新,不符合文件唯一性的工程性准则。在调试程序时,让 Visual Studio 帮你切换当前工作目录到 dll 相应的目录下:
在 Visual Studio ==> Project ==> Properties ==> Select Configuration ==> Configuration Properties ==> Debugging ==> Working directory 里填上 dll 所在目录,这样当在调试程序时,Visual Studio 会把当前工作目录切换到这个目录下,从而会自动读取本目录下的 dll 文件。简单!但在切换了当前工作目录后,可能会找不到程序的配置文件,在程序里写的诸如”./config.ini”全部都找不到了;另外,要把所有的 dll 都放到这个工作目录里,否则一样会提示说找不到 xxx.dll 的问题。
最后一个方法,认为最好, 在 Visual Studio 工程属性里把一个目录临时添加到 PATH 环境变量里:
MSDN 上也有类似的介绍:How to: Set Environment Variables for Projects,方法很简单,在 “工程属性” ==> “调试” ==> “环境”里,添加类似如下所示的内容:PATH=%PATH%;$(TargetDir)\DLLS
就可以把$(TargetDir)\DLLS
临时添加到该工程所属的系统 PATH 里。
https://blog.csdn.net/waitforfree/article/details/8622059
项目开发的时候,相对路径是以project.vcproj为起点,但是项目在发布后,相对路径变成了以 exe 文件所在的目录为起点