既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
不去管vim与Emacs的世纪之争,vim真的是很强大的编辑器,摆脱对图形界面的依赖,效率会有很大提高。vim有无穷尽的插件,善加选择利用,有很多针对编程的快捷功能是vs也提供不了的。
vim的学习曲线虽然有些陡峭,但还是很值得的。
网上有很多学习Vim的资源,推荐几本书:《Vim用户手册中文版7.3》《A Byte of Vim》《Practical Vim》,还有这个视频教程(需要翻墙)。
使用vim,一般只需动用肌肉“记住”一些常用操作(如下表),其余功能用的时候查询即可。
vim入门
vim入门主要是熟练使用基本命令。最直接的学习资源是在Terminal里输入vimtutor
,会有一个差不多30分钟的入门教程。
或者学习上文提到的书籍和视频,对常见命令均有讲解。
推荐直接从官方文档或是书籍入手,比在网上搜来搜去只鳞片爪有效率的多。四处搜索别人的学习经验,出发点是少走弯路,结果反而欲速则不达,还不如沉下心好好看完一本书。
有几张不同角度的cheat sheet,方便查阅。作者见文末参考链接。
[外链图片转存中…(img-KCf3n9hV-1715802153764)]
[外链图片转存中…(img-e8MN39CE-1715802153765)]
在vim中,获得帮助的命令是:help
。
如果不知道自己想查什么,可以运行:help user-manual
查看整个用户手册的目录。
如果大概知道想使用的功能而不知道具体命令,可以使用:helpgrep
在整个文档中搜索相关内容。比如想了解如何查看词首,可以运行:helpgrep beginning of a word
。可以使用:cnext
和:cprev
在搜索结果间跳转。
更多关于帮助的帮助,参看《A Byte of Vim》中Vim en:Help这一节。
vim自定义配置
基本操作熟练之后,多半都会开始安插件折腾自定义配置。
自定义配置与插件包括vimrc、global plugin、filetype plugin、syntax highlighting plugin、compiler plugin。
一般将简单配置记录在vimrc文件里,高级功能使用插件来实现。
在Linux系统中,vimrc文件地址是$HOME/.vimrc
。vim安装时自带的插件在$VIMRUNTIME/plugin/
目录下。自定义插件放在$HOME/.vim/plugin/
的相应子目录下,且需在vimrc文件里设置自动加载此插件。
详细的配置与插件使用及编写请参看《A Byte of Vim》中Vim en:Plugins这一节。
偷了个懒,在github上发现一个很棒的配置方案spf13-vim,就直接安上了。
spf13-vim是运行在vim层之上,为方便编程,对vimrc和plugin都做了很多特殊优化配置的一个工具。
spf13-vim。安装很容易,根据readme上的指示进行即可。
安装成功后,可以打开~/.vimrc
看到spf13-vim的预置配置。创建~/.vimrc.local
和~/.gvimrc.local
进行自定义配置。
spf13-vim已经预置了很多插件,关于这些插件的启动和使用可以看spf13-vim的介绍和这些插件自己的github页面。此处唯一提醒的一点是<leader>
键在spf13-vim这里是逗号,
。
我目前也在摸索中,有心得会随时更新。这是我的操作速查表。
在Quora上看到有些人并不推荐使用spf13等工具,认为会引诱你学习这个工具的配置方式,而不是真正学习vim的配置文件,使用自定义的针对自己需求的配置文件才最符合vim精神。不过对于初学者来说,我觉得spf13-vim还是很有价值的,毕竟时间这么宝贵,折腾是很累人的。
编译运行
gcc
GCC(GNU Compiler Collection)是一组编译工具的总称,支持多平台、多语言源文件到可执行文件的编译与生成。其中也包括gcc(C编译器)和g++(C++编译器)。
推荐书籍《An Introduction to GCC》。
在GCC内部寻找帮助,使用gcc --help
,如果想看gcc选项的完整列表使用gcc -v --help 2>&1 | more
。
最简单的应用示例:一个hello.cpp文件。一下语句就是编译与运行。
1
2
|
g++ hello.cpp hello
./hello
|
gcc常用命令
基本语法格式如下。
对于C:gcc [options] [filenames]
对于C++:g++ [options] [filenames]
上述命令行按编译选项(options)指定的操作对给定的文件(filenames)进行编译处理。
选项主要列表如下。
选项 | 选项描述 |
---|---|
-c | 只对文件进行编译和汇编,但不进行连接,生成目标文件”.o” |
-S | 只对文件进行编译,但不汇编和连接 |
-E | 只对文件进行预处理,但不编译汇编和连接 |
-g | 在可执行程序中包含标准调试信息 |
-o file1 [file2] | 将文件file1编译成可执行文件file2 |
-v | 打印出编译器内部编译各过程的命令行信息和编译器的版本 |
-I dir | 在头文件的搜索路径列表中添加dir目录 |
-L dir | 在库文件的搜索路径列表中添加dir目录 |
-static | 强制链接静态库 |
-lNAME | 连接名为libNAME的库文件 |
-Wall -W | 开启GCC最常用的警告,GCC的warning一般格式为file:line-number:message |
-pedantic | 要求严格符合ANSI标准 |
-Wconversion | 开启隐式类型转换警告 |
-Wshadow | 开启同名变量函数警告 |
-Wcast-qual | 开启对特性移除的cast的警告,如const |
-O(-O1) | 对编译出的代码进行优化 |
-O2 | 进行比-O高一级的优化 |
-O3 | 产生更高级别的优化 |
-Os | 产生最小的可执行文件 |
-pg | 开启性能测试,记录每个函数的调用次数与时长 |
-ftest-coverage | 记录每一行被执行的次数 |
-fprofile-arcs | 记录每个分支语句执行的频率 |
注意选项的大小写。只在最后发行版时再使用优化。即使在最后发行版也应该加上-g选项。
几种情境
以c++为例。
编译单个文件为可执行文件:
g++ -Wall -W hello.cpp -o hello
编译多个文件为可执行文件:
g++ -Wall -W main.cpp hello_fun.cpp -o newhello
编译单个文件为可执行文件,连接静态库static library:
系统默认库文件在目录
/usr/lib
和/lib
,还会自动搜索/usr/local/lib/
和/usr/lib/
。
相应的,系统默认头文件在目录,会自动搜索/usr/local/include/
和/usr/include/
。
显式指定库目录与文件,g++ -Wall -W calc.cpp /usr/lib/libm.a -o calc
或更好的写法g++ -Wall -W -static calc.cpp -lm -o calc
,连接系统自动搜索库目录里的库文件。注意g++优先使用shared library。如果找到同名.so就不会用同名.a。所以如果需要强制使用.a文件的话,应使用-static
。
如果不在自动搜索的库目录,两种方法。一般使用命令行这一种。
- 使用命令行
-I
添加搜索头文件的目录和-L
添加搜索库文件的目录,例如g++ -Wall -W -static -I/code/test/include -I/code/another/include -I. -L/code/test/lib -L/code/another/lib -L. calc.cpp -ltest
。.表示当前目录。- 或使用环境变量
C_INCLUDE_PATH
©或CPLUS_INCLUDE_PATH
(C++)和LIBRARY_PATH
。用如下语句添加搜索路径,之后就可以使用g++ -Wall -W -static calc.cpp -ltest
了。
1 2 3 4
|
CPLUS\_INCLUDE\_PATH = /code/test/include:/code/another/include:.:$CPLUS\_INCLUDE\_PATH export CPLUS\_INCLUDE\_PATH LIBRARY\_PATH = /code/test/lib:/code/another/lib:.:$LIBRARY\_PATH export LIBRARY\_PATH
|
编译单个文件为可执行文件,连接共享库shared library:
如果只是用了系统默认库,
g++ -Wall -W calc.cpp -lm -o calc
除了前文系统自动搜索库文件目录之外,如果要添加其他共享库目录,必须在环境变量LD_LIBRARY_PATH
中添加路径。
1 2
|
LD\_LIBRARY\_PATH = /code/test/lib:/code/another/lib:.:$LD\_LIBRARY\_PATH export LD\_LIBRARY\_PATH
|
之后可以使用
g++ -Wall -W calc.cpp /usr/lib/libm.so -o calc
或g++ -Wall -W -I/code/test/include -I/code/another/include -I. -L/code/test/lib -L/code/another/lib -L. calc.cpp -ltest
。
使用ldd calc
可以查看该可执行文件依赖哪些.so。
编译多个文件为静态库文件.a:
将多个.o文件集合为一个静态库文件.a。其中cr表示”create and replace”。
1 2 3
|
g++ -Wall -c hello\_fn.cpp g++ -Wall -c bye\_fn.cpp ar cr libhello.a hello\_fn.o bye\_fn.o
|
可以查看一个.a文件里包含哪些.o。使用
ar t libhello.a
。
编译多个文件为共享库文件.so:
g++ x.cpp y.cpp z.cpp -fPIC -shared -o libtest.so
。其中-fPIC表示编译为位置独立的代码。
预处理
可以用gcc选项定义宏,-DNAME会定义一个名为NAME的宏。如g++ -Wall -DTEST dtest.cpp
,定义了名为TEST的宏。定义的宏会对代码产生影响。
也可以为宏定义值,-DNAME=VALUE。如g++ -Wall -DNUM=100 dtestval.cpp
,g++ -Wall -DNUM="2+2" dtestval.cpp
,g++ -Wall -DMESSAGE="\"Hello,World!\"" dteststr.cpp
。在代码中把宏用括号括起来是好习惯。
性能
开启-pg选项生成可执行文件后。首先正常运行一次可执行文件./hello
,然后运行gprof hello
查看数据。
开启-fprofile-arcs -ftest-coverage选项生成可执行文件后。首先正常运行一次可执行文件./hello
,然后运行gcov hello.cpp
查看数据。未被执行的语句会在.gcov文件中标记上-,可以通过执行grep '-' *.gcov
查找未被执行的语句。
makefile文件
对于较大的工程,如果还像前文一样写命令行就太痛苦了。而使用makefile可以管理整个工程的编译规则,之后用一个make命令就可自动编译,相对方便很多。
推荐书籍《跟我一起写Makefile》和官方文档。内容看起来还是很晦涩…(・-・*)还好现在有了自动化工具,用来自动生成的工具也可以用工具自动生成了。
最基础的用法
makefile基本规则是:
target1 target2 target3: prerequisite1 prerequisite2
command1
command2
其中target是目标文件。prerequisites是要生成target所需文件或目标。command是make需要执行的命令。规则表示了一个文件的依赖关系,即target依赖于prerequisites,生成规则为command。如果target不存在或prerequisites中至少一个文件比target新的话,command定义的命令就会执行。这就是makefile中最核心的内容。
make工作的流程是:
当我们输入make命令之后,
- 读入所有的makefile文件。
- 读入被include包括的其他makefile文件。
- 初始化文件中的变量。
- 推导隐式规则,分析所有规则。
- 为所有的目标文件创建依赖关系链。
- 根据依赖关系,决定哪些目标要重新生成。
- 执行生成命令。
makefile文件名应为Makefile
或makefile
。
小示例
我为自己刷题的一个小项目写了一个将几个目录下所有的源文件一起编译的一个makefile。在这里看工程的目录结构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
EXEC = MyLeetCode
SRC\_DIR = ../../catch ../../include ../../src
SOURCES := $(foreach x, ${SRC\_DIR},\
$(wildcard \
$(addprefix ${x}/\*,.cpp)))
OBJECTS = $(SOURCES:.cpp=.o)
CXX = g++
CXXFLAGS = -I../../catch -I../../include -I../../src -std=c++11
$(EXEC): $(OBJECTS)
$(CXX) -o $@ $^
.PHONY : clean
clean:
$(RM) $(OBJECTS)
$(RM) $(EXEC)
|
网上看到一个Generic Makefile for C/C++ Program。
使用CMake自动生成makefile
当处理较大型的项目时,手动书写makefile就比较痛苦,这时用来用来自动化自动化工具makefile的自动化工具就是CMake。不过天下哪有那么便宜的事,它也是要写自己的CMakeLists.txt的。
推荐书籍《CMake实践》《Mastering CMake》和官网帮助。
简介
CMake是一个跨平台的自动化建构系统,它是用一个名为CMakeLists.txt的文件来描述构建过程,可以产生标准的构建文件,如Unix的makefile或Windows Visual Studio的projects/workspaces。
文件CMakeLists.txt需要手工编写,也可以通过编写脚本进行半自动的生成。
在Linux平台下使用CMake生成makefile并编译的流程如下:
- 安装CMake。在Ubuntu上安装cmake很简单
$sudo apt-get install cmake
。如果想要其Qt图形界面另需安装sudo apt-get install cmake-qt-gui
。一般不需要,在Ubuntu系统上用ccmake就可以了。 - 编写CMakeLists.txt。
- 运行CMake。用
cd
将当前目录设为生成目标目录,执行命令ccmake srcdir
(文字界面)或cmake -i
(交互命令行),如果想使用Qt图形界面使用cmake-gui
。 - Makefile已经生成。使用make命令进行编译。
- 如果想清理工程。使用
make clean
。
简单语法
注释:#
命令语法:COMMAND(参数1 参数2 ...)
字符串列表:A;B;C
或A B C
。分号或空格分隔的值。
变量(字符串或字符串列表):
set(Foo a b c)
设置变量Foo。
command(${Foo})
等价于command(a b c)
。
command("${Foo}")
等价于command("a b c")
。
command("/${Foo}")
转义,和a b c无关联。
流控制结构:
IF()...ELSE()/ELSEIF()...ENDIF()
WHILE()...ENDWHILE()
FOREACH()...ENDFOREACH()
正则表达式:
常用命令总结
命令 | 意义 |
---|---|
INCLUDE_DIRECTORIES( “dir1” “dir2” … ) | 头文件路径,相当于编译器参数 -Idir1 -Idir2 |
AUX_SOURCE_DIRECTORY( “sourcedir” variable) | 收集目录中的文件名并赋值给变量 |
ADD_EXECUTABLE | 可执行程序目标 |
ADD_LIBRARY | 库目标 |
ADD_CUSTOM_TARGET | 自定义目标 |
ADD_DEPENDENCIES( target1 t2 t3 ) | 目标target1依赖于t2 t3 |
ADD_DEFINITIONS( “-Wall -ansi”) | 本意是供设置 -D… /D… 等编译预处理需要的宏定义参数,对比 REMOVE_DEFINITIONS() |
TARGET_LINK_LIBRARIES( target-name lib1 lib2 …) | 设置单个目标需要链接的库 |
LINK_LIBRARIES( lib1 lib2 …) | 设置所有目标需要链接的库 |
SET_TARGET_PROPERTIES( … ) | 设置目标的属性 OUTPUT_NAME, VERSION, …. |
MESSAGE(…) | 这个指令用于向终端输出用户定义的信息 |
INSTALL( FILES “f1” “f2”DESTINATION . ) | DESTINATION 相对于 ${CMAKE_INSTALL_PREFIX} |
SET( VAR value [CACHE TYPE DOCSTRING [FORCE]]) | 定义与修改变量 |
LIST( APPEND/INSERT/LENGTH/GET/REMOVE_ITEM/REMOVE_AT/SORT …) | 列表操作 |
STRING( TOUPPER/TOLOWER/LENGTH/SUBSTRING/REPLACE/REGEX …) | 字符串操作 |
SEPARATE_ARGUMENTS( VAR ) | 转换空格分隔的字符串到列表 |
FILE( WRITE/READ/APPEND/GLOB/GLOB_RECURSE/REMOVE/MAKE_DIRECTORY …) | 文件操作 |
FIND_FILE | 注意 CMAKE_INCLUDE_PATH |
FIND_PATH | 注意 CMAKE_INCLUDE_PATH |
FIND_LIBRARY | 注意 CMAKE_LIBRARY_PATH |
FIND_PROGRAM | |
FIND_PACKAGE | 注意 CMAKE_MODULE_PATH |
EXEC_PROGRAM( bin [work_dir] ARGS <…> [OUTPUT_VARIABLE var] [RETURN_VALUE var] ) | 执行外部程序 |
OPTION( OPTION_VAR “description” [initial value] ) |
变量
工程路径
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
表示工程顶层目录。
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
表示生成目标目录。
CMAKE_CURRENT_SOURCE_DIR
表示当前处理的CMakeLists.txt所在的目录。
CMAKE_CURRRENT_BINARY_DIR
表示当前处理的CMakeLists.txt的目标目录。
CMAKE_CURRENT_LIST_FILE
输出调用这个变量的CMakeLists.txt的完整路径。
Debug和Release模式的构建
在CMakeList.txt文件中使用
SET(CMAKE_BUILD_TYPE Debug)
。
或命令行参数cmake DCMAKE_BUILD_TYPE=Release
。
编译器参数
CMAKE_C_FLAGS
CMAKE_CXX_FLAGS
也可以通过指令ADD_DEFINITIONS()
添加。
包含路径
CMAKE_INCLUDE_PATH
配合FIND_FILE()
以及FIND_PATH()
使用。如果头文件没有存放在常规路径(/usr/include, /usr/local/include等),则可以通过这些变量就行弥补。如果不使用FIND_FILE
和FIND_PATH
的话,CMAKE_INCLUDE_PATH
没有任何作用。
CMAKE_LIBRARY_PATH
配合配合FIND_LIBRARY()
使用。否则没有任何作用。
CMAKE_MODULE_PATH
CMake为上百个软件包提供了查找器(finder):FindXXXX.cmake。当使用非CMake自带的finder时,需要指定finder的路径,这就是CMAKE_MODULE_PATH
,配合FIND_PACKAGE()
使用。
编写CMakeLists.txt的示例
对于如下的目录结构。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
+example
|
+--- CMakeList.txt
+--+ src/
| |
| +--- main.cpp
| /--- CMakeList.txt
|
+--+ thirdparty/
| |
| +--- hello.h
| +--- hello.cpp
| /--- CMakeList.txt
|
/--+ build/
|
多文件夹编译(手动)
将所有子文件夹中的源文件包含进来,然后生成。
在顶文件夹中example的CMakeLists.txt中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
cmake\_minimum\_required(VERSION 2.6)
project(HELLO)
# include src
include(src/CMakeLists.txt)
foreach(FILE ${FILES})
set(subdir1Files ${subdir1Files} src/${FILE})
endforeach(FILE)
# include thirdparty
include(thirdparty/CMakeLists.txt)
foreach(FILE ${FILES})
set(subdir2Files ${subdir2Files} thirdparty/${FILE})
endforeach(FILE)
# add the source files to the executable
add\_executable(hello ${subdir1Files} ${subdir2Files})
|
在src目录的CMakeLists.txt中:
1
2
|
# list the source files for this directory
set (FILES main.cpp)
|
在thirdparty目录的CMakeLists.txt中:
1
2
|
# list the source files for this directory
set(FILES hello.h hello.cpp)
|
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
|
在src目录的CMakeLists.txt中:
| | |
| --- | --- |
|
1
2
|
list the source files for this directory
set (FILES main.cpp)
|
在thirdparty目录的CMakeLists.txt中:
| | |
| --- | --- |
|
1
2
|
list the source files for this directory
set(FILES hello.h hello.cpp)
|
[外链图片转存中...(img-QQv4r0uN-1715802153766)]
[外链图片转存中...(img-3TDYY85C-1715802153766)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**