前言
本文记录一下关于一个简单的C/C++工程如何编译生成可执行程序文件的流程步骤。
与Python的执行方式不同,Python只需要配置好环境,直接编写代码,在命令行执行python sample.py即可。C/C++需要配置好GCC环境,然后编写代码,再使用gcc/make/bazel这些编译器编译出可执行文件,再运行可执行文件。
对于单文件这类简单的C/C++代码编译相对简单,一条命令即可,要是工程很复杂,则需要编写CMakeList文件,使用cmake编译,并且其中包含很多头文件、库文件、其他项目的动态链接库等,本文根据网络搜索到的资料对这些概念做些笔记。
环境:
Linux,配置好g++、cmake
一、相关概念
- 头文件与库文件的区别
C或者C++代码的编译过程可以分为4步:预处理(需要头文件)->编译->汇编->链接(需要库文件),在程序执行时可能还有动态链接的过程。
头文件的主要作用是提供声明,如我们平常编写的h文件就属于头文件,在编译的时候,在预处理阶段利用头文件的声明就足够了。
库文件主要提供定义/实现。
在链接的时候,把已经编译好的目标文件(常见的如.o文件,在编译过程的第三阶段汇编完以后产生)和现有的库文件(常见的如.lib和.a静态库文件,.dll和.so动态库文件)进行链接,生成可执行文件。
库文件是二进制的,在库文件中是看不到原始的源代码的。库和可执行文件的区别是,库不是独立程序,他们是向其他程序提供服务的代码。 当然使用库文件的好处不仅仅是对源代码进行保密,使用库文件还可以减少重复编译的时间,增强程序的模块化。
将库文件链接到程序中,有两种方式,一种是静态链接库,另一种是动态链接库。
静态库和动态库文件
静态库文件:编译的时候二进制库文件直接加载到内存(可以简单理解为把代码段直接插入到程序中) 。
常见的.lib文件(windows)和.a文件(linux)是静态库文件
动态库文件:编译的时候,只是产生一些调用动态库文件代码的导入表,在执行到需要的地方时再加载到内存,不需要的时候再释放这片内存。
常见的.dll文件(windows)和.so文件(linux)是动态库文件
静态库由于在编译的时候直接将库文件加载到内存,在执行的时候就不需要加载,因而其执行速度快,但是耗内存空间。动态库由于在程序运行时才将需要的库文件加载到内存,不需要的时候就释放内存,因而其省内存空间,但是执行速度慢。
- 头文件、库文件搜索顺序
linux系统头文件搜索路径顺序:
- 搜索当前目录
- 如果采用gcc编译器,搜索-I(大写的i)指定的目录,如果采用make编译器,搜索
CMakeLists.txt
中include_directories()
指定的目录 - 搜索环境变量
CPLUS_INCLUDE_PATH
(C程序使用的是C_INCLUDE_PATH
)中指定的目录。(可以在终端输入echo $CPLUS_INCLUDE_PATH
,一般刚开始没有设置的时候为空) - 搜索系统默认的目录(使用
cpp -v
查看)
linux系统库文件搜索路径顺序:
- 编译目标代码时指定的库文件搜索路径,如果是gcc编译器,搜索-L指定的目录(对应link),如果是make编译器,搜索
CMakeLists.txt
中link_directories()
指定的目录 - 环境变量
LD_LIBRARY_PATH
指定的动态库搜索路径 - 配置文件
/etc/ld.so.conf
中指定的动态库搜索路径 - 默认的动态库搜索路径
/lib;/usr/lib;/usr/local/lib
二、编译命令
对于一个普通的项目,项目应该有以下几个部分:
.
├── build (主要用来存放生成的Makefile以及可执行文件)
│ └── hello
├── CMakeLists.txt (cmake编译所依据的规则)
├── include (主要用来存放我们自己编写的头文件)
│ └── hello.h
├── src (主要用来存放我们自己写的c/cpp文件)
│ ├── hello.c
│ └── main.c
- 使用g++命令直接编译可执行文件
# -c只编译
g++ -c src/hello.c -o build/hello -I./include -std=c++11
# 编译加链接
g++ src/main.c build/hello.o -o build/main -I./include -lxxx -std=c++11
gcc -I(大写i) -L -l(小写l)区别
-I:
格式:-Idirectory
作用:指定额外头文件搜索路径
-L:
格式:-Ldirectory
作用:指定额外的动态库文件搜索路径
-l:
格式:-llibrary
作用:链接时搜索指定的动态库库名
gcc编译选项加入了-static表示寻找静态库文件
如果我们在程序中用到的库是C/C++标准库,如标准io库(如printf)和标准模板库,那么我们-L和-l参数都不需要指定。
如果我们用到了非标准库,且该库所在目录在系统默认的库文件目录中,我们可以不用-L指定库文件目录,但必须要用-l指定库名。
(系统默认的库文件目录下有很多库,系统默认只会载入C/C++标准库,非标准库是不会默认载入的,即使它在默认的库目录中,因为都加载的话程序就太大了)如果我们用到了非标准库,且该库所在目录不在系统默认的库文件目录中,我们需要同时使用-L指定库文件目录,用-l指定库名。
- 使用cmake编译
CMakieLists.txt
内容如下:
project(hello)
#使用通配符添加多个源文件名列表赋给SRC_LIST
file(GLOB SRC_LIST "src/*.c*")
#include_directories("include")
#link_directories("lib")
#已包含到LD_LIBRARY_PATH中,这里不需再指定
#link_directories("/usr/local/lib64")
#opencv需要的库
link_libraries(opencv_core opencv_imgcodecs opencv_highgui)
#编译选项
add_compile_options(-std=c++11)
#生成可执行程序文件
add_executable(sample1 ${SRC_LIST})
#生成动态库文件
#add_library(hello SHARED ${SRC_LIST})
使用CMakeLists.txt
编译生成可执行程序文件或库文件:
1、进入build
目录
2、执行 cmake .. && make
CMakeLists常用指令
# 项目名称
project(${project_name})
#添加debug信息,使得生成的可执行文件可以调试
set(CMAKE_BUILD_TYPE DEBUG)
#编译选项
add_compile_options(-std=c++11)
include_directories(dir1 dir2...)
#添加额外的头文件目录 相当于gcc的 -Idir1 dir2...
#如果除了系统默认的头文件目录之外,没有程序额外需要的头文件目录,可以不写
link_directories(dir1 dir2...)
#添加额外的库文件目录 相当于gcc的 -Ldir1 dir2...
#如果除了系统默认的库文件目录之外,没有程序额外需要的库文件目录,可以不写
link_libraries(lib1 lib2...)
#设置所有目标需要链接的库 相当于gcc的 -llib1 lib2...
#如果除了标准库之外,没有其他非标准库,可以不写,反之link_libraries或target_link_libraries二选一
#注意这个命令必须用在add_executable()命令之前
target_link_libraries(targetname lib1 lib2...)
#设置单个目标需要链接的库 相当于gcc的 -llib1 lib2...
#如果除了标准库之外,没有其他非标准库,可以不写,反之link_libraries或target_link_libraries二选一
#注意这个命令必须用在add_executable()命令之后
add_executable(abcd src/abcd.c)
#将abcd.c编译生成可执行文件abcd
add_library(abcd STATIC src/abcd.cpp)
#添加自己编写的库文件,#STATIC表示生成静态库abcd.a文件,SHARED表示生成静态库abcd.so文件
最后
参考文章:
声明
本文仅作为参考文章基础上的总结与个人使用记录,如有侵权,请告知删除。