c++编译

1.1 c++编译

c++脚本程序写完之后,并不能直接运行,需要进行编译,转成.o文件,再链接才能运行,一般包括:预处理,汇编,编译。链接四步,如下:

  • 预编译

    把 .c源文件编译成 .ii 预处理文件

    gcc -E [源文件.c] -o [自定义名.ii]
    
  • 编译成汇编语言

    把 .i 文件编译成 .s 汇编语言文件

    gcc -S [源文件.c] 
    
    • 注意:隐藏了预编译、删除预处理i文件的过程
  • 编译成二进制
    把 .s 编译成二进制.o 文件

    gcc -c [源文件.c] -o [自定义文件名.o] [编译选项]
    
    • 注意:隐藏了。。。
  • 链接成可执行文件
    把 .o 文件,链接成可执行的二进制文件

    gcc [.o] -o [自定义文件名] [链接选项]
    

注意:实际使用中一般就是编译成二进制.o 文件,然后将所有 .o 文件,链接成可执行的二进制文件

1.1.1 单文件或少文件编译

  • 编译:源文件[.c/cpp] -> Object文件[.o]

    g++ -c [.c/cpp][.c/cpp]... -o [.o][.o]... -I[.h/hpp]
    # g++是编译命令 -c,-o,-I是选项 -c接源脚本文件 -o接目标文件 -I接头文件(-c c++ /-o object/ -I include)
    
  • 链接:Object文件[.o]->可执行文件

    gcc [.o] -o out.exe -l[库名称] -L[库路径]
    

    总结下:
    编译的时候使用-I命令可以装入include搜索路径。
    连接的时候使用-l -L命令可以装入连接搜索路径或文件

1.1.2多文件编译(使用Makefile 和 CMake)

cmake比Makefile高级,但是两者的功能都是快速地进行批量的编译(因为当你有很多的c++源文件的时候,一个一个地去用g++去编译是很麻烦的)

1.1.3 Makefile教程

参考: https://zhuanlan.zhihu.com/p/396448133

GNU Make官方网站:https://www.gnu.org/software/make/

GNU Make官方文档下载地址:https://www.gnu.org/software/make/manual/

1. 基本格式

targets : prerequisties

  command
  • target:目标文件,可以是OjectFile,也可以是执行文件,还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。

  • prerequisite:要生成那个target所需要的文件或是目标。

  • command:是make需要执行的命令,

2. Makefile规则

(1)make会在当前目录下找到一个名字叫“Makefile”或“makefile”的文件。

(2)如果找到,它会找文件中第一个目标文件(target),并把这个文件作为最终的目标文件。

(3)如果target文件不存在,或是target文件依赖的.o文件(prerequities)的文件修改时间要比target这个文件新,就会执行后面所定义的命令command来生成target这个文件

(4)如果target依赖的.o文件(prerequisties)也存在,make会在当前文件中找到target为.o文件的依赖性,如果找到,再根据那个规则生成.o文件。

3 Makefile的变量

变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,并用小括号“()”把变量给包括起来。

3.1 变量的定义和使用

  • 定义
# 头文件路径
include_paths := /usr/local/cuda-10.1/include \
         /datav/lean/opencv4.5.1/include/opencv4 \
         /datav/lean/tensorRT6.0.1.5_cuda10.1_cudnn7.6.0.3/include/ \
         src \
         src/tensorRT \
         src/tensorRT/common \
  • 使用
include_paths := $(foreach item,$(include_path),-I(item))

3.2 Makefile常用的预定义变量

  • $@  目标(target)的完整名称。

  • $<  第一个依赖文件(prerequisties)的名称。

  • $^  所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件。

4 Makefile的常用运算符

4.1赋值:=

  用于变量的定义、赋值

# 链接库名称
link_library := cudart opencv_core opencv_imgcodecs opencv_imgproc \
         gomp nvinfer protobuf cudnn pthread \
         cublas nvcaffe_parser nvinfer_plugin python3.8

4.2 累加+=

# 编译选项设置
cpp_compile_flags := -m64 -fPIC -g -O0 -std=c++11 -w -fopenmp
cpp_compile_flags += $(include_paths)

5 Makefile的常用函数

函数调用,很像变量的使用,也是以“$”来标识的,其语法如下:

$(<function> <arguments>)
  • <function>就是函数名,make 支持的函数不多。

  • <arguments>是函数的参数,参数间以逗号“,”分隔,而函数名和参数之间以“空格”分隔。

5.1 shell

$(shell <command> <arguments>)
  • 名称:shell命令函数——subst。

  • 功能:调用shell命令command

  • 返回:函数返回shell命令command的执行结果

  • 示例:

cpp_srcs := $(shell find src -name "*.cpp") 

5.2 patsubst

$(patsubst <pattern>,<replacement>,<text>)
  • 名称:模式字符串替换函数——patsubst。

  • 功能:查看中的单词是否符合模式,如果匹配的话,则以替换。这里,可以包括通配符“%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个“%”将是中的那个“%”所代表的字串。

  • 返回:函数返回被替换过后的字符串。

  • 示例:

cpp_srcs := $(shell find src -name "*.cpp") #shell指令,src文件夹下找到.cpp文件
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs)) #cpp_srcs变量下cpp文件替换成 .o文件

5.3 subst

$(subst <from>,<to>,<text>)
  • 名称:字符串替换函数——subst。

  • 功能:把字串中的字符串替换成

  • 返回:函数返回被替换过后的字符串。

  • 示例:

cpp_srcs := $(shell find src -name "*.cpp") #shell指令,src文件夹下找到.cpp文件
cpp_objs := $(patsubst %.cpp,%.o,$(cpp_srcs)) #cpp_srcs变量下cpp文件替换成 .o文件
cpp_objs := $(subst src/,objs/,$(cpp_objs)) #cpp_objs 变量下替换后的 .o文件 从src文件夹移植到objs文件夹

5.4 foreach

$(foreach <var>,<list>,<text>)
  • 名称:循环函数——subst。

  • 功能:把字串<list>中的元素逐一取出来,执行<text>包含的表达式

  • 返回:<text>所返回的每个字符串所组成的整个字符串(以空格分隔)

  • 示例:

library_paths := /datav/shared/100_du/03.08/lean/protobuf-3.11.4/lib \
        /usr/local/cuda-10.1/lib64 \
library_paths := $(foreach item,$(library_paths),-L$(item))

5.5 dir

$(dir <names...>)
  • 名称:取目录函数——dir。

  • 功能:从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。

  • 返回:返回文件名序列的目录部分。

  • 示例:

$(dir src/foo.c hacks)   # 返回值是“src/ ./”。

6 伪目标

“伪目标”不是一个文件,只是一个标签,。我们要显示地指明这个“目标”才能让其生效。“伪目标”的取名不能和文件名重名,不然其就失去了“伪目标”的意义了。

为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪目标”。

.PHONY : clean
  • 只要有这个声明,不管是否有“clean”文件,要运行“clean”这个目标,只有“make clean”。

7 编译过程

7.1 总览

源文件[.c/cpp] -> 预处理成[.i/.ii] -> 编译成[.s] -> 汇编成[.o] -> 链接成可执行文件

7.2 包含静态库的编译过程

### 步骤:

(1)源文件[.c/cpp] -> Object文件[.o]

g++ -c [.c/cpp][.c/cpp]... -o [.o][.o]... -I[.h/hpp] -g

(2)Object文件[.o] -> 静态库文件[lib库名.a]

ar -r [lib库名.a] [.o][.o]...      # 编译静态库用ar

(3)main文件[.c/cpp] -> Object文件[.o]

g++ -c [main.c/cpp] -o [.o] -I[.h/hpp] -g

(4)链接 main的Object文件 与 静态库文件[lib库名.a]

g++ [main.o] -o [可执行文件] -l[库名] -L[库路径]

7.3 包含动态库(共享库)的编译过程

### 步骤:

源文件[.c/cpp] -> Object文件[.o]

g++ -c [.c/cpp][.c/cpp]... -o [.o][.o]... -I[.h/hpp] -g -fpic

Object文件[.o] -> 动态库文件[lib库名.so]

g++ -shared [.o][.o]... -o [lib库名.so]    # 编译动态库用g++ -shared

main文件[.c/cpp] -> Object文件[.o]

g++ -c [main.c/cpp] -o [.o] -I[.h/hpp] -g

链接 main的Object文件 与 动态库文件[lib库名.so]

g++ [main.o] -o [可执行文件] -l[库名] -L[库路径] -Wl,-rpath=[库路径]

注意: 编译静态库用ar; 编译动态库用g++ -shared

1.2 include

#include<> 和 #include ""的区别

  • 对于#include <filename.h>,编译器先从标准库路径开始搜索filename.h,使得系统文件调用比较快

  • 对于#include "filename.h",编译器先从用户的工作路径开始搜索filename.h,后去寻找系统路径,使得自定义文件较快。
    所以在写代码的过程中要根据实际情况选择是<>还是"",

    一般标准库文件用include <> , 自定义文件 include ""

1.3 指针定义 与 使用

  • 定义的时候* 和 & 指的是指针变量和引用

    • 如下:
      int* a = nullptr;  // 指的是指向 int的指针类型
      void func(int* a); // 形参时,你传入的得是 地址/指针
      void func(int& a); // 形参时,你传入的得是 引用
      
  • 使用的时候* 和 & 指的是取地址上的值 和 取变量的地址

    • 如下:
      int value = *a;   // 此时a是个指针变量,取该地址的变量值
      int* ptr = &b;    // 此时b是个非指针变量,取该变量的地址
      func(*a);         
      func(&b); 
      

linux系统中c++ opencv编译安装: https://zhuanlan.zhihu.com/p/392751819
GCC 指令详解及动态库、静态库的使用: https://www.cnblogs.com/hyacinthLJP/p/16839524.html

posted @ 2023-09-02 16:50  silence_cho  阅读(75)  评论(0编辑  收藏  举报