mac及linux C++环境配置

mac及linux C++环境配置

linux系统介绍

  • 什么是linux系统?
  • linux是开源的操作系统

多用户多任务

windows属于:单用户,多任务。

Linux属于:多用户,多任务

vscode快捷键

功能 快捷键
转到文件 command+p
命令面板 F1
打开/跳转到终端 ctrl+` 或 ctrl+j
变量统一重命名 F2
开关侧边栏 command+b
转到定义处 F12
代码格式化 command+K 然后 command+F
当前行下方插入一行 command+enter
当前行上方插入一行 command+shift+enter
定义处缩略图(只看一眼而不跳转过去) alt+F12
删除当前行光标右侧所有的字符 command+delete
多光标同时输入 alt+左键 选多行
同时选中所有匹配(可以方便修改和添加东西) command+shift+L
怕上面命令匹配太多可以逐个匹配 command+d
回到上一个光标操作 command+U
整个项目中查找 command+shift+F
全屏 F11
字体和窗口大小调整 command+“+/-”
撤销 command+z
恢复撤销 command+shift+z
单步步过 F10
单步步入 F11
选中当前行 command+L
返回上一个标签页 ctrl+”-“
上下移动当前光标的代码 alt+上/下
上下复制当前光标的代码 alt+shift+上下
折叠代码块 command+k 然后 command+{
展开代码块 command+k 然后 command+}
当前代码大纲 command+shift+.
当前代码大纲2 使用转到文件,然后输入@

makeFile学习

ubuntu下的开发环境安装

1
2
3
4
5
6
apt-get update #更新安装源
apt-get install g++ #安装gcc和c++的开发库
apt-get install gdb #安装调试工具
apt-get install make
apt-get install openssh-server #远程连接工具
apt-get install vim #编辑工具

安装好开发环境后,当输入make命令时,会在当前目录下找makefile或Makefile文件来执行。

为什么要学习makefile

  • linux下C/C++编程 makefile应用广泛
  • 编译移植开源项目,大部分开源项目都基于makefile,学会makefile才能够调试编译过程中的问题
  • 手写makefile太过于繁琐,自动生成的makefile不易于配置,学习本门课程便携自动化makefile,以后新项目只需要include makefile头文件
  • 学习makefile理解大型项目怎么分布编译的

目标

  1. 从零开始学会写第一个makefile编译Linux项目
  2. 能够掌握makefile原理,能够灵活应用
  3. 学会makefile变量,函数,shell实战方法
  4. 学会便携执行程序,动态库,静态库的makefile
  5. 学会通过一个makefile编译所有的项目
  6. 学会编写安装和卸载makefile
  7. 学会使用makefile自动生成启动和停止脚本

编译器相关

什么是编译器

是一个根据源代码生成机器码的程序。

厂商 C C++ Fortran
GNU gcc g++ gfortran
LLVM clang clang++ flang

一个源代码经过

  1. 预处理 Preprocessing
  2. 编译 Compilation(源码生成汇编语言,针对的是单文件)
  3. 汇编 Assembly(汇编语言生成二进制代码,针对的是单文件)
  4. 链接 Linking(多文件之间的关系整合起来,找不到就会报错)
  5. 运行
  • 在windows下,链接是找的lib文件,运行时找的dll文件
  • 在linux下,链接和运行找的都是so文件

gcc常用选项

选项 含义
-c 仅对源文件进行编译,不链接生成可执行文件。在对源文件进行查错时,或只需产生目标文件时可以使用该选项
-g[gdb] 生成调试信息,在可执行文件中加入调试信息,方便进行程序的调试。如果使用中括号中的选项,表示加入gdb拓展的调试信息,方便使用gdb来进行调试
-o[0或1或2或3或] 对生成的代码使用优化,中括号中的部分为优化级别,缺省的情况为2级优化,0为不进行优化。注意,采用更高级的优化并不一定得到效率更高的代码。
-Idir (dir表示具体的路径)在编译源程序时增加一个搜素头文件的额外目录–dir,即include增加一个搜索的额外目录(可以设置多个)
-Ldir (dir表示具体的路径)在编译源文件时增加一个搜索库文件(动态库或静态库)的额外目录–dir(可以设置多个)
-llibrary (library表示具体库的名字)在编译链接文件时,增加一个额外的库,库名为liblibrary.so(即此处写的名字是要去前缀和后缀的)
-w 禁止所有警告
-Wwarning (warning表示具体的取值)允许产生warning类型的警告,warning可以是:main,unused等很多取值,最常用是-Wall,表示产生所有警告。如果warning取值为error,其含义是将所有警告作为错误(error),即出现警告就停止编译。

gcc编译动态链接库

  • -fPIC 编译选项 产生位置无关代码(PIC),一般创建共享库时用到。在x86上,PIC的代码的符号引用都是通过ebx进行操作的
  • -shared 产生动态链接库

例如

1
2
g++ -shared -fPIC mylib.cpp -o libmylib.so#编译动态链接库
g++ test.cpp -Imylib -L/root/cpp

静态库编译

linux中的静态库是.a后缀

1
ar -crv libmylib.a mylib.o

参数讲解:

  • -c 不显示创建
  • -v 显示过程
  • -r 创建静态库

编译时相关文件查找顺序

头文件顺序

Linux中,以include<”fileName.suffix“>为例,编译时:

首先检索文件的当前路径;如果没有,再检索标准路径

  1. 先搜索当前目录
  2. 然后搜索-I指定的目录
  3. 再搜索gcc的环境变量CPLUS_INCLUDE_PATH(C程序使用的是C_INCLUDE_PATH
  4. 最后搜索gcc的内定目录(即上述的标准路径)

库文件顺序

可以查看[[linux基础以及系统编程#Linux如何查找动态库|linux基础以及系统编程页面中的Linux如何查找动态库部分]]

GDB调试器

  • GDB(GUN Debugger)是一个用来调试C/C++程序的功能强大的调试器,是LINUX系统开发C/C++最常用的调试器

  • 程序员可以使用GDB来跟踪程序中的错误,从而减少程序员的工作量

  • Linux开发C/C++一定要熟悉GDB

  • VSCode是通过调用GDB调试器来实现C/C++的调试工作的

    Windows系统中,常用的集成开发环境(IDE),如VS,VC等,它们内部已经嵌套了相应的调试器

GDB调试器只适合单线程程序调试,多线程调试,还是使用日志打印方式更靠谱

GDB主要功能

  • 设置断点(断点可以是条件表达式)

  • 使程序在指定的代码行上暂停执行,便于观察

  • 单步执行程序,便于调试

  • 查看程序中变量值的变化

  • 动态改变程序的执行环境

  • 分析崩溃程序产生的core文件

    如何设置开启core文件的产生

常用调试命令参数

调试开始:执行gdb [exefilename],进入gdb调试程序,其中exefilename为要调试的可执行文件名,gdb [exefilename] [core文件]可以用于分析core,查看错误产生的位置等信息

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
##以下命令后括号内为命令的简化使用,比如run(r),直接输入命令r就代表命令run

$(gdb)help(h) #查看命令帮助,具体命令查询在gdb中输入help+命令

$(gdb)run(r) #重新开始运行文件 (run-text:加载文本文件,run-bin:加载二进制文件)

$(gdb)start #单步执行,运行程序,停在第一行执行语句

$(gdb)list(l) #查看源代码(list,查看当前断下位置附近的代码 list-n,从第n行开始查看代码,list+函数名:查看具体函数)

$(gdb)set #设置变量的值

$(gdb)next(n) #单步调试(逐过程,函数直接执行)

$(gdb)step(s) #单步调试(逐语句,跳入自定义函数内部执行)

$(gdb)backtrace(bt) #查看函数的调用的栈帧和层级关系

$(gdb)frame(f) #切换函数的栈帧

$(gdb)info(i) #查看函数内部局部变量的数值

$(gdb)finish #结束当前函数,返回到函数调用点

$(gdb)continue(c) #继续运行

$(gdb)print(p) #打印值及地址(例如p x 表示打印名为x的变量)

$(gdb)quit(q) #退出gdb

$(gdb)break+num(b) #在第num行设置断点

$(gdb)info breakpoints(i b) #查看当前设置的所有断点

$(gdb)delete breakpoints num(d) #删除第num个断点

$(gdb)display #追踪查看具体变量值

$(gdb)undisplay #取消追踪观察变量

$(gdb)watch #被设置观察点的变量发生修改时,打印显示

$(gdb)i watch #显示观察点

$(gdb)enable breakpoints #启用断点

$(gdb)disable breakpoints #禁用断点

$(gdb)x #查看内存x/20xw 显示20个单元,16进制,4字节每单元

$(gdb)run argv[1] argv[2] #调试时命令行传参

$(gdb)set follow-fork-mode child#Makefile项目管理:选择跟踪父子进程(fork())

Tips:

  1. 编译程序时需要加上-g,之后才能用gdb进行调试:gcc -g main.c -o main

    如果不加-g,将看不见程序的函数名,变量名,所代替的全是运行时的内存地址

  2. 回车键:重复上一命令

list显示代码命令详解

简写: l

  • list linenum:打印第linenum行的上下文内容.

  • list function:显示函数名为function的函数的源程序。

  • list: 显示当前行后面的源程序。

  • list -:显示当前文件开始处的源程序。

  • list file:linenum: 显示file文件下第n行

  • list file:function: 显示file文件的函数名为function的函数的源程序

    一般是打印当前行的上5行和下5行, 如果显示函数是是上2行下8行, 默认是10行, 当然, 你也可以定制显示的范围, 使用下面命令可以设置一次显示源程序的行数。

  • set listsize count:设置一次显示源代码的行数。

  • show listsize: 查看当前listsize的设置。

break设置断点命令详解

简写: b

简单断点–当前文件
  • b 10 在源程序第10行设置断点
  • b func 设置断点,在func函数入口处
多文件设置断点
  • b filename:linenum 在源文件filename的linenum行处停止
  • b filename:function 在源文件filename的function函数的入口处停止
查询所有断点

info break (info可简写为 i) 所以可以直接写 i b

条件断点

if关键词,后跟断点条件.如: b test.c:8 if intValue == 5

维护断点
  • delete [range...] 删除指定的断点, 其简写命令为d

    • 如果不指定断点号, 则表示删除所有的断点。range表示断点号的范围(如:3-7)。
      • 删除某个断点: delete num
      • 删除多个断点: delete num1 num2 ...
      • 删除连续的多个断点: delete m-n
      • 删除所有断点: delete
    • 比删除更好的一种方法是disable停止点, disable了的停止点, GDB不会删除, 当你还需要时, enable即可, 就好像回收站一样。
  • disable [range...] 使指定断点无效, 简写命令是dis

    如果什么都不指定, 表示disable所有的停止点。

    • 使一个断点无效/有效: disable num
    • 使多个断点无效有效: disable num1 num2 ...
    • 使多个连续的断点无效有效: disable m-n
    • 使所有断点无效有效: disable
  • enable [range...] 使无效断点生效, 简写命令是ena。如果什么都不指定, 表示enable所有的停止点。

    • 使一个断点无效/有效: enable num
    • 使多个断点无效有效: enable num1 num2 ...
    • 使多个连续的断点无效有效: enable m-n
    • 使所有断点无效有效: disable/enable
调试代码
  • run/r 运行程序 start运行程序,停在第一行执行语句
  • next/n 单步步过
  • step/s 单步步入
  • finish 退出进入的函数
  • until/u 在一个循环体内单步跟踪时,该命令可以运行程序直到退出循环体
  • continue/c 继续运行程序(若有断点,则跳到下一个断点)
显示运行中信息
  • print/p 变量名/&变量(变量的地址) 查看运行时变量的值

自动显示变量的值

你可以设置一些自动显示的变量, 当程序停住时, 或是在你单步跟踪时, 这些变量会自动显示。相关的GDB命令是display。

  • display 变量名
  • info display – 查看display设置的自动显示的信息。
  • undisplay num(info display时显示的编号)
  • delete display dnums …undisplay dnums … – 删除自动显示, dnums意为所设置好了的自动显式的编号。如果要同时删除几个, 编号可以用空格分隔, 如果要删除一个范围内的编号, 可以用减号表示(如:2-5)
    • 删除某个自动显示: undisplay num 或者delete display num
    • 删除多个: delete display num1 num2
    • 删除一个范围: delete display m-n
  • disable display dnums…
    • 使一个自动显示无效: disable display num
    • 使多个自动显示无效: delete display num1 num2
    • 使一个范围的自动显示无效: delete display m-n
  • enable display dnums…
    • 使一个自动显示有效: enable display num
    • 使多个自动显示有效: enable display num1 num2
    • 使一个范围的自动显示有效: enable display m-n
  • disable和enalbe不删除自动显示的设置, 而只是让其失效和恢复。

查看变量的类型

  • ptype width –查看变量width的类型

    type = double

改变变量的值

可以使用 set var 命令来告诉GDB,width不是你GDB的参数,而是程序的变量名,如: set var width = 47

在你改变程序变量取值时,最好都使用set var 格式的GDB命令

makefile基本格式

1
2
目标: 依赖
(tab)命令//必须是tab,不能是空格
  • 目标:要生成的目标
  • 依赖:目标文件由哪些文件生成
  • 命令:通过执行该命令由依赖文件生成目标
1
2
3
4
目标...: 依赖...
命令1#注意每条命令前必须有且仅有一个 tab 保持缩进,这是语法要求(此处要注意vscode编写的话要把tab键切换成4空格给关掉)
命令2
...

Makefile 文件默认只生成第一个目标文件即完成编译,但是我们可以通过 “ALL” 指定需要生成的目标文件。通常会把ALL也设置为伪目标

makefile文件中tab开头的表示他是命令,命令开头有一些可选项可填入

  • 开头加上@,表示该命令执行的时候,不显示命令本身
  • 开头加上-,表示该命令执行失败也依然会执行后续命令。

最基础的makefile文件

1
2
3
#当前目录下有first_make.cpp;second.cpp;second.h
first_make:first_make.cpp second.cpp #目标文件:依赖文件
g++ first_make.cpp second.cpp -o first_make -lpthread#-l表示添加某个库,因为使用到了pthread库

makefile工作流程

image-20211224202502589

捕获

makefile文件主要包含了5部分内容

  1. 显示规则。说明了如何生成一个或多个目标文件。由makefile文件的创作者指出,包括要生成的文件,文件的依赖文件,生成的命令
  2. 隐式规则。由于make有自动推导的功能,所以隐式的规则可以比较粗糙地简略书写makefile文件,这是由make所支持的。
  3. 变量定义。在makefile文件中要定义一系列的变量,变量一般都是字符串,这与C语言中的宏有些类似。当makefile文件执行时,其中的变量都会扩展到相应的引用上。
  4. 文件指示。其中包含3个部分,(1)在一个makefile文件中引用另一个makefile文件;(2)根据某些情况指定makefile文件中的有效部分;(3)定义一个多行的命令
  5. 注释。makefile文件中只有行注释,其注释用“#”字符。如果要在makefile文件中使用“#”字符,可以用反斜框进行转义,如:”\#”

makefile中的变量

类似于C语言的宏定义

1
2
3
如:下面是变量的定义和使用
foo = abc // 定义变量并赋值
bar = $(foo) // 使用变量, $(变量名)

主要有以下4种

  1. 系统自带变量
  2. 手动定义变量
  3. make传递变量
  4. 自动推理参数设置

GUN的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们就没必要去在每一个[.o]文件后都写上类似的命令,因为,我们的make会自动识别,并自己推导命令。只要make看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果make找到一个whatever.o,那么whatever.c,就会是whatever.o的依赖文件。并且gcc -c whatever.c也会被推导出来

正是因为强大的[自动推导功能],因此依赖项写成.o文件后,就可以一条命令搞定整个编译过程

makefile中常见预定义变量

image-20211224164553549

p.s.CXXFLAGS=-I../inc 表示头文件的路径在当前源文件的父文件夹中的inc文件夹中

在makefile中变量的用法$(变量名)

自动变量

自动变量(下面表格的变量)只能在规则中的命令使用

image-20211224204554371

$(@D)是目标文件的目录,$(@F)是目标文件,

手动定义变量的方法:
$$
变量名=存的值(仅第一次有效)
$$

$$
变量名:=存的值(已经定义了变量,修改用这个)
$$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#生成的目标文件名
TARGET=main
#依赖文件.o(自己写的)
OBJS=test.o main.o
#依赖文件,加入的头文件
LIBS=-lpthread
#头文件所在目录
CXXFLAGS:=-I../inc
#目标项:依赖项
$(TARGET):$(OBJS)
$(CXX) $^ -o $@ $(LIBS)

#目标项clean
clean:
$(RM) $(TARGET) $(OBJS)

#标识伪目标
.PHONY:clean

生成动态库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#生成的目标文件名
TARGET=libfunc.so
#依赖文件.o(自己写的)
OBJS=func.o
#依赖文件,加入的头文件
LIBS=-lpthread
#头文件所在目录
CXXFLAGS=-I../inc -fPIC
$(TARGET):$(OBJS)
$(CXX) -shared $^ -o $@ $(LIBS)

#目标项clean
clean:
$(RM) $(TARGET) $(OBJS)

#标识伪目标
.PHONY:clean

联合动态库编译文件test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#生成的目标文件名
TARGET=test
#依赖文件.o(自己写的)
OBJS=test.o
#头文件所在目录
CXXFLAGS=-I../inc
#库文件所在路径
LDFLAGS=-L../lib
#具体引用了哪些库
LIBS=-lmylib -lpthread
$(TARGET):$(OBJS)
$(CXX) -shared $^ -o $@ $(LIBS)

#目标项clean
clean:
$(RM) $(TARGET) $(OBJS)

#标识伪目标
.PHONY:clean

上面所有案例都有个问题:一旦新增源文件,就要修改OBJS=……

有没有一劳永逸的方法,不需要修改makefile就能适应呢。

模式规则

要求至少在规则的目标定义中要包含’%’,’%’表示一个或多个,在依赖条件中同样可以使用’%’,依赖条件中的取值取决于其目标:

如下:

捕获

makefile的清理操作

用途:清除编译生成的中间.o文件和最终目标文件

make clean 命令 同于执行此操作

但是如果当前目录下有同名clean文件,则不执行clean对应的命令,解决方案是 伪目标声明

.PHONY:clean 声明目标为伪目标之后,makefile将不会检查该目标是否存在或者该目标是否需要更新,不检查直接调用对应的命令.

rm -f 强制执行,比如若要删除的文件不存在,使用 -f 不会报错

makefile命令中的特殊符号

  • - 表示即使此条命令出错,make也会继续执行后续的命令. 如: -rm main.o
  • @不显示命令本身,只显示结果. 如:@echo clean done

[注意]: ~/这样的路径在makefile的命令中不可使用,取而代之可以这样: $(HOME)/

make命令的参数

  • make 默认执行第一个出现的目标,可通过 make dest(dest表示其他目标)指定要执行的目标
  • make -f 文件名制定一个makefile文件来执行 如:make -f mainmak指定要执行的makefile文件名为mainmak
  • 测试makefile时,应先使用-n参数,检查无误再执行。(只显示命令,但不真正执行)

makefile的常见函数

所有函数都有返回值

  • wildcard函数 当前目录下匹配模式的文件 例如:src=$(wildcard *.c)

  • notdir函数 去除路径 例如:$(notdir $src)

  • patsubst函数 模式匹配替换 例如:$(patsubst%.c,%.o,$src)

    ​ 等价于$(src:.c=.o) 意思是把src的所有.c后缀的文件名换成.o后缀文件

  • shell函数 执行shell命令 例如$(shell ls -d */)

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
#生成的目标文件名
TARGET=main
#可以设定CXX(库可以直接加在这句中,如下list库,-g表示调试也可以加在这句中)
#CXX=g++ -g -llist
#依赖文件.o(自己写的)
SRCS=$(wildcard *.cpp)
#上面与下面同理
#SRCS=$(shell ls *.cpp)
#下面的可以遍历所有子目录中的.cpp
#SRCS=$(shell find ./ -name '*.cpp')
OBJS=$(SRCS:.cpp=.o)
#依赖文件,加入的头文件
LIBS=-lpthread
#头文件所在目录
CXXFLAGS:=-I../inc
#目标项:依赖项
$(TARGET):$(OBJS)
$(CXX) $^ -o $@ $(LIBS)

#目标项clean
clean:
$(RM) $(TARGET) $(OBJS)

#标识伪目标
.PHONY:clean

到这里makefile中只在需要添加新的库的时候,在LIBS=-lpthread后面添加链接库

别人项目中的makefile编写参考

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
.SUFFIXES: .c .o .lo

COMPILE = $(CC) -g -Wall -O -D_FILE_OFFSET_BITS=64 -DDEBUG
INC_PATH = -I/usr/local/include
LIB_PATH = -L/usr/local/lib -lfdfsclient -lfastcommon -lserverframe
TARGET_PATH = $(TARGET_PREFIX)/bin

#SHARED_OBJS = common_func.o dfs_func.o
SHARED_OBJS = common_func.o dfs_func_pc.o

ALL_OBJS = $(SHARED_OBJS)

ALL_PRGS = gen_files test_upload test_download test_delete combine_result

all: $(ALL_OBJS) $(ALL_PRGS)
.o:
$(COMPILE) -o $@ $< $(SHARED_OBJS) $(LIB_PATH) $(INC_PATH)
.c:
$(COMPILE) -o $@ $< $(SHARED_OBJS) $(LIB_PATH) $(INC_PATH)
.c.o:
$(COMPILE) -c -o $@ $< $(INC_PATH)
.c.lo:
$(COMPILE) -c -fPIC -o $@ $< $(INC_PATH)
install:
mkdir -p $(TARGET_PATH)
cp -f $(ALL_PRGS) $(TARGET_PATH)
clean:
rm -f $(ALL_OBJS) $(ALL_PRGS)

这个Makefile文件可以用来编译和链接一些测试程序,例如test_upload、test_download、test_delete等。您可以运行 make all 来编译所有程序,运行 make install 来安装所有程序,运行 make clean 来清理所有生成的文件。

cmake学习

CMake 是一个开源的跨平台自动化构建系统,用来管理软件构建的过程,不依赖于某特定编译器,可以支持多层目录、多个应用程序和多个函数库。它用配置文件控制构建过程(build process)的方式和 Unix 的 make 相似,只是 CMake 的配置文件取名为 CMakeLists.txt。CMake 并不直接构建出最终的软件,而是产生标准的构建文件(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的构建方式使用

  • CMake是一个跨平台的安装编译工具,可以用简单的语句来描述所有平台的安装(编译过程)。
  • CMake可以说已经成为大部分C++开源项目标配(事实标准)

语法特性介绍

基本语法格式:指令(参数1 参数2…)

  • 参数使用括弧括起
  • 参数之间使用空格分号分开

指令是大小写无关的,参数和变量是大小写相关的

1
2
3
set(HELLO hello.cpp)
add_executable(hello main.cpp hello.cpp)
ADD_EXECUTABLE(hello main.cpp ${HELLO})

变量使用 ${}方式取值,但是在IF控制语句中是直接使用变量名

重要指令

  • cmake_minimum_required==指定CMake的最小版本要求

    语法:cmake_minimum_required(VERSION versionNumber [FATAL_ERROR])

    1
    2
    #CMakezz最小版本要求为2.8.3
    cmake_minimum_required(VERSION 2.8.3)
  • project==定义工程名称,并可指定工程支持的语言

    语法:project(projectname [CXX] [C] [Java])

    1
    2
    #指定工程名为HELLOWORLD 
    project(HELLOWORLD)
  • set==显式的定义变量

    语法:set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

    1
    2
    #定义SRC变量,其值为main.cpp hello.cpp
    set(SRC sayhello.cpp hello.cpp)
  • Include_directories==向工程添加多个特定的头文件搜索路径—》相当于指定g++编译器的-I参数

    要放在add_executable前面

    语法:include_directories([AFTER | BEFORE] [SYSTEM] dir1 dir2 ...)

    1
    2
    将/usr/include/myincludefolder 和 ./include 添加到头文件搜索路径
    include_directories(/usr/include/myincludefolder ./include)
  • Link_directories==向工程添加多个特定的库文件搜索路径—》相当于指定g++编译器的-L参数

    要放在add_executable前面

    语法:link_directories(dir1 dir2 ...)

    1
    2
    3
    #将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径
    link_directories(/usr/lib/mylibfolder ./lib)
    #windows下会自动解析路径下的Debug或Release目录
  • add_library==生成库文件

    语法:add_library(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] source1 source2 … sourceN)

    1
    2
    3
    #通过变量SRC生成libhello.so共享库
    add_library(hello SHARED ${SRC})
    #SHARED表示动态库,STATIC表示静态库
  • Add_compile_options==添加编译参数

    语法:add_compile_options(

    1
    2
    #添加编译参数 -Wall -std=c++11 -o2
    add_compile_options(-Wall -std=c++11 -o2)
  • Add_executable==生成可执行文件

    语法:add_library(exename source1 source2 … sourceN)

    1
    2
    #编译main.cpp生成可执行文件main
    add_executable(main main.cpp)
  • Target_link_libraries==为target添加需要链接的共享库—》相当于指定g++编译器-l参数

    放在add_executable后面

    语法:target_link_libraries(target library1<debug | optimized> library2...)

    1
    2
    3
    #将hello动态库文件链接到可执行文件main
    target_link_libraries(main hello)
    #此处无论是linux,mac,windows中,hello不需要写前面的lib和后面的后缀
  • add_subdirectory==向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置

    语法:add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

    1
    2
    #添加src子目录,src中需有一个CMakeLists.txt
    add_subdirectory(src)
  • Aux_source_directory==发现一个目录下所有的源文件并将列表存储在一个变量中,这个指令临时被用来自动构建源文件列表(多个该指令可以累加找到的源文件到变量中)

    语法:aux_source_directory(dir VARIABLE)

    1
    2
    3
    4
    #定义SRC变量,其值为当前目录下所有的源代码文件
    aux_source_directory(. SRC)
    #编译SRC变量所代表的的源代码文件,生成main可执行文件
    add_executable(main ${SRC})

    自动查找头文件可以通过下面命令实现

    FILE (GLOB H_FILE "${INCLUDE_PATH}/*.h*")

    含义为将${INCLUDE_PATH}/*.h*匹配的文件放到H_FILE变量中

  • add_definitions

    用于向 C/C++ 编译器添加预处理器定义

    语法:add_definitions("-DDEFINITION1 -DDEFINITION2")

    1
    add_definitions(-Dxlog_STATIC) #传递宏给项目,宏的默认值为1

CMake常用变量

  • CMAKE_C_FLAGS gcc编译选项

  • CMAKE_CXX_FLAGS g++编译选项

    1
    2
    #在CMAKE_CXX_FLAGS编译选项后追加-std=c++11
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
  • CMAKE_BUILD_TYPE 编译类型(Debug,Release)

    1
    2
    3
    4
    #设定编译类型为debug,调试时需要选择debug
    set(CMAKE_BUILD_TYPE Debug)
    #设定编译类型为release,调试时需要选择release
    set(CMAKE_BUILD_TYPE Release)
  • CMAKE_BINARY_DIR / PROJECT_BINARY_DIR / <projectname>_BINARY_DIR

    1. 这三个变量指代的内容是一致的
    2. 如果是in source build,指的就是工程顶层目录
    3. 如果是Out-of-source编译,指的是工程编译发生的目录
    4. PROJECT_BINARY_DIR跟其他指令稍有区别,不过现在,你可以理解为他们是一致的
  • CMAKE_SOURCE_DIR / PROJECT_SOURCE_DIR / <projectname>_SOURCE_DIR

    1. 这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录
    2. 也就是在in source build时,他跟CMAKE_BINARY_DIR等变量一致
    3. PROJECT_SOURCE_DIR跟其他指令稍有区别,不过现在,你可以理解为他们是一致的
  • CMAKE_C_COMPILER 指定C编译器

  • CMAKE_CXX_COMPILER 指定C++编译器

  • EXECUTABLE_OUTPUT_PATH 可执行文件输出的存放路径

  • LIBRARY_OUTPUT_PATH 库文件输出的存放路径

  • CMAKE_VERBOSE_MAKEFILE 开关显示详细的生成指令 ON/OFF

  • CMAKE_CURRENT_LIST_DIR当前正在执行的CMakeLists.txt文件所在的目录的路径

CMAKE编译工程

CMake目录结构:项目主目录存在一个CMakeLists.txt文件

两种方式设置编译规则

  1. 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可
  2. 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的CMakeLists.txt中

编译流程

  1. 手动编写CMakeLists.txt
  2. 执行命令cmake PATH生成Makefile(PATH是顶层CMakeLists.txt所在的目录)
  3. 执行命令make进行编译

p.s. vs code有一个叫cmake的拓展,可以支持智能补全cmake指令与直接生成CMakeLists.txt模板文件

cmake tools是让[vs code支持cmake的插件](#CMake Tools插件)

两种构建方式

  • 内部构建(in_source build):不推荐使用

    内部构建会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起会显得杂乱无章

    1
    2
    3
    4
    5
    ##内部构建
    #在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件
    cmake .
    #执行make命令,生成target
    make
  • 外部构建(out_of_source build):推荐使用

    将编译输出文件与源文件放到不同目录中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ##外部构建
    # 1. 在当前目录下,创建build文件夹
    mkdir build
    # 2. 进入到build文件夹(一般默认该文件夹都叫build)
    cd build
    # 3. 编译上级目录的CMakeLists.txt,生成Makefile和其他文件。也可以在上级目录的情况下,直接cmake -B ./build
    cmake ..
    # 4. 执行make命令,生成target
    make

【实战】CMake代码实践

最小的CMake工程

1
2
3
cmake_minimum_required(VERSION 3.0)
project(HELLOWORLD)
add_executable(helloWorld_cmake helloworld.cpp)

内部构建后(还未执行make)如图:

image-20220117153415398

红色框中是原有的文件,紫色框中的是内部构建生成的,可以看到非常乱

外部构建后(还未执行make)如图:

image-20220117155157081

cmake的产物都放到build文件夹下了,整洁了很多 ,如果之后执行make,生成的可执行文件也会在build文件夹下

多目录工程-直接编译

项目结构如下:

image-20220117162108450

CMakeLists.txt如下:

1
2
3
4
5
6
7
cmake_minimum_required(VERSION 3.0)

project(TESTCMAKE)

INCLUDE_DIRECTORIES(include)

add_executable(main_cmake main.cpp src/tool.cpp)

操作如下:

image-20220117162245705

make后结构如下:

image-20220117162516558

下面两个等同于cd build;cmake .

1
2
3
4
5
#在build上级目录
#配置项目
cmake -S . -B build
#构建项目
cmake --build build

vscode配合cmake进行调试

  1. 合理设置项目目录
  2. 编写项目源文件
  3. 编写CMakeLists.txt构建项目编译规则
  4. 使用外部构建,手动编译CMake项目
  5. 配置VSCode的json文件并调试项目

CMakeLists.txt编写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cmake_minimum_required(VERSION 3.0)

project(TESTCMAKE)

##添加C++11支持及其他选项
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -Wall")

#调成debug模式
#set(CMAKE_BUILD_TYPE "DEBUG")

INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)

#add_compile_options(-g)

aux_source_directory(./src SRC)

aux_source_directory(. SRC_MAIN)

add_executable(main_cmake ${SRC_MAIN} ${SRC})
  • -Wall 选项意思是编译后显示所有警告。
  • -O2 在这一级别GCC将会提供所有支持的优化,但这其中并不包括以空间换时间的优化手段,例如编译器不会使用循环展开和函数内联。和-O相比,该选项进一步加快了编译时间和生成代码的性能。

点击下面图片位置,创建json文件

image-20220119163310314

【超级重点】:必须在编辑框显示源文件的情况下点击这个按钮

image-20220119201327676

CodeLLDB(用来debug,解决Catalina不支持lldb调试问题)

Mac在更新到Catalina后不再支持lldb调试,即C++(GDB/LLDB)无效,因此下载CodeLLDB插件,也因此上图选择LLDB按钮。

image-20220119201249218

红框中设置为生成的可执行文件的路径

然后直接按F5就可以调试运行

如果想每次调试前自动编译项目,可以配合cmake,在.vscode文件夹下新建tasks.json文件,将下面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
28
29
30
31
32
33
34
35
36
{
"version": "2.0.0",
"options": {
"cwd": "${workspaceFolder}/build"
},
"tasks": [
{
"type": "shell",
"label": "cmake",
"command": "cmake",
"args": [
".."
]

},
{
"label": "make",
"group": {
"kind": "build",
"isDefault": true
},
"command": "make",
"args": [

]
},
{
"label": "Build",
"dependsOrder": "sequence",
"dependsOn": [
"cmake",
"make"
]
}
],
}

上面json文件就相当于每次调试前执行如下代码:

1
2
3
#相当于在这个${workspaceFolder}/build路径下执行如下代码
cmake ..
make

这样的好处在于每次调试前保证调试的是最新的可执行文件。

cmake进阶

在使用CMake组织工程时,如果我们需要依赖某个库文件,需要完成以下步骤:

  1. 查找库文件(find_package命令)
  2. 判断是否找到库文件(XXX_FOUND标记),并包含头文件(include_directories命令)
  3. 链接库文件到目标(target_link_libraries命令)

例如:

1
2
3
4
5
6
7
8
9
#查找OpenCV是否安装
find_package(OpenCV REQUIRED core highgui imgproc imgcodecs)
if (OPENCV_FOUND)
message(STATUS "找到OpenCV:\"${OpenCV_INCLUDE_DIRS}\",ENABLE_OPENCV宏已打开")
message(STATUS "找到OpenCV:\"${OpenCV_LIBS}\"")
include_directories(${OpenCV_INCLUDE_DIRS})
add_definitions(-DENABLE_OPENCV)
list(APPEND LINK_LIB_LIST ${OpenCV_LIBS})
endif (OPENCV_FOUND)

现代CMAKE

现代cmake的完全体:cmake版本3.15及以上

终端指令学习

基本指令

Windows Macos/linux 功能
cd cd 前往文件夹
cd pwd 取得目前所在路径
dir ls 查看当前目录的文件和文件夹
mkdir mkdir 新建文件夹
无指令 touch 新建文件
copy cp 复制文件
move mv 移动文件
del rm 删除文件
cls clear 清除画面上的显示

win部分特殊终端命令

homebrew常用命令

安装软件:brew install xxx
卸载软件:brew uninstall xxx
搜索软件:brew search xxx
更新软件:brew upgrade xxx
查看列表:brew list
更新brew:brew update
清理所有包的旧版本:brew cleanup
清理指定包的旧版本:brew cleanup $FORMULA
查看可清理的旧版本包,不执行实际操作:brew cleanup -n
查看包的详细信息:brew info xxx
检查brew所有服务状态:brew services list
启动xxx服务:brew services start xxx
停止xxx服务:brew services stop xxx
重启xxx服务:brew services restart xxx
查看某个软件的安装路径: brew --prefix xxx

删除所有未使用的依赖 brew autoremove (加-n可以预先确定哪些软件将会被清理)

brew tap someone/special 这个命令会将 someone/special 仓库克隆到本地机器上的一个特定目录中(通常是 /usr/local/Homebrew/Library/Taps),Homebrew 之后会在这个仓库中查找软件包。

brew tap 是 Homebrew 的一个命令,用于添加额外的软件包源(也称为 “tap”)。Homebrew 默认情况下只包含其主仓库(homebrew/core),这个仓库里包含了大量常用的软件包。但是,用户可能需要安装一些不在主仓库中的软件包,这时就可以使用 brew tap 命令来添加一个新的源。

取消 tap: 如果你不再需要某个 tap,可以使用 brew untap 命令 followed by the tap’s name 来移除它

brew tap 是一个非常有用的特性,让用户能够轻松地扩展 Homebrew 的软件库,安装和管理广泛的第三方软件包

浅克隆问题

Homebrew 浅克隆问题指的是 Homebrew 在安装或更新时,为了节省时间和带宽,通常会创建一个所谓的 “浅克隆”(shallow clone)的 Git 仓库。浅克隆只包含最近的提交记录,而不是整个仓库的历史记录。这通常足够 Homebrew 正常工作,因为大多数用户只关心最新的包版本。

然而,GitHub 对于浅克隆的更新操作提出了性能问题。因为浅克隆需要在服务器端进行更多的计算来确定要发送哪些数据给客户端。当大量用户进行浅克隆的更新时,这将对 GitHub 服务器产生很大的负担。

因此,Homebrew 在 GitHub 的请求下引入了一个限制,要求用户在更新浅克隆的 Homebrew 仓库之前,先将其转换为完整克隆(unshallow clone)。完整克隆包含了仓库的完整提交历史,虽然它需要更多的磁盘空间和更长的初始克隆时间,但之后的更新操作会更简单,对 GitHub 的服务器负载也更小。

你可以通过执行以下命令将 Homebrew 的浅克隆转换为完整克隆:

1
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow

win终端命令相关

基本指令参考

终端命令重命名

终端输入$profile查对应终端的配置文件所在,没有就自行新建

打开文件输入,格式:

1
2
3
4
function 别名 { 需要替代的命令,可以包含空格 }
或者
function 名称 { 需要替代的命令,可以包含空格 }
Set-Alias 别名 名称

如:

1
2
3
4
function sshLinux{
ssh 'kylin@192.168.10.88' $args
}
Set-Alias sshlinux sshLinux

保存后重启power shell就可以使用别名了

powershell新建文件命令

powershell基本指令大多与linux一致,但无touch命令

1
fsutil file creatnew 新建文件名 0

工具命令

ssh

ssh远程连接 ssh 用户名@ip地址或域名

scp

从服务器上下载文件 scp username@servername:/path/filename /var/www/local_dir(本地目录)

加上 -r 的参数才可以传目录

例如scp root@192.168.0.101:/var/www/test.txt 把192.168.0.101上的/var/www/test.txt 的文件下载到/var/www/local_dir(本地目录)

上传本地文件到服务器 scp /path/filename username@servername:/path

例如scp /var/www/test.php root@192.168.0.101:/var/www/ 把本机/var/www/目录下的test.php文件上传到192.168.0.101这台服务器上的/var/www/目录中

如果上传下载的是整个目录,scp后加 -r

某些较新的系统scp命令默认使用SFTP协议来传输文件,但老系统却不支持sftp,此时使用-O使得scp变回传统传输行为

sftp

使用和scp类似,但功能更多

比如:SFTP支持断点续传,SCP则不能

代码调试

打断点

  • 集成开发环境IDE
  • gdb

printf 适用于有调试终端的情况

写log日志 当应用程序没有终端的时候

此处记录一个小技巧,跟踪显示日志打印(以跟踪打印最后50行为例)

  • windows: Get-Content -Path "C:\Path\To\Your\File.txt" -Wait -Tail 50
  • Linux/Mac: tail -n 50 -f /path/to/your/file.txt
  • 应用程序发布了, 通过日志文件记录程序的工作状态

    ​ 写日志有级别
    ​ 级别越低写的日志越多
    ​ 级别越高写的日志越少

IDE相关tips

在用vs调试的时候: 若看到无法解析的外部符号就是没有函数的实现, 应该是没有连接到库

各IDE跳出括号方法

vs开发linux项目

新建项目-跨平台-Linux-空项目

VS远程开发

工具-选项-跨平台-连接管理器-添加(主机名填写ip,端口22,填写用户名与密码)-连接

项目属性页-配置属性-常规 中:

  • 远程生成计算机选择上面添加的
  • 远程生成根目录
  • 远程生成项目目录

vscode远程开发

Vscode 安装 remote development插件

安装后打开远程资源管理器,设置为ssh target,点击+号键,添加ssh目标.

右键ssh目标选择连接,输入密码,开始远程开发

更细节的操作参考

可以配合[给windows配置ssh server](#给windows配置ssh server)

vscode配置C++调试

  1. 编译的时候需要加-g选项,才会附带调试需要的信息

  2. 点击左侧菜单栏中的运行和调试中的创建launch.json文件按钮,如下图(非红圈)

    image-20230219205902832

    点击后会在项目根目录下生成.vscode/launch.json文件

code runner配置

Code Runner默认运行是在输出端,是不能进行编辑输入的,所以我们要将其改到终端运行,打开VS Code设置,找到Run In Terminal配置处,将其勾选住,也可在设置的配置文件settings.json文件里添加”code-runner.runInTerminal”: true配置,保存,我们再通过Code Runner运行,就可以在终端中运行了,可以在上面进行输入了。

windows下的vscode终端乱码

windows下乱码最简单的方式是设置通过utf-8 with BOM的方式保存,如果不行再参考下面的方式解决

VSCode 默认是 utf-8 编码,而在中国地区下的 Windows 的 cmd 默认是 GBK 编码,因此需要配置vscode终端为utf-8编码

通过chcp 命令查看代码页编号,

  • 936 对应 GBK2312
  • 65001 对应 UTF-8

文件-首选项-设置,点击右上角打开设置(json),添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
"terminal.integrated.profiles.windows": {
"Command Prompt": {
"path": "C:\\Windows\\System32\\cmd.exe",
"args": ["-NoExit", "/K", "chcp 65001 > nul"]
},
"PowerShell": {
"source": "PowerShell",
"args": ["-NoExit", "/C", "chcp 65001"]
}
},
"terminal.integrated.defaultProfile.windows": "Command Prompt"

将以上配置添加在settings.json里最外层的{}里即可(注意!!!,如果{}里之前有内容记得要在最后一行加上一个英文逗号,之后再把配置复制进去)


更好的方法是修改程序的locale变量,这个会修改C/C++的运行环境,代码如下:

1
2
3
td::locale::global(std::locale(".utf8")); 
// 或者
// std::setlocale(LC_ALL, ".UTF-8");

因为修改locale变量修改的是C/C++的运行环境,并不会修改Windows系统函数的环境,所以如果调用系统函数WriteConsoleOutput函数,还是会出现乱码。 Windows上有些库(例如:spdlog)会调用WriteConsoleOutput函数,设置的locale还是会出现乱码,还需要设置控制台编码

1
2
std::locale::global(std::locale(".utf8")); 
SetConsoleOutputCP(CP_UTF8);

更详细完整的说法参阅

vscode的配置文件

安装并配置好C/C++插件后

  • 编译的话配置tasks.json

    它可以定义一系列的任务,包括编译,运行,测试等,以便在开发过程中快速执行这些任务

  • 【调试】启动的话配置launch.json

    它是用于调试的配置文件

使用[CMake Tools插件](#CMake Tools插件)配合launch.json可以实现一键使用cmake启动程序的作用

tasks.json

终端-配置任务 生成tasks.json文件

下面为用clang++配置的任务

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": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "m1Mac单文件生成任务",
"command": "/usr/bin/clang++",
"args": [
"-std=c++17",
"-fcolor-diagnostics",
"-fansi-escape-codes",
"-g",
"${file}",//此处为需要编译的源文件
"-o",
"${fileDirname}/${fileBasenameNoExtension}"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": "build",
"detail": "编译器: /usr/bin/clang++"
}
]
}

配置好以后使用 终端-运行生成任务 来调用该tasks.json配置的任务

launch.json

运行-添加配置 生成launch.json文件(生成的launch文件中可以进一步点击添加配置按钮生成小模版)

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
28
29
30
31
32
33
34
35
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
"version": "0.2.0",
"configurations": [
{
"name": "g++ - 生成和调试活动文件",// 配置名称,将会在启动配置的下拉菜单中显示
"type": "cppdbg",// 配置类型,这里只能为cppdbg
"request": "launch",// 请求配置类型,可以为launch(启动)或attach(附加)
"program": "${fileDirname}/${fileBasenameNoExtension}",// 将要进行调试的程序的路径
"args": [],// 程序调试时传递给程序的命令行参数,一般设为空即可
"stopAtEntry": false, // 设为true时程序将暂停在程序入口处,我一般设置为true
"cwd": "${fileDirname}",// 调试程序时的工作目录
"environment": [],// (环境变量?)
"externalConsole": false,// 调试时是否显示控制台窗口,一般设置为true显示控制台
"MIMode": "gdb",// 指定连接的调试器,可以为gdb或lldb。但目前lldb在windows下没有预编译好的版本。
// 用处未知,模板如此
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
],
// 调试会话开始前执行的任务,一般为编译程序。与tasks.json的label相对应
//"preLaunchTask": "C/C++: g++ bulid active file",
"miDebuggerPath": "/usr/bin/gdb"// 调试器路径,Windows下后缀不能省略,Linux下则去掉
}
]
}

vscode其他配置文件细节参考跳转

参考2

案例

m1 mac上的launch.json案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"version": "0.2.0",
"configurations": [
{
"name": "(lldb) 启动",
"type": "cppdbg",
"request": "launch",
"program": "${fileDirname}/${fileBasenameNoExtension}",
"args": ["-std=c++17"],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
}

]
}

设置好后,点击调试按钮进行调试

c_cpp_properties.json

c_cpp_properties.json 文件是 Visual Studio Code (VS Code) 中用于配置C/C++插件的重要文件。它主要用于针对特定项目设置包含头文件的路径,设置 C/C++ 支持的版本号等。

一般情况下,c_cpp_properties.json 文件位于 VS Code 中项目的 .vscode 文件夹中。如果你的项目中没有这个文件,你可以通过以下步骤来创建它:

  1. 在 VS Code 中打开命令面板(快捷键是 Ctrl+Shift+P
  2. 输入并选择 C/C++: Edit Configurations (UI) 命令

如果使用 CMake,VS Code 的 C/C++ 插件可以自动处理包含路径和定义,因此不需要手动配置 c_cpp_properties.json 文件

常用配置项

  • includePath

    用来指定头文件搜索路径的。当你在编写代码时,编译器会在 includePath 指定的路径中查找你引用的头文件。这样,编译器就能找到并正确地解析对应的头文件

    此外,includePath 还有助于实现代码的自动补全功能,你在写代码的时候,VS Code 会根据 includePath 中的路径去找头文件,这样就可以使用自动补全功能,而且不会有下划线警告

  • cppStandard

    你想要设置的 C++ 版本号,例如 "c++17""c++20"

官方配置文档

vscode插件

vscode设置技巧:

Ctrl + Shift + P显示所有命令,查找Preferences: OPen Default Settings (JSON),可以查看默认的配置文件,可以根据需要来定制进行环境的迁移

CMake Tools插件

cmake插件的目的是为了编写CMakeLists.txt的时候有智能提示,而CMake Tools插件是让vscode和cmake进行集成,更方便操作

CMake Tools插件会在启动项目的时候检测是否存在CMakeLists.txt,存在才启动该插件的功能.因此新建的CMakeLists.txt被创建后要重启项目.

如果需要使用该插件来调试的话,需要配合launch.json使用

launch.json需要修改的代码如下:

1
2
3
4
5
6
"program": "${command:cmake.launchTargetPath}"
其实就是调用了CMake Tools插件提供的一个命令:cmake.launchTargetPath
该命令的作用是编译好后返回生成的可执行程序的路径

使用vscode集成终端添加:
"console": "integratedTerminal"

接下来就可以通过F5来调试项目了,也可以使用vscode底部的运行和调试按钮

C/C++插件可以通过CMakeLists.txt自动配置vscode的代码提示.如果vscode的代码提示不正确,可以手动执行命令C/C++:Select IntelliSense Configuration...,然后选择使用CMake Tools,就可以使代码提示和CMakeLists.txt同步

vscode clangd插件

C/C++插件代码一多,提示就太慢了,并且偶尔崩溃,使用clangd插件替代

二者都是智能提示插件,互相冲突

当需要配置智能提示的头文件时:

  • 在项目目录的.vscode下新建settings.json文件

  • 来添加编译选项,智能提示会依据这些编译选项来提示

    1
    2
    3
    4
    5
    6
    {
    "clangd.fallbackFlags": [
    "-I./inc",
    "-std=c++14"
    ]
    }
  • 配置完成后要重启vscode或者command+shift+p:restart-clangd-server才能生效

clangd插件需要cmake下build文件夹生成的compile_commands.json文件来指导智能提示

如果是makefile项目,可以使用compiledb -n make -C build(pip安装命令)来生成compile_commands.json

如果是基于其他方式,可以使用Bear项目中的方式来生成对应的 compile_commands.json文件

vscode Clang-Format插件

代码格式化工具

clang-format基于Clang的代码解析能力,可以根据预定义的代码样式规范自动调整代码的缩进、换行、空格等格式

可以在插件的设置中的style项中进行配置,如下图:

image-20240214162308645

上图为针对Cpp语言的代码格式化设置

  • IndentWidth:指定每个缩进级别的空格数,默认为2。
  1. TabWidth:指定Tab字符的宽度,默认为4。
  2. UseTab:指定是否使用Tab字符进行缩进,默认为false,即使用空格进行缩进。
  3. ContinuationIndentWidth:指定在行尾继续缩进的空格数,默认为4。

tabout

tab功能拓展,可以按一下tab直接从括号或引号中跳出,无需按方向键或end键

gitlens

功能非常强大,甚至能显示代码部分是谁在什么时候做的修改

GitLens – Git supercharged拓展地址

文件路径跳转插件

quick-go-to-selected-terraform-path插件

C/C++ Include Guard插件

新建头文件时自动包含防重复包含的编译选项

插件主页

c cpp cmake project creator插件

新建基于cmake的项目文件

插件主页

Debug Visualizer

调试可视化,c++还未完全支持

插件主页

使用:

命令行里输入 Debug Visualizer: New View

https://marketplace.visualstudio.com/items?itemName=chendm.translate)

变量名翻译

var-translate-en

mac上快捷键:

  • 双击control翻译成中文
  • control+shift+v 翻译成英文

自动补分号

Colonize

  • shift+enter自动补分号,光标停在末尾
  • alt+enter自动补分号,光标停在新行处

单元测试插件

可以支持catch2,googleTest,docTest的vscode插件: C++ TestMate

容器开发支持

主要是两个插件

  • docker插件
  • Dev Containers插件

docker插件

使用Docker扩展可以非常方便的从VisualStudio Code构建,管理和部署容器化应用程序

主要功能如下:

  • 自动生成dockerfile、docker-compose.yml和.dockerignore文件(按F1并搜索Docker:将Docker文件添加到Workspace)

    Dockerfile包含了构建Docker容器的指令,而Docker Compose文件则用于定义多个容器之间的关系和配置

  • 语法突出高亮显示以及docker-compose.yml和Dockerfile文件的智能提示,悬停提示,语法检查和分析,会提示警告或错误

  • 集成最常见的Docker命令(例如docker build,docker push等,需按F1唤起)

  • Docker镜像、容器管理

  • 对Azure的支持(这块我们就不具体介绍了)

  • .NET Core程序调试支持

  • 连接docker-machine

Dev Containers插件

Dev Containers

Dev Containers 允许使用 Docker 容器作为完整功能的开发环境,提供一致、易于复制的工具链和隔离的开发环境。

  • 使用Docker的方式: 可以通过多种方式在 Dev Containers 中使用 Docker,包括本地安装 Docker、远程环境安装 Docker,以及使用其他符合 Docker 标准的 CLI。
  • 工作原理: Dev Containers 扩展会启动或附加到运行着定义良好的工具和运行时堆栈的开发容器,将工作区文件从本地文件系统挂载到容器中,并在容器内安装和运行扩展。

常配合docker插件使用,连接docker容器,开发环境标准化

代码悬浮翻译插件

Code Translate

可以自由拆分组合词翻译,鼠标悬浮翻译,快速

代码中单词翻译的终极解决方法

日志文件高亮显示插件

Log File Highlighter

排列整齐,高亮显示

自动生成Getter和Setter函数

要自己试一下才知道哪个最适合自己

Getter and Setter Generator

C++ Getter/Setter Generator

Getter/Setter Generator

一键复制文件给AI等结合Aider使用

Aide插件

利用ai API密钥可以一键生成注释

可视化文件插件

记录时还未上架vscode插件市场

使用方式: F1后输入visual file命令

JavaScript Debugger

vscode断点映射到浏览器

css pick

预览当前应用的css代码

下载地址

console ninja

console.log显示直接显示到代码右侧

下载地址

Code spell chacker

拼写检查,蓝线标识拼写错误

下载地址

代码书签插件

下载地址

可以给代码打书签

注释中的todo高亮插件

可以将注释中的todo高亮显示

下载地址

粘贴JSON变为类

Paste JSON as Code

下载地址

c#相关插件盘点

从CSharp代码生成PlantUML类图

这是一个命令行工具,但有配套的vscode插件可以使用

开源地址

vscode配置qt开发

vscode开发qt|720x360

vs团队资源管理器

VS团队管理器是一种基于Git for Windows的Git GUI. 即命令都是由Git for Windows去执行的, 而VS只负责将界面的UI操作转换成对应的命令行发送给Git for Windows.直接作为一个管理器嵌入在VS中

在单个组件中搜索git安装两个组件:

image-20240419114211380

VS的Github扩展需要浏览器支持,本质是在浏览器安装了个插件

装好后,可以直接在github打开该界面

20200621211708619

手动打开clone项目:

image-20240419115818424image-20240419115854380

主页介绍

vsRemote

详情参考

  1. 更改(Change):这是你对代码进行的修改。当你在Visual Studio中修改了代码后,这些更改会在”更改”区域中显示出来。你可以查看你的更改,然后提交这些更改到本地的Git仓库。对应的Git命令是`git commit
  2. 分支(Branch):在Git中,分支是用来隔离你的工作环境的,让你可以在不影响其他分支的情况下进行开发。你可以创建新的分支进行开发,完成后再将其合并回主分支。对应的Git命令是git branch来创建新的分支,git checkout来切换分支,git merge来合并分支
  3. 拉取请求(Pull Request):这是一种在团队中协作开发的方式。你可以在自己的分支上进行开发,然后通过创建拉取请求来请求将你的更改合并到主分支。这也是一种代码审查的方式,可以让团队成员在合并前对你的更改进行审查。对应的Git命令是git pull来拉取最新的更改
  4. 同步(Sync):这是将你的本地仓库与远程仓库进行同步的操作。你可以拉取远程仓库的最新更改到你的本地仓库,也可以将你的更改推送到远程仓库。对应的Git命令是git pull来拉取最新的更改,git push来推送你的更改
  5. 标记(Tag):在Git中,标记通常用于标记某个重要的提交点,如版本发布等。对应的Git命令是git tag来创建新的标签

点击分支,右键某分支,可以选择查看历史记录,打开的窗口能看到详细的时间点流程图

vscode相关设置

  • editor.wordSeparator可以设置分隔符,类似-的从中剔除,可以选中整个-分割的名称
  • explorer.compactFolders设置为false: 关闭单目录层级融合
  • editor.dragAndDrop 设置为false:禁止代码拖放
  • Editor: Insert Spaces 设置为true 用空格替代tab(对各种编译器兼容性有好处)
  • editor.acceptSuggestionOnEnter 将其设置为”off”,这样在按下回车键时就不会选择匹配项了(还是smart好)👌
  • Editor: Format On Save 设置是否在保存的时候自动格式化代码

vs相关设置

[[CSharp入门#visual studio开发环境相关|参考此处笔记]]

给windows配置ssh server

如果在启用或关闭Windows功能中没有看到ssh服务器端的选项的话,可以自行安装,遵循一下步骤:

以管理员启动PowerShell

  1. 先查看是否已安装:

    Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'

  2. 安装OpenSSH

    • 安装 OpenSSH 客户端:Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
    • 安装 OpenSSH 服务器:Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
  3. 安装好后的配置

    • 设置 sshd 服务自动启动:Set-Service -Name sshd -StartupType 'Automatic'
    • 确保 Windows Defender 防火墙允许 tcp 22 端口的入站连接。若规则丢失或被禁用,可以创建新规则:New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22;
    • (可选)如果要修改配置,如设置用户访问权限,可以编辑 sshd_config 配置文件:Start-Process notepad C:\ProgramData\ssh\sshd_config,编辑完成后保存并关闭记事本;
  4. 重启sshd服务

    Restart-Service sshd

上述方法不行的话,还可以通过到github上下载 openssh-win64.zip方式来安装

vscode远程连接的用户名是C:\Users\下的用户名之一,默认是ADMIN,注意要是激活电脑时使用的用户名,密码为开机密码,这样能确保权限够高,能安装上vscode服务端.

配合vscode远程开发使用

代理相关

代理模式

  • 系统代理

    将数据交给本地http/socks服务的系统代理,GFW可以判断得到是vpn链接发起请求,由于其处于灰色地带,正规途径有需要,因此会放行,但特殊时期可能阻断

    问题:并非所有软件都遵循系统代理,除去浏览器,绝大部分软件都不会走系统代理,甚至连设置代理的地方都没有,行为完全取决于软件开发者.并且系统代理一般都是http代理而非socks5,http代理不支持udp,游戏等流量无法代理

  • TUN/TAP代理(最常用)

    使用虚拟网卡接管系统全局流量的tun/tap代理

    主流模式,手机默认就是这种模式,软路由接管全家科学上网也是同样的原理(称为透明代理)

    问题:我们使用的ss,vmess,trojan等主流的翻墙协议都无法封装网络层的数据包,比如tun模式,ping谷歌的话,返回假延迟,因为上述协议无法代理网络层的icmp协议

  • 真VPN代理

    能封装网络层数据包,只有能封装网络层数据包,才能实现异地组网,内网穿透,才能称为真正的VPN

    软件一般是用WireGuard,可以正常ping外网地址

    问题:并非为翻墙而生,只是对数据进行加密顺便实现了翻墙功能,他的流量清晰明了地写着它就是VPN的流量.而且vpn分流很不方便.因此不推荐用来科学上网

    image-20230722105912700

SS协议 –减少GFW重放攻击-> SSR协议(带伪装插件的SS协议)

SSR协议

DNS泄露:clsh使用了ip规则进行分流的情况下,访问谷歌之前必须先得到谷歌的ip地址,于是clash发起dns请求,dns如果是明文的,那么当你要到谷歌ip的时候,马上给其他服务器发了一堆加密数据,该行为特征,鬼都知道你在干啥.再比如:网飞客户端偷偷发一个dns请求,权威dns服务器记载的这个dns请求的上游dns服务器所在地就会和代理服务器的上游dns服务器所在地不是一个地区,以此判定你在使用vpn工具

DNS泄露的本质原因:在代理的情况下,本地发出了dns请求,而这可能仅仅是为了获得ip匹配clash规则中的ip分流规则,实际上真正的dns解析依旧会在远程代理服务器上进行.

解决方案:fake-ip模式:当我们使用Fake-IP模式时(openclash运行在路由器上),浏览器请求dns解析,dns解析请求被clash截获,clash立刻返回一个Fake IP给浏览器,浏览器根据ip发起连接,请求被clash拦截,clash根据fake ip在自己的映射表中反查出域名,再用域名去走代理,代理服务器解析dns。3 这样客户端响应速度加快,浏览体验更加顺畅,减轻网页加载时间过长的情况。4

软件层面 v2rayN -> shadowsocks ->

DNS分流

UDP穿透篇

clash配置文件

proxy-providers 节点提供者

proxy-groups 代理组

兼容性相关

各种平台宏详细参考

  • 路径兼容性

  • 使用跨平台的库和框架

    Boost,QT,

  • 避免使用平台特定API,万不得已使用条件编译根据不同平台编写不同代码

    需要注意的是,使用条件编译也存在一些缺点。例如,代码可读性较差,维护成本较高,容易引入错误等。因此,应该谨慎使用条件编译。

  • 使用跨平台的编译器

    GCC/Clang/Visual Studio Code等等

路径兼容性

1
2
3
4
5
6
//例如
#ifdef _WIN32
const char pathSeparator = '\\';
#else
const char pathSeparator = '/';
#endif

使用boost库方案:

使用Boost库中的boost::filesystem::path来保证路径操作在不同平台上的一致性;在多线程编程中,可以使用Boost库中的线程、互斥锁和条件变量等组件来保证多线程代码在不同平台上的可移植性。

1
2
3
4
5
6
7
#include <boost/filesystem.hpp>
#include <iostream>
int main() {
boost::filesystem::path p("path/to/file.txt");
std::cout << "File extension: " << p.extension() << std::endl;
return 0;
}

交叉编译

构建机平台 –> 目标机平台

本机编译,顾名思义就是本机编译的程序或者动态在本机上运行(也可以在同平台的机器上运行,这里不做过多赘述),这个很好理解,要编译的程序或者动态库依赖的一些头文件或者库文件就在本机系统上,或在默认目录(以linux为例,/usr/include /usr/lib等),另外也可以通过人工指定的方式(例如qt中可以通过LIBS关键字或者INCLUDEPATH等指定库文件路径和头文件路径等)指明依赖的头文件和库文件位置

基于gitlab的工作流程设计

软件开发阶段

  • IDEA: 每一个从点子开始的项目,通常来源于一次闲聊。在这个阶段,GitLab 集成了Mattermost
  • ISSUE: 最有效的讨论一个点子的方法,就是为这个点子建立一个工单讨论。你的团队和你的合作伙伴可以在工单追踪器issue tracker中帮助你去提升这个点子
  • PLAN: 一旦讨论得到一致的同意,就是开始编码的时候了。但是等等!首先,我们需要优先考虑组织我们的工作流。对于此,我们可以使用工单看板Issue Board。
  • CODE: 现在,当一切准备就绪,我们可以开始写代码了。
  • COMMIT: 当我们为我们的初步成果欢呼的时候,我们就可以在版本控制下,提交代码到功能分支了。
  • TEST: 通过GitLab CI,我们可以运行脚本来构建和测试我们的应用。
  • REVIEW: 一旦脚本成功运行,我们测试和构建成功,我们就可以进行代码复审code review以及批准。
  • STAGING:: 现在是时候将我们的代码部署到演示环境来检查一下,看看是否一切就像我们预估的那样顺畅——或者我们可能仍然需要修改。
  • PRODUCTION: 当一切都如预期,就是部署到生产环境的时候了!
  • FEEDBACK: 现在是时候返回去看我们项目中需要提升的部分了。我们使用周期分析 Cycle Analytics来对当前项目中关键的部分进行的反馈。

m1 mac Rosetta2转译

m1 mac Rosetta2转译运行命令行程序如下:

arch -x86_64 /bin/zsh 进入转译模式终端:进入后,在该终端执行的所有命令会默认以arch -x86_64开头

可以使用archuname -m判断当前架构

如果返回i386或x86_64表明已经进入转译模式

如果要单独执行的命令在转译模式下,只需要在前面添加arch -x86_64 即可

  • 从 Apple Silicon 支持开始,Homebrew 推荐在 ARM 处理器的 Mac 上使用 /opt/homebrew 作为安装路径
  • 在 Intel 处理器架构设计的 Homebrew 默认安装路径(/usr/local

mac上部署docker开发环境

优势:在windows或mac上获得linux开发环境

  • 针对M系列芯片的docker已经完全适配
  • 文件访问,使用挂载方式,可以保证文件共享,mac与linux系统之间文件访问没有障碍
  • 资源消耗
  • 外网访问

参考视频|720x360

基于docker进行开发:

arm下linux环境的C++开发环境,带valgrind的docker

x64下linux环境的C++开发环境,带valgrind的docker mac上使用该docker环境会运行在Rosetta上

安装好docker两个镜像后配置vscode或clion

此处附带一个微软官方的开发环境docker镜像(其内还支持vcpkg) 该镜像链接

clion配置示例

注意,Docker Desktop一定要一直开着,才能连接docker进行开发

除了Linux,Windows和Mac都是使用Docker Desktop

image-20240601171257644 image-20240601171337207 image-20240601171414611 image-20240601171458121

配置vcpkg

运行效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;

int main() {
std::cout << "Hello, World!" << std::endl;
int i=isupper('F');
int j=isupper('f');
int32_t f;

int *p = new int[100];
cout<<"for F is upper was "<<i<<endl;
cout<<"for f is upper was "<<j<<endl;
cout<<"Uninitialized int is "<<f<<endl;
return 0;
}

这段代码在不同环境下的运行结果

  • arm mac下

    1
    2
    3
    4
    Hello, World!
    for F is upper was 1
    for f is upper was 0
    Uninitialized int is 83668748
  • arm linux下

    1
    2
    3
    4
    Hello, World!
    for F is upper was 256
    for f is upper was 0
    Uninitialized int is 65535
  • X64 linux下

    1
    2
    3
    4
    Hello, World!
    for F is upper was 256
    for f is upper was 0
    Uninitialized int is 64

使用[[C++相关工具盘点#valgrind|valgrind]]步骤

valgrind使用

在容器中使用whereis valgrind查看valgrind位置,将可执行文件位置拷贝到clion的设置-构建、执行、部署-动态分析工具-Valgrind

image-20240603092546972

创建docker容器的目的仅仅是获知valgrind的可执行文件的地址填入clion,因为clion实际上会自动启用无名的临时docker容器,无法进入终端.

接下来只需要点击下图所选项即可

image-20240603093426918

vscode使用docker开发环境

vscode的docker插件,针对镜像可以右键选择附加Visual studio code

image-20240603192915759

将会打开一个在docker中的vscode,在docker中打开的vscode没有任何插件,需要重新为docker中的vscode安装

使用Dev Containers会更简单一些,可以轻松配置一些需要用到的环境

使用Dev Containers开发流程

详情可点击参考

已经存在容器

  • 运行一个开发容器(同时将现在的目录挂载到容器内/work路径)

    docker run -it --rm -v $(pwd):/work 开发镜像名

  • 在Dev Containers中将看到在运行的容器,点击附加到容器

  • 在新打开的vscode中到/work路径下工作,还需要为vscode重新安装相关拓展

Dev Containers自带的开发容器

也可以直接使用Dev Containers拓展中的新的开发容器选项:创建一个容器,默认自带了常用开发套件,包括vcpkg

在m1 mac上自动生成的cpp容器需要更改devcontainer.json文件内容,如下:(因为是arm架构)(其他视图固化的指令修改也可以通过类似操作实现)

1
2
3
4
5
6
7
"name": "C++",
"build": {
"dockerfile": "Dockerfile"
},
"containerEnv": {
"VCPKG_FORCE_SYSTEM_BINARIES": "1"
}

新增containerEnv块,目的是相当于执行了export VCPKG_FORCE_SYSTEM_BINARIES=1(但重启不会生效),或相当于Dockerfile添加了ENV VCPKG_FORCE_SYSTEM_BINARIES=1,添加后会询问是否重建容器

这样的好处是,拓展帮我们自行进行映射,vcpkg安装的东西,项目文件夹中的所有改动,以及vcpkg安装的拓展都会帮我们映射到相应的卷中,非常方便

如果连接的是远程主机上的docker

需要先通过remote ssh连接到远程主机,再使用docker插件或Dev Containers插件连接到远程主机上的docker.

但是连接到远程主机上的docker会没有用户权限(发现无法连接),需要将登陆用户添加到docker用户组中

1
2
3
sudo gpasswd -a <当前登录用户名> docker
#从用户组中删除
sudo gpasswd -d <当前登录用户名> docker

重启远程服务器即可使用

1
sudo reboot

p.s. 改动的过程中频繁需要输入密码,可以通过ssh key方式免掉这些输入密码的操作

  1. ssh-keygen先生成公私密钥对

    生成与放置的位置,以windows和Linux为例

    /home/用户名/.ssh

    C://用户/用户名/.ssh

  2. 将公钥(pub后缀文件)放到远程服务器的这个路径

即生效(不生效的话重启远程服务器)

mac相关开发运维配置记录

mac环境变量配置

在macOS上,双击启动应用程序和在命令行中执行open Ollama.app有以下几个主要区别

  • 双击启动: macOS的Finder是通过Launch Services来管理和启动应用程序的,Launch Services不会读取/etc/profile或用户的shell环境配置
  • 命令行启动: 通过命令行启动时,应用程序的进程是由当前shell fork出来的,所以它能继承当前shell的环境变量配置

相比于使用export OLLAMA_HOST=0.0.0.0:11434配置环境变量

使用下面来配置,可以同时作用于双击启动和命令行启动

launchctl setenv OLLAMA_HOST "0.0.0.0:11434" 允许任何来源ip访问11434端口

取消设置使用:launchctl unsetenv OLLAMA_HOST

mac开启ssh连接

  • 输入命令 sudo systemsetup -setremotelogin on并输入密码启用
  • 通过 sudo systemsetup -setremotelogin off关闭

验证SSH是否开启

在终端中输入命令 sudo systemsetup -getremotelogin如果显示 Remote Login: On表示 SSH 已开启

可以在终端尝试ssh连接本机:

1
ssh $(whoami)@localhost

可编辑 /etc/ssh/sshd_config文件来修改默认端口、禁用root登录等,修改后需重启SSH服务

mac上的虚拟机方案

multipass:一键运行ubuntu,纯命令行操作,不占资源

ipad上可以使用utm

GDB调试原理

优质视频参考

Mac终端美化

使用iterm2终端 + 配合Oh My Zsh主题

语法高亮插件

然后为了语法高亮,可以配置zsh-syntax-highlighting插件,步骤如下:

  1. 确定是否安装可以通过: ls ~/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting
  2. 安装命令: git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
  3. 配置启用插件
    1. vim ~/.zshrc
    2. plugins 数组中添加 zsh-syntax-highlighting,并确保其位于最后一行(否则可能被覆盖): plugins=(git other-plugin zsh-syntax-highlighting) # 必须放在末尾!
    3. 保存后,刷新配置: source ~/.zshrc

自动建议插件

  1. git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
  2. 并在 .zshrcplugins 数组中添加 zsh-autosuggestions(位于高亮插件之前)