C++进阶篇:001.linux入门
一、vim编辑器的使用
1.如何打开vim编辑器
1. 先touch一个文件,然后使用指令:vi/vim + filename即可,这种方式打开文件,无论是否编辑文件,都会在文件系统中产生一个普通文件2. 直接使用vi/vim + 不存在的文件名,如果对文本内容进行更改,则保存退出后,会在文件系统上产生一个普通文件,如果没有更改数据,保存后退出,则不会创建该文件
2.命令行模式
1.使用vi + 文件名,刚进入编辑器的模式就是命令行模式2.命令行模式的使用,主要完成对文本内容整体的复制、粘贴、剪切、删除、光标移动等操作3.命令行模式的主要操作:yy:复制当前行的文本内容nyy:复制光标所在行及以后的n行文本内容p:将剪切板中的内容进行粘贴dd:删除光标所在的一行文本内容ndd:删除光标所在行及以后的n行文本内容u:撤销上一步操作ctrl + r:反撤销gg:将光标直接跳到首行位置nG:将光标跳到第n行0:表示光标移动到当前行行首位置$(ctrl + shift + 4):表示光标移动到当前行行尾位置
3.插入模式
1.功能:用于文件内容的编辑2.如何进入插入模式:只能从命令行模式进入,不能从底层模式进入键盘上特殊的键能从命令行模式进入插入模式:Insert键:从光标所在的字符前进入插入模式i键:从光标所在的字符前进入插入模式I键:从光标所在行行首进入插入模式a键:从光标所在的字符后面进入插入模式A键:从光标所在行行尾进入插入模式o键:从光标所在行的下一行进入插入模式O键:从光标所在位置的上一行进入插入模式s键:删除光标所在的字符后,从光标位置进入插入模式S键:删除光标所在的一行文本后,从当前行进入插入模式
4.底行模式
1.功能:完成对文本内容的保存、退出操作或者替换、查找工作2.如何进入底行模式:从命令行模式中键入shift + :(其实就是输入一个冒号)3.主要操作:w 保存文本内容:q 退出编辑器:q! 强制退出:wq 保存后退出:x 保存后退出:set number 显示行号:set nonumber 不显示行号:/string 查找字符串string,并将光标指向该字符串的首位置:%s/string1/string2/g 表示将所有的string1更换成string2:m,ns/string1/string2/g 表示[m, n]行闭区间内的所有string1更换成string2
二、可执行程序编译过程
1.如何将程序编译生成可执行程序
方法一
一步到位的编译
1.g++ ***.cpp //此时会默认生成一个 a.out 的可执行程序
2.g++ ***.cpp -o 可执行程序名称 //此时会生成可执行程序
方法二(重要!)
分步编译: ESc --> .iso
1.预处理(Pre_processing)功能:将源程序头文件展开、删除、注释、宏替换语法格式:g++ -E ***.cpp -o ***.i2.编译(Compiling)功能:将程序编译生成汇编语言语法格式:g++ -S ***.i -o ***.s3.汇编(Assembling)功能:将汇编语言编译生成二进制文件语法格式:g++ -c ***.s -o ***.o4.链接(Linking)功能:链接相关库文件,生成可执行程序语法格式:g++ ***.o -o 可执行程序
三、sys库的使用
1.什么是man手册
linux系统提供的有关函数或指令介绍的相关帮助手册,可以在该手册页中查看函数、指令的功能。一共有七章内容,主要使用前三章,第一章是shell指令相关说明,第二章是系统调用函数相关说明(重点),第三章是库函数(重要)
2.man手册的使用
进入手册:只需要键入man + 相关函数/指令即可
退出手册:键入q
3.查询指令
4.C语言函数库
如果想要查看C语言库函数相关功能,需要安装相关库
sudo yum install man-pages man-pages-devel
5.常用的内核提供的函数库sys
- 文件的操作:open()、read()、write()、close()、lseek() 等等
- 进程控制函数:fork()、exit()、wait()、execl() 等等
- 信号操作:kill()、signal() 等等
- 网络通信:socket()、bind()、listen() 等等
6.使用GDB调试程序
-
在linux系统下的警告和错误,当程序出现bug时,linux终端会给大家两种不同的信息
警告(warning):有时的警告是不影响可执行程序的产生
错误(error):错误如果不改正,是不能生产可执行程序的
总结:相比于gcc编译器,g++编译器要求更严格
警告可以被忽略,继续产生可执行程序,但是错误必须更改后才能产生可执行程序
-
什么是gdb:https://www.sourceware.org/gdb/
GDB,GNU项目调试器,允许您查看一个程序执行时"内部"发生了什么,或者一个程序崩溃时正在做什么
-
gdb是干啥的
gdb只要做四件事:
启动程序,指定可能影响其行为的任何内容。 使程序在指定条件下停止。 检查程序停止时发生了什么。 更改程序中的内容,这样您就可以尝试纠正一个错误的影响,并继续了解另一个错误。
gdb可以调试指定的当前程序,向程序中传递参数
gdb还可以调试出错的文件,查看错误原因
gdb还可以调试正在运行的进程
-
如何使用gdb
1)准备C++程序
#include <iostream> #include <stdio.h>using namespace std;void print() {cout << "hello world" << endl;cout << "这是要我想说给大家听的!!!" << endl;} int main(int argc, const char* argv[]) {int arr[5] = {1, 2, 3, 4, 5};int i = 0;for(i = 0; i < 5; i++){cout << arr[i] << " ";}cout << endl;print();return 0; }
2)编译程序,编译选项中需要加上 -g
g++ -g ***.cpp -o ***
3)启动gdb调试
gdb ./***
4)gdb常用指令
quit (q):表示退出gdb模式
run (r):表示执行可执行程序,如果没有设置断点,则将整个程序从头到尾执行一遍
list (l):展示可执行程序的相关行信息,默认展示10行
list m, n:表示展示从m行到n行的信息
list func:表示展示func函数旁边的相关程序

break (b):表示设置断点,当调试器将程序运行到断点所在位置后,会暂停于此
break 行号:表示在某行设置断点
break func:表示在指定的函数处设置断点
info break:查看所有断点的信息
delete breakpoint 断点编号:表示删除指定的断点
next (n):表示执行下一条语句
continue(c):表示从断点处继续向后执行,直到遇到下一个断点或者程序结束
step(s):能够跳入到指定函数中,查看相关函数内部代码
print(p) 变量名/地址:表示打印指定变量或地址信息
set variable 变量名=值:表示给某个变量设置相关的值
gdb使用小技巧
shell:后面可以跟终端指令,表示执行终端相关操作
set logging on:设置开启日志功能,会在当前目录中生成一个gdb.txt文档文件记录接下来的调试内容
watchpoint:观察点,如果设置的观察点的值发生改变,则会将该值的旧值和新值全部展示出来
gdb调试出错的文件
当一个可执行程序出现错误时,会产生一个core文件,用于查看相关错误信息
linux系统默认是不产生core文件,需要进行相关设置后才能产生
通过ulimit -a 查看所有linux的限制内容
通过 ulimit -c unlimited来设置core文件的个数
查看错误原因
gdb调试其他正在运行的进程
./可执行程序 & :表示将可执行程序后台运行,不占用当前终端
[zpp@localhost day2]$ g++ -g 05test.cpp //编译程序
[zpp@localhost day2]$ ./a.out & //后台运行程序
[1] 26837 //显示当前后台运行程序的作业号和
进程号
[zpp@localhost day2]$ gdb -p 26837 //调试指定的运行的进程
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
4> 库的制作(静态库与动态库)
1、为什么引入库
在上述案例中,主程序要是有的源程序代码,在add.cpp中,如果项目结束后,到了交付阶段,由
于主程序的生成需要其他程序联合编译,那么就要将源程序打包一起发给老板,这样该程序的开发者自
身的价值就不大了,该项目的知识产权就很容易被窃取。为了保护我们的知识产权,我们引入了库的概
念。
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 26837
Reading symbols from /home/zpp/day2/a.out...done.
Reading symbols from /lib64/libstdc++.so.6...(no debugging symbols
found)...done.
Loaded symbols for /lib64/libstdc++.so.6
Reading symbols from /lib64/libm.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libm.so.6
Reading symbols from /lib64/libgcc_s.so.1...(no debugging symbols found)...done.
Loaded symbols for /lib64/libgcc_s.so.1
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols
found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
main (argc=1, argv=0x7ffd31897908) at 05test.cpp:17
17 int main(int argc, const char *argv[]) //进程运行在某处
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64
libgcc-4.8.5-44.el7.x86_64 libstdc++-4.8.5-44.el7.x86_64
(gdb) n
22 test();
(gdb) n
23 test1();
(gdb) step
test1 () at 05test.cpp:12
12 int num = 520;
(gdb) n
13 num++;
(gdb) c
Continuing.
四、库的概念、动态库与静态库
1.为什么引入库
在上述案例中,主程序要是有的源程序代码,在add.cpp中,如果项目结束后,到了交付阶段,由于主程序的生成需要其他程序联合编译,那么就要将源程序打包一起发给老板,这样该程序的开发者自身的价值就不大了,该项目的知识产权就很容易被窃取。为了保护我们的知识产权,我们引入了库的概念。
2.什么是库
库在linux中是一个二进制文件,它是由.cpp文件(不包含main函数)编译而来,其他程序如果想要使用该源文件中的函数时,只需要在编译生成可执行程序时,链接上该源文件生成的库文件即可。库中存储的是二进制文件,不容易被窃取知识产权,做到了保护作用。
库在linux系统中分为两类,分别是静态库和动态库
windows:
***.lib(静态库)
***.dll(动态库)
linux:
***.a:静态库
***.so:动态库
3.静态库及其制作
概念:将一个***.cpp的文件编译生成一个lib***.a的二进制文件,当你需要使用该源文件中的函数时,只需要链接该库即可,后期可以直接调用
静态体现在:在使用g++编译程序时,会将你的文件和库最终生成一个可执行程序(把静态库也放入到可执行程序中),每个可执行程序单独拥有一个静态库,体积较大,但是执行效率较高
3.1准备程序
add.h
#ifndef _ADD_H_
#define _ADD_H_int add(int m, int n); //函数声明#endif
add.c
int add(int m, int n)
{return m+n;
}
main.cpp
#include<iostream>
#include<stdio.h>
#include "add.h"using namespace std;int main(int argc, const char *argv[])
{cout << add(3,8) << endl; //调用外部文件中的相关函数return 0;
}
3.2编译生成静态库
g++ -c ***.c -o ***.o //只编译不链接,生成二进制文件
ar -crs lib***.a ***.o //编译生成静态库如果有多个.o文件共同编译生成静态库:ar -crs lib***.a ***.o xxx.o ...ar:用于生成静态库的指令
-c:用于生成静态库
-r:将文件插入或替换进静态库中同名文件
-s:重置静态库索引
3.3使用静态库
g++ main.cpp -L 库的路径 -l库名 -I 头文件的路径 //注意l和库名之间没有空格,而且库名不是lib***,而是***
4.动态库及其制作
概念:将一个***.cpp的文件编译生成一个lib***.so的二进制文件,当你需要使用该源文件中的函数时,只需要链接该库即可,后期可以直接调用
动态体现在:在使用g++编译程序时,会将你的文件和库中的相关函数的索引表一起生成一个可执行程序(把静态库也放入到可执行程序中),每个可执行程序只拥有函数的索引表,当程序执行到对应函数时,会根据索引表动态寻找相关库所在位置进行调用。体积较小,执行效率较低,但是可以多个程序共享同一个动态库,所以动态库也叫共享库
4.1准备程序
add.h
#ifndef _ADD_H_
#define _ADD_H_int add(int m, int n); //函数声明#endif
add.c
int add(int m, int n)
{return m+n;
}
main.cpp
#include<iostream>
#include<stdio.h>
#include "add.h"using namespace std;int main(int argc, const char *argv[])
{cout << add(3,8) << endl; //调用外部文件中的相关函数return 0;
}
4.2编译生成动态库
g++ -fPIC -c ***.cpp -o ***.o //编译生成二进制文件
g++ -shared ***.o -o lib***.so //依赖于二进制文件生成一个动态库上述两个指令可以合成
g++ -fPIC -shared ***.cpp -o lib***.so
4.3使用动态库
4.4以上错误的解决方式
方法1:更改路径的宏
export LD_LIBRARY_PATH=库的路径 //不要乱加括号!!!
方法2:将自己的动态库放入到系统的库函数目录中(/lib64 or /usr/lib)
4.5动态库和静态库编译生成可执行程序的大小比较
5.如何使用第三方库
- C/C++语言默认支持标准输入输出库的,但是其他的相关函数库需要第三方引入,并且,编译程序时,需要链接上对应的库
- 使用数学库:#include <math.h>
-
线程支持类库:#include <pthread.h>
如果不能支持线程进程库:sudo yum install manpages-posix manpages-posix-dev
五、Makefile
1.什么是Makefile
用于工程项目管理的一个文本文件,文件名为Makefile
Makefile可以大写也可以小写,一般Makefile首字母使用大写
如果Makefile和makefile都存在,系统会默认使用小写的
2.什么是make
make是一个执行Makefile的工具,是一个解释器,用来对Makefile中的命令进行解析并执行一个shell指令
make这个指令在 /usr/bin 中
如果没有安装make这个指令,安装指令如下:sudo yum install make
3.Makefile的用途
描述了整个工程的编译、链接规则
软件项目的自动化编译,相当于给软件编译写一份脚本文件
4.学习Makefile的必要性
Linux/Unix环境下开发的必备技能
系统架构师、项目经理的核心技能
研究开源项目、Linux内核源码的必需品
加深对底层软件构造系统及过程的理解
5.如何学习Makefile
-
理论基础
软件的构造过程、程序的编译和链接:预处理->编译->汇编->链接面向依赖的思想: 依赖一个.cpp文件的程序 可执行程序 <--依赖于-- .o文件 <--依赖于-- .s文件 <--依赖于-- .i文件 <--依赖于-- .cpp文件依赖多个.cpp文件的程序 可执行程序 <--依赖于-- .o文件 <--依赖于-- .s文件 <--依赖于-- .i文件 <--依赖于-- .cpp文件| -- .o文件 <--依赖于-- .s文件 <--依赖于-- .i文件 <--依赖于-- .cpp文件| -- .o文件 <--依赖于-- .s文件 <--依赖于-- .i文件 <--依赖于-- .cpp文件| -- .o文件 <--依赖于-- .s文件 <--依赖于-- .i文件 <--依赖于-- .cpp文件......
-
项目编程基础
C++程序语言基础 C语言基础 多文件原码管理、头文件包含、函数声明与定义
6.Makefile的工作过程
Makefile本身是面向依赖进行编写的
源文件 ---> 编译 ---> 目标文件 ---> 链接 ---> 可执行文件
hello.cpp ---> hello.o ---> hello
本质上一步就可以生成可执行程序,但是,由于在生成可执行程序时,可能会有多个文件进行参与,后期其他文件可能要进行更改,更改后,会影响到可执行程序的执行,其他没有更改的文件也要参与编译,浪费时间
7.第一个Makefile
-
编写一个hello.cpp文件
#include<iostream> #include<stdio.h> using namespace std; int main(int argc, const char *argv[]) {cout << "hello world" << endl;return 0; }
2.通过终端编译程序
3.使用Makefile管理功能
编写Makefile文件
# Makefile中的注释是以#开头 # 语法格式: # 目标:依赖 # 通过依赖生成目标的指令 # 注意:指令前面必须使用同一个tab键隔开,不能使用多个空格顶过来hello:hello.og++ hello.o -o hellohello.o:hello.sg++ -c hello.s -o hello.ohello.s:hello.ig++ -S hello.i -o hello.shello.i:hello.cppg++ -E hello.cpp -o hello.i
简化的Makefile文件
# Makefile中的注释是以#开头 # 语法格式: # 目标:依赖 # 通过依赖生成目标的指令 # 注意:指令前面必须使用同一个tab键隔开,不能使用多个空格顶过来 hello:hello.og++ hello.o -o hello hello.o:hello.cppg++ -c hello.cpp -o hello.o
4.执行Makefile文件
make ---> 默认找到Makefile中的第一个目标开始进行执行 make 目标 ---> 找到对应的目标进行执行
8.Makefile的语法规则
-
规则
构成Makefile的基本单元,构成依赖关系的核心部件
其他内容可以看作为规则的服务
-
变量
类似于C++中的宏,使用变量:$(变量名) 或者 ${变量名}
作用:使得Makefile更加灵活
-
条件执行
根据某一变量的值来控制make执行或者忽略Makefile的某一部分
-
函数
文本处理函数:字符串的替换、查找、过滤等等
文件名的处理:读取文件/目录名、前后缀等等
-
注释
Makefile中的注释,是以#号开头
9.规则
1、规则的构成:目标、目标依赖、命令
2、语法格式
目标:目标依赖命令注意事项:命令必须使用tab键开头,一般是shell指令一个规则中,可以无目标依赖,仅仅是实现某种操作一个规则中可以没有命令,仅仅描述依赖关系
3、目标详解
1)默认目标
一个Makefile里面可以有多个目标,一般选择第一个当做默认目标
也就是make默认执行的目标
2)多目标
一个规则中可以有多个目标,多个目标具有相同的生成命令和依赖文件
clean distclean:rm hello.[^cpp] hello
3)多规则目标
多个规则可以是同一目标
all:test1
all:test2
test1:echo "hello"
test2:echo "world"
4)伪目标
并不是一个真正的文件名,可以看做是一个标签
无依赖,相比一般文件,不会重新生成、执行
伪目标:可以无条件执行,相当于对应的指令eg:.PHONY:clean #设置伪目标clean:rm hello.[^cpp] hello
4、目标依赖
1)文件时间戳
根据时间戳来判断目标依赖是否要进行更新
所有文件都更改过,则对所有文件进行编译,生成可执行程序
在上次make之后修改过的cpp文件,会被重新编译
在上次make之后修改过的头文件,依赖该头文件的目标依赖也会重新编译
2)模式匹配
% ---->通配符匹配
$@ ---->目标
$^ ---->依赖
$< ---->第一个依赖
* ---->普通通配符注意:%是Makefile中的规则通配符,*是普通通配符
头文件
#ifndef _OPERATOR_H
#define _OPERATOE_H
int add(int m, int n);
#endif
源文件
int add(int m, int n)
{return m+n;
}
主程序:
#include<iostream>
#include"operator.h"
using namespace std;
int main(int argc, const char *argv[])
{cout << add(3,5) << endl;return 0;
}
#第一个依赖关系
all:operator
operator:main.o operator.o
# g++ main.o operator.o -o operatorg++ $^ -o $@
#main.o:main.cpp
# g++ -c $< -o $@
#operator.o:operator.cpp
# g++ -c $^ -o $@
#将上述两个规则统一成一个规则5、命令
10> 变量
1、变量基础
2、变量的分类
3、变量的外部传递
可以通过命令行给变量进行赋值操作make ARCH=g++
#此时的%表示所有的Makefile内容,不能换成*
%.o:%.cppg++ -c $< -o $@
clean:rm -f *.o operator #此处的*是普通通配符
5、命令
1)命令的组成由shell命令组成,以tab键开头2)命令的执行每条命令执行完,make会检测这个命令的返回码如果返回成功,则继续执行后面的命令如果返回失败,则make会终止当前执行的规则并退出3)并发执行命令make -j4 ----->表示开辟4个线程执行time make ----->执行make时,显示当前时间
10.变量
1、变量基础
1)变量定义:变量名 = 变量值
2)变量的赋值追加赋值:+= --->在原有的基础上追加相关内容条件赋值:?= --->如果之前没有值,则为变量赋值,如果之前有值,则不进行赋值
3)变量的使用: $(变量名)或者${变量名}
2、变量的分类
1)理解展开变量使用:=操作符进行赋值在解析阶段直接复制常量字符串
2)延迟展开变量使用=操作进行赋值将最后一次赋值的结果给变量名使用
3)注意事项一般在目标、目标依赖中使用立即展开赋值在命令中一般使用延迟展开赋值
3、变量的外部传递
可以通过命令行给变量进行赋值操作
make ARCH=g++
var1 = main.o #定义一个变量并赋值
var1 += operator.o #给变量追加值
CC:=g++
#第一个依赖关系
all:operator
operator:$(var1)
# g++ main.o operator.o -o operator$(CC) $^ -o $@
#main.o:main.cpp
# g++ -c $< -o $@
#operator.o:operator.cpp
# g++ -c $^ -o $@
#将上述两个规则统一成一个规则
#此时的%表示所有的Makefile内容,不能换成*
%.o:%.cpp$(CC) -c $< -o $@
clean:rm -f *.o operator #此处的*是普通通配符
11.条件执行
1、关键字
ifeq、else、endif
ifneq、else、endif
2、使用
ifeq (要判断的量,判断的值)Makefile语句
elseMakefile语句
endif注意:条件语句从ifeq开始执行,括号与关键字之间使用空格隔开括号里面挨着括号处,不允许加空格
12.总结
1、执行过程
进入编译目录、执行make命令
依赖关系解析阶段
命令执行阶段
2、依赖解析阶段
解析Makefile,建立依赖关系树
控制解析过程,引入Makefile,变量展开、条件执行
3、命令执行阶段
把解析生成的依赖关系树加载到内存
按依赖关系,按顺序生成这些文件
再次编译make会检测文件的时间戳,判断是否过期如果无过期,在不在编译如果文件有更新,则依赖该文件的所有依赖关系上的目标都会重新编译
4、make执行结果
make的退出码0:表示成功执行1:表示允许错误,退出
13.参考源码
1、头文件
#ifndef _OPERATOR_H
#define _OPERATOE_Hint add(int m, int n);#endif
2、源文件
int add(int m, int n)
{return m+n;
}
3、主程序
#include<iostream>
#include"operator.h"
using namespace std;
int main(int argc, const char *argv[])
{cout << add(3,5) << endl;return 0;
}
4、Makefile
var1 = main.o #定义一个变量并赋值
var1 += operator.o #给变量追加值
#条件选择,如果变量COMPILE的值是g++那么CC的值就是g++,否则是gcc
ifeq ($(COMPILE), g++)CC:=g++
elseCC:=gcc
endif
#第一个依赖关系
all:operator
operator:$(var1)
# g++ main.o operator.o -o operator$(CC) $^ -o $@
#main.o:main.cpp
# g++ -c $< -o $@
#operator.o:operator.cpp
# g++ -c $^ -o $@
#将上述两个规则统一成一个规则
#此时的%表示所有的Makefile内容,不能换成*
%.o:%.cpp$(CC) -c $< -o $@
clean:rm -f *.o operator #此处的*是普通通配符
六、vsc中使用cmake
1.前言
1> CMake是一个跨平台的安装编译工具,可以使用简单的语句来描述所有平台的安装(编译过程)
2> CMake可以说是已经成为大部分的C++开发项目的标配
3> 可以使用几行或者几十行的代码来完成非常冗长的Makefile代码
2.为什么要使用CMake
1> 在不使用CMake时,编译工程如下
2> 在上面的机制中,工程文件中添加一个源程序,bar.cpp
3> 使用CMake来管理工程的状态
4> 使用CMkake管理工程中添加一个新文件 bar.cpp
3.语法特性介绍
- 基本语法:指令(参数1 参数2 ...)
参数使用括号括起来
参数之间使用空格或分号隔开
- 注意:指令是大小写无关的,但是参数和变量是大小写相关的
set(HELLO hello.cpp) # 定义一个变量名叫HELLO 变量的值为hello.cpp
add_executable(hello main.cpp hello.cpp) # 通过main.cpp 和 hello.cpp 编译生成hello可执行程序
ADD_EXECUTABLE(hello main.cpp ${HELLO}) #作用同上
- 变量使用${}进行取值,但是在if控制语句中,是直接使用变量名的
if(HELLO) 是正确的
if(${HELLO}) 是不正确的
4.重要的指令
-
cmake_minimum_required:指定cmake的最小版本支持,一般作为第一条cmake指令
# CMake设置最小支持版本为 2.8 cmake_minimum_required(VERSION 2.8)
-
project:定义工程的名称,并可以指定工程支持的语言
# 指定工程的名称为HELLOWORLD project(HELLOWORLD CXX) # 表示工程名为HELLOWORLD 使用的语言为C++
-
set:显式定义变量
# 定义变量 SRC 其值为 sayhello.cpp hello.cpp set (SRC sayhello.cpp hello.cpp)
-
add_executable:通过依赖生成可执行程序
# 编译main.cpp 生成main的可执行程序 add_executable(main main.cpp)
-
include_directories:向工程添加多个特定的头文件搜索路径,类似于g++编译指令中的 -I
# 将/usr/lib/mylibfolder 和 ./include添加到工程路径中 include_directories(/usr/lib/mylibfolder ./include)
-
link_directories:向工程中添加多个特定的库文件搜索路径,类似于g++编译指令中的 -L
# 将/usr/lib/mylibfolder 和 ./lib添加到库文件搜索路径中 link_directories(/usr/lib/mylibfolder ./lib)
-
add_library:生成库文件(包括动态库和静态库)
# 通过SRC变量中的文件,生成库hello add_library(hello SHARED ${SRC}) #该语句生成的是动态库 add_library(hello STATIC ${SRC}) #该语句生成的是静态库
-
add_compile_options:添加编译参数
# 添加编译参数 -Wall -std=c++11 add_compile_options(-Wall -std=c++11)
-
target_link_libraries:为target添加需要链接的共享库
# 将hello 动态库文件链接到可执行程序main中 target_link_libraries(main hello)
5.CMake常用变量
-
CMAKE_C_FLAGS:gcc编译选项的值
-
CMAKE_CXX_FLAGS:g++编译选项的值
# 在CMAKE_CXX_FLAGS编译选项后追加 -std=c++11 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
-
CMAKE_BUILD_TYPE:(Debug、Release)
# 设定编译类型为Debug,调试时需要选择该模式 set(CMAKE_BUILD_TYPE Debug)# 设定编译类型为Release,发布时需要选择该模式 set(CMAKE_BUILD_TYPE Release)
6.CMake编译工程
CMake目录结构:项目主目录中会放一个CMakeLists.txt的文本文档,后期使用cmake指令时,依赖的就是该文档
- 包含源文件的子文件夹中包含CMakeLists.txt时,主目录的CMakeList.txt要通过add_subdirectory添加子目录
- 包含源文件的子文件夹中不包含CMakeLists.txt文件时,子目录编译规则,体现在主目录中的CMakeLists.txt中
6.1 两种构建方式
-
内部构建:不推荐使用
内部构建会在主目录下,产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起时,会显得比较杂乱无章
## 内部构建# 在当前目录下,编译主目录中的CMakeLists.txt文件生成Makefile文件 cmake . # .表示当前路径 # 执行make命令,生成目标文件 make
-
外部构建:推荐使用
将编译输出的文件,与源文件放在不同的目录下,进行编译,此时,编译生成的中间文件不会跟工程源文件混淆
## 外部构建# 1、在当前目录下,创建一个build文件,用于存储生成中间文件 mkdir build# 2、进入build文件夹 cd build# 3、编译上一级目录中的CMakeLists.txt,生成Makefile文件以及其他文件 camke .. # ..表示上一级目录# 4、执行make命令,生成可执行程序 make
7.CMake代码实战
7.1 同一目录下的文件进行编译
-
源文件
#include <myhead.h> int main(int argc, const char *argv[]) { std::cout << "Hello, World!" << std::endl; //输出一个hello world语句 return 0; }
-
CMakeLists.txt文件
# 设置最小版本支持 cmake_minimum_required(VERSION 2.8) # 项目名称 project(HELLO) # 生成可执行程序,依赖于hello.cpp生成hello可执行程序 add_executable(hello hello.cpp)
-
内部构建
- 外部构建
7.2 分文件编译
1、头文件
#ifndef SWAP_H
#define SWAP_H
#include <iostream>
using namespace std;//声明一个交换类
class My_swap
{
private:int a;int b; //两个成员变量
public://定义构造函数My_swap(int a, int b){this->a = a;this->b = b;}//声明交换函数void run();//声明打印函数void printInfo();};#endif
2、源文件
#include"swap.h"
//对交换函数的定义
void My_swap::run()
{ int temp = a; a = b; b = temp;
}
//对打印函数的定义
void My_swap::printInfo()
{ cout << "a = " << a<< endl; cout << "b = " << b<< endl;
}
3、测试文件
#include "swap.h"
int main(int argc, const char *argv[])
{ //声明一个交换类对象,调用有参构造完成 My_swap my_swap(520, 1314); //输出交换前的结果 my_swap.printInfo(); //520 1314 cout << "**************************" << endl; //调用交换函数 my_swap.run(); //输出交换后的结果 my_swap.printInfo(); return 0;
}
4、分文件编译使用g++编译器生成可执行程序
5、创建工程管理文件 CMakeLists.txt
# 指定最小编译版本
cmake_minimum_required(VERSION 2.8)
# 执行工程名称
project(SWAP)
# 指定头文件路径 就是 g++ 编译器的 -I 选项
include_directories(include)
# 生成可执行程序
add_executable(swap_cmake main.cpp src/swap.cpp)