在Linux中开发C++
本文档为本人在学习慕课网课程——[重学C++ ,重构你的C++知识体系]时的一些记录与思考,侵删。学习课程请支持正版!
1. 搭建C/C++编译情况
1.1 gcc 和 g++ 的区别
本质上没有太大区别,gcc 默认使用 c 编译器,g++ 默认使用C++ 编译器:
- 假如是 .c 文件,gcc 会使用 c 编译器来编译,但 g++ 会使用 c++ 编译器;
- 但假如是 .cpp 文件,两者是一样的。
1.2 一个C/C++ 小例子
详细代码如下:
- #include<iostream>
- using namespace std;
- int main(int argc,char** argv){
- cout<<"Hello Ubnutu"<<endl;
- return 0;
- }
复制代码 可通过以下指令编译:
g++ helloworld.cc -o helloworld
即可生成名为 helloworld 的可执行步伐!其中 -o 表现我们指定生成的 可执行文件名称!
但假如我们必要用 gcc 来编译,其会报如下错误:
- /usr/bin/ld: /tmp/ccO1YpSf.o: in function `main':
- hello.cc:(.text+0x1d): undefined reference to `std::cout'
- /usr/bin/ld: hello.cc:(.text+0x22): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
- /usr/bin/ld: hello.cc:(.text+0x2c): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
- /usr/bin/ld: hello.cc:(.text+0x37): undefined reference to `std::ostream::operator<<(std::ostream& (*)(std::ostream&))'
- /usr/bin/ld: /tmp/ccO1YpSf.o: in function `__static_initialization_and_destruction_0(int, int)':
- hello.cc:(.text+0x6b): undefined reference to `std::ios_base::Init::Init()'
- /usr/bin/ld: hello.cc:(.text+0x80): undefined reference to `std::ios_base::Init::~Init()'
- collect2: error: ld returned 1 exit status
复制代码 很显然,它没有连接到 std 库,以是例如 std::cout 等指令都没有找到。我们可以改为用如下指令编译:
gcc hello.cc -o helloworld -lstdc++
我们发现没有题目了,这是因为 gcc 可以举行 C++ 文件的预处置惩罚,编译,汇编,但不会主动连接 iostream等 C++ 库,而假如我们手动指定必要连接 -lstdc++,它就会去主动连接该库!
参考文档:[C++ OpenCV常见链接error及办理方案,/usr/bin/ld:][https://blog.csdn.net/qq_45983373/article/details/136361499] 中有一幅挺好的图,呈现了C++编译的过程:
除此之外,我们还可以尝试让他显示告诫信息,例如对于一下代码:
- #include<iostream>
- using namespace std;
- int main(int argc,char** argv){
- cout<<"Hello Ubnutu"<<endl;
- int a=0;
- return 0;
- }
复制代码 假如我们使用以下指令举行编译:
gcc hello.cc -o helloworld -lstdc++ -Wall
我们会发现它就会有如下提示信息:
- hello.cc: In function ‘int main(int, char**)’:
- hello.cc:5:6: warning: unused variable ‘a’ [-Wunused-variable]
- 5 | int a=0;
- | ^
复制代码 也就是说,我们使用 -Wall 选项就可以让他输出一些可以优化的提示信息。
1.3 更多编译指令
除此之外,还有如下选项:
- -ansi : 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色,例如 asm 或 typeof 关键字。
- -S : 知己或预处置惩罚和编译,就是把文件编译成为汇编代码(.s 文件)
- -c : 质变仪并生成目标文件,即把文件编译为二进制文件(.o 文件)
- -g : 生成调试信息,GNU 调试器可以使用该信息。
- -o FILE : 生成指定的输出文件,用在生成可执行文件时。
- -O0 : 不举行优化处置惩罚
- -O 或 -O1: 优化生成代码
- -O2 或 -O3: 进一步优化
- -shared : 生成共享目标文件。通常用在建立共享库时。
- -static : 禁止使用共享链接。
- -w : 不生成任何告诫信息
- -Wall : 生成所有告诫信息
- -IDIRECTORY : 指定额外的头文件搜索路径 DICTIONARY
- -LDIRECTORY : 指定额外的函数库搜索路径 DICTIONARY
- -ILIBRARY : 连接时搜索指定的函数库 LIBRARY
- -m486 : 针对 486 举行代码优化。
- -E 只运行 C 预编译器,即把头文件展开等,生成预编译文件(.i 文件)
最终编译过程描述为图:
2. Makefile 文件的编写
2.1 Makefile 简单介绍
makefile 主要在工程实践中帮助我们完成C++工程配置题目。make 字面意思就是制作文件,制作一个当前平台可以运行的文件!像我们之前使用 g++ / gcc 生成可执行文件就是一个简单的 makefile 的过程。
可执行步伐产生过程:
- 配置情况(体系情况)
- 确定标准库和头文件位置
- 确定依赖关系(源代码之间编译的依赖关系)
- 头文件预编译
- 预处置惩罚
- 编译
- 链接
- 安装
- 和操作体系建立接洽
- 生成安装包
在大型工程中,有许多头文件和 .cpp 文件,它们在编译的时间会存在以来关系!同时许多头文件会被许多文件 include,我们希望只被编译一次!汇编可转化为呆板代码,然后通过链接引入目标库等。
当依赖关系复杂的时间,make 命令工具诞生了,而 Makefile 文件正式为 make 工具所使用的。Makefile 描述了整个工程所有文件的编译顺序、编译规则!属于可执行步伐生成过程中很紧张的部分,帮助我们编译器更好的生成可执行代码。
2.2 多个文件编译简单示例
写一个例子,在reply.h 中定义 Reply 类:
- #include<iostream>
- class Reply{
- public:
- Reply();
- ~Reply();
- void printHello();
- };
复制代码 然后再 reply.cc 中详细定义这三个方法:
- #include"reply.h"
- using namespace std;
- Reply::Reply(){}
- Reply::~Reply(){}
- void Reply::printHello(){
- cout<<"Helloworld!"<<endl;
- }
复制代码 最后我们使用 main 函数调用 Reply:
- #include"reply.h"
- int main(){
- Reply reply;
- reply.printHello();
- return 0;
- }
复制代码 假如我们按照老方法来编译:
- 使用 gcc main.cc -o main
报错如下:
- /usr/bin/ld: /tmp/cc4EXok8.o: in function `main':
- main.cc:(.text+0x24): undefined reference to `Reply::Reply()'
- /usr/bin/ld: main.cc:(.text+0x30): undefined reference to `Reply::printHello()'
- /usr/bin/ld: main.cc:(.text+0x41): undefined reference to `Reply::~Reply()'
- /usr/bin/ld: main.cc:(.text+0x67): undefined reference to `Reply::~Reply()'
- /usr/bin/ld: /tmp/cc4EXok8.o: in function `__static_initialization_and_destruction_0(int, int)':
- main.cc:(.text+0xab): undefined reference to `std::ios_base::Init::Init()'
- /usr/bin/ld: main.cc:(.text+0xc0): undefined reference to `std::ios_base::Init::~Init()'
- /usr/bin/ld: /tmp/cc4EXok8.o:(.data.rel.local.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
- collect2: error: ld returned 1 exit status
复制代码 - 使用 gcc mian.cc -o mian -lstdc++
报错如下:
- /usr/bin/ld: /tmp/cchzdnO3.o: in function `main':
- main.cc:(.text+0x24): undefined reference to `Reply::Reply()'
- /usr/bin/ld: main.cc:(.text+0x30): undefined reference to `Reply::printHello()'
- /usr/bin/ld: main.cc:(.text+0x41): undefined reference to `Reply::~Reply()'
- /usr/bin/ld: main.cc:(.text+0x67): undefined reference to `Reply::~Reply()'
- collect2: error: ld returned 1 exit status
复制代码 我们发现,按照原来的方式来编译是不能乐成的!但假如我们使用这一句话编译:
- 使用gcc reply.cc main.cc -o main -lstdc++
是可以乐成编译的!调用 main 也可以乐成输出效果!
那我们尝试使用 make 命令来生成一次!内容如下:
- main: reply.o main.o //main 这个文件依赖于 reply.o 和 main.o 两个文件
- gcc reply.o main.o -o main -lstdc++ //生成 main 的指令
- reply.o: reply.cc //上一行是对下一行有依赖关系的
- gcc -c reply.cc -o reply.o -lstdc++
- main.o: main.cc
- gcc -c mian.cc -o main.o -lstdc++
复制代码 然后我们直接执行一个 make 指令,然后就可以一样的生成 main 可执行文件。
2.3 make 和 Makefile 的调用
make究竟是什么?
make 是操作体系中的一个批处置惩罚工具,它可以帮我们把许多命令融合在一起,一次性把这些命令执行下去,即可以一次性完成许多命令。
可以这样一个比喻,make 是一个指挥家,而 Makefile 则是乐谱,指挥着所有工具完成任务,但为什么 windows 不消那么麻烦呢?
因为 Linux 是相对开放的体系,许多东西必要自己来搭积木,但 windows 是贸易化的,它的用户性是做的比力好的,在 IDE 中许多 maker 的事都帮我们做完了,因此我们必要相识更多细节。
当然在Linux 中,也有其他 IDE 工具,例如 CMake,它就是帮助我们编写 Makefile 的!CMake 我们只必要编写一个 CMakeList.txt,它内部就会帮助我们转换为 Makefile 文件!
还有 QT 中的 QMake,也是实现类似的功能!
2.4 Makefile 的格式
基本语法原则:
- target: prerequisites ...
- command ...
复制代码 留意每个命令行前面必须是一个 Tab 字符,即命令行第一个字符是 Tab。
简化规则:
- 变量定义: var=string
- 变量使用: $(var)
复制代码 此时 Makefile 可以写为:
- TARGET = main
- OBJS = reply.o main.o
- $(TARGET):$(OBJS)
- gcc $(OBJS) -o $(TARGET) -lstdc++
- reply.o: reply.cc
- main.o: main.cc
复制代码 此时,生成指令如下:
- g++ -c -o reply.o reply.cc
- g++ -c -o main.o main.cc
- gcc reply.o main.o -o main -lstdc++
复制代码 显然,生成 main.o 和 reply.o 的生成都是主动生成的,调用的是 g++。
假如我们希望若已存在则删除?则使用如下指令:
- TARGET = main
- OBJS = reply.o main.o
- $(TARGET):$(OBJS)
- gcc $(OBJS) -o $(TARGET) -lstdc++
- reply.o: reply.cc
- main.o: main.cc
- clean: rm $(TARGET) $(OBJS)
复制代码 此时,我们可以调用 make clean,即可删除掉这些文件:
但值得留意的是,这里的 clean 现实上是我们生成的 Target,只不外我们生成的指令是 rm 指令而已;我们可以再优化一下:
- TARGET = main
- OBJS = reply.o main.o
- .PHONY: clean
- $(TARGET):$(OBJS)
- gcc $(OBJS) -o $(TARGET) -lstdc++
- reply.o: reply.cc
- main.o: main.cc
- clean:
- rm $(TARGET) $(OBJS)
复制代码 这里的 .PHONY 是一个关键字,代表这个文件并不是实体存在的,因此不会受到已存在 clean 文件的影响!
2.5 用 Makefile 实现步伐安装卸载
如何安装卸载?
- TARGET = main
- OBJS = reply.o main.o
- .PHONY: clean
- $(TARGET):$(OBJS)
- gcc $(OBJS) -o $(TARGET) -lstdc++
- reply.o: reply.cc
- main.o: main.cc
- clean:
- rm $(TARGET) $(OBJS)
- install:
- cp ./main /usr/local/bin/mainTest
- uninstall:
- rm /usr/local/bin/mainTest
复制代码 这样,我们就可以通过 make install 把我们的步伐安装到体系中了,可以在恣意地方通过 mainTest 指令调用!而同样的,可以通过 make uninstall 来卸载。
留意,我们现实上就是把当前可执行文件放到了当前 $PATH 情况变量中,以是我们直接在控制台输入即可访问,当前我的呆板的情况变量:
- /home/xuzhenge/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
复制代码 在大型工程中,大概还必要依赖一些第三方库,还必要把一些信息写到注册表中!
2.6 Makefile 的变量题目
在Makefile 中,有以下几种变量:
- 用户自定义变量:在 Makefile 中,自定义变量很想C++中的宏,但会有一些特别的符号,表现一些特别的含义(后续会提到);且留意,大小写是敏感的!
- 变量中的变量:
在 Makefile 中,支持如下定义方式:
- foo = $(bar)
- bar = $(ugh)
- ugh = Huh
复制代码 也就是我们可以把变量的真实值往后放,把真实的变量延迟到后面,会非常灵活,此时的 bar 就是变量中的变量!
当我们使用以下句子时:
- test1:
- echo $(foo), foo
- test2:
- echo $(bar), bar
复制代码 当我们调用 make test1 时,输出语句为:
也就是前面会输出 foo 变量的最终值,后面会将 foo 作为字符串处置惩罚,输出 foo 字符。
这种方法好处是很灵活,但坏处时会让变量的编写过程变得非常繁杂。
我们看另一段代码:
- foo = $(bar)
- bar = $(ugh)
- ugh = Huh
- test1: echo $(foo), footest2: echo $(bar), bary:= $(x)barz=$(x)barx:=footest3: echo $(x),x echo $(y),y echo $(z),z
复制代码 在这一段代码中,最终输出为:
在这里我们可以看到,这里 y 通过 := 赋值,但没有获取到 x 的真实值,这里的 y其实是一个空值;但 z 通过 = 赋值,这里就获取到了 x 的目标值。
这里 := 现实上就是制止依赖后面定义的变量的赋值方法!
再进一步:
- foo = $(bar)
- bar = $(ugh)
- ugh = Huh
- test1: echo $(foo), footest2: echo $(bar), bary:= $(x)barz=$(x)barx:=foox+=xzgtest3: echo $(x),x echo $(y),y echo $(z),z
复制代码 其输出为:
- foo xzg,x
- bar,y
- foo xzgbar,z
复制代码 可以看出来,x 在 foo 的基础上还追加了 xzg 字符!
- 多行变量:
以下代码:
- foo = $(bar)
- bar = $(ugh)
- ugh = Huh
- test1: echo $(foo), footest2: echo $(bar), bary:= $(x)barz=$(x)barx:=foox+=xzgtest3: echo $(x),x echo $(y),y echo $(z),zdefine two-linesfooecho $(bar)endeftest4: echo $(two-lines)
复制代码 其中执行 make test4 效果如下:
即对于一个变量会同时打出两行,第一行即字符 foo,第二行即变量 bar 的最终值,即 Huh。这就是多行变量。
- 情况变量
类似于 PATH 这种变量,例如常见的情况变量 $PATH,$LANG 等
C++ 的情况路径为 $LD_LIBRARY_PATH,即动态和静态库的搜索路径!
对情况变量赋值可以通过 export 赋值!调用情况变量:
- testEnv:
- echo $(HOME),$(SHELL),$(LD_LIBRARY_PATH)
复制代码 输出效果为:
同样的,编译器也有自己的情况变量!
- testMakefileEnv:
- echo $(CXX), $(RM)
复制代码 其输出效果如下:
2.7 实现共享库
在 windows 平台中,共享库一样平常是 .lib 或 .dll 文件;在 Linux 平台中,共享库一样平常是 .so 文件。这里,我们就尝试把刚刚写的 reply.o 文件做成一个共享库 reply.so!
代码如下:
- TARGET = main
- OBJS = reply.o
- LIB = libreply.so
- CXXFLAGS = -c -fPIC
- .PHONY: clean install uninstall
- $(TARGET):$(LIB) main.o
- $(CXX) main.o -o $(TARGET) -L. -lreply
- $(LIB):$(OBJS)
- $(CXX) -shared $(OBJS) -o $(LIB)
- reply.o: reply.cc
- $(CXX) $(CXXFLAGS) reply.cc -o $(OBJS)
- main.o: main.cc
- $(CXX) $(CXXFLAGS) main.cc -o main.o
- clean:
- rm $(TARGET) $(OBJS)
- install:
- cp ./main /usr/local/bin/mainTest
- uninstall:
- rm /usr/local/bin/mainTest
复制代码 首先,我们定义必要生成的文件为 LIB = libreply.so,即一个动态链接库。同时我们必要使用一个 CXXFLAGS 变量来保存 C++ 的编译指令。这里包括两个编译选项,生成目标文件 (-c) 和 生成共享动态链接库 (-fPIC)。
动态链接库可以只生成一份备份,多份代码可共享一份代码!这一份二进制步伐可以被其他二进制步伐所共享!
这里生成过程其实还挺复杂的,其中生成 main 的语句为:
- g++ main.o -o main -L. -lreply
复制代码 其中这里的 -L. 表现从当地探求动态链接库,-lreply 表现探求名为 libreply.so 的动态链接库。
生成 main.o 的语句为:
- g++ -c -fPIC main.cc -o main.o
复制代码 -fPIC选项应用于静态库编译时,编译器会生成额外的位置无关代码和数据结构,以确保生成的静态库可以在差别的内存地址上加载。
生成 libreply.so 的语句如下:
- g++ -shared reply.o -o libreply.so
复制代码 这里 -shared 表现生成动态链接库,它是通过 .o 文件生成的!
生成 reply.o 文件的语句如下:
- g++ -c -fPIC reply.cc -o reply.o
复制代码 这个语句就没啥好说的了。
但假如直接这样生成,运行 ./main 会出现以下报错:
- ./main: error while loading shared libraries: libreply.so: cannot open shared object file: No such file or directory
复制代码 即我们无法打开共享链接库!说明还有一些参数有题目!我们继承修改生成 TARGET 的指令为:
- g++ main.o -o main -L. -lreply
- -Wl,-rpath ./
复制代码 这里的 -Wl 表现编译器将后面的参数传递个连接器 ld;-rpath 选项添加了一个链接库的定位路径,在运行连接时,会优先搜索 -rpath 的路径,再去搜索 LD_RUN_PATH 的路径。
假如不加 -fPIC,就会有如下报错:
- /usr/bin/ld: reply.o: relocation R_X86_64_PC32 against symbol `_ZSt4cout@@GLIBCXX_3.4' can not be used when making a shared object; recompile with -fPIC
- /usr/bin/ld: final link failed: bad value
复制代码 其中可以顺利产生 reply.o,但不能产生动态链接库 libreply.so,说明这个 -fPIC 现实上是维持 .o 到 .so 之间的稳固的!
2.8 继承讲变量
- 主动变量(目标变量):
把前面的语句写作如下形式:
- $(LIB):$(OBJS)
- $(CXX) -shared $^ -o $@
复制代码 原本为:
- $(LIB):$(OBJS)
- $(CXX) -shared $(OBJS) -o $(LIB)
复制代码 这两个变量 $^ 和 $@ 并不是原本就有的,而是依据某些规则生成的变量!公式如下:
因此这里的 $^ 显着就是 $(OBJS) ,而 $@ 就是 $(LIB) 。留意,这里这两个变量是局部变量,只有在当前规则中有用。因此当变量名冲突的时间则会优先以局部变量为主!
最后,我们修改完毕的 Makefile 文件长这样:
- TARGET = main
- OBJS = reply.o
- LIB = libreply.so
- CXXFLAGS = -c -fPIC
- LDFLAGS = -L. -lreply -Wl,-rpath $(@D)
- .PHONY: clean install uninstall
- $(TARGET):main.o $(LIB)
- $(CXX) $< -o $(TARGET) $(LDFLAGS)
- $(LIB):$(OBJS)
- $(CXX) -shared $^ -o $@
- reply.o: reply.cc
- $(CXX) $(CXXFLAGS) $< -o $@
- main.o: main.cc
- $(CXX) $(CXXFLAGS) $< -o $@
- clean:
- rm $(TARGET) $(OBJS) $(LIB) main.o
- install:
- cp ./main /usr/local/bin/mainTest
- uninstall:
- rm /usr/local/bin/mainTest
复制代码 - 模式变量
不消再依赖项中把每个文件都清晰写出来,可以通过扩展名的方式找到恣意字符为文件名这样的文件,因此有了通配符 %。
最终简化下来的代码如下:
- TARGET = main
- OBJS = reply.o
- TESTOBJ = main.o
- LIB = libreply.so
- CXXFLAGS = -c -fPIC
- LDFLAGS = -L. -lreply -Wl,-rpath $(@D)
- .PHONY: clean install uninstall
- $(TARGET):$(TESTOBJ) $(LIB)
- $(CXX) $< -o $(TARGET) $(LDFLAGS)
- $(LIB):$(OBJS)
- $(CXX) -shared $^ -o $@
- %.o:%.cc
- $(CXX) $(CXXFLAGS) $< -o $@
- clean:
- $(RM) $(TARGET) $(OBJS) $(LIB) $(TESTOBJ)
- install:
- cp ./main /usr/local/bin/mainTest
- uninstall:
- rm /usr/local/bin/mainTest
复制代码 这里就是把 .o 和 .cc 的关系通过一句话都编完了!
3. Makefile 主动生成与部署
在项目中,我们一样平常有如下文件夹
- src: 源代码,源步伐
- include: 头文件
- build: 生成临时文件
- test: 做测试用例
- example: 一些 demo
- Lib: 用于保存第三方库的文件夹
- bin: 可运行文件
- ……
但假如是大型项目,直接写 Makefile 文件很累,因此我们有主动化工具:
- automake/autoconfig
- CMake
这里就演示一下 CMake 的用法!
先下载安装包,然后用 tar -zxvf 举行解压缩!
然后在解压缩后的文件夹中执行 ./bootstrap 命令即可安装。
然后我们就可以编写 CMakeList.txt,这个是有固定模板的!
- #CMakeLists.txt
- # 设置 cmake 最低版本
- cmake_minimum_required(VERSION 2.8.0)
- # 设置C++标准
- set(CMAKE_CXX_STANDARD 11)
- # 项目名称
- project(cmake_test)
- # 包含的头文件目录
- include_directories(./include)
- set(SRC_DIR ./src)
- # 指定生成链接库
- add_library(XXX ${SRC_DIR}/XXX.cc)
- add_library(YYY ${SRC_DIR}/YYY.cc)
- # 设置变量
- set(LIBRARIES XXX YYY)
- set(OBJECT XXX_test)
- # 生成可执行文件
- add_executable(${OBJECT} ${SRC_DIR}/main.cc)
- # 为可执行文件链接目标库
- target_link_libraries(${OBJECT} ${LIBRARIES})
复制代码 生成的 Makefile 文件如下:
4. 如何调用他人写的库
4.1 通过 -L. 指定库目录编译
我们以 openfec 为例,这是一个由 www.openfec.org 提供的用于方便举行 AL-FEC 编解码的方法,经过 make 后,它会生成一个 libopenfec.so 文件,下面,我们将要尝试假如在其他 .cc 代码中引入该文件的相关方法应该如何编译!
我们编译的目标是 openfec 自带的一个例子,详细在 ~/applis/how_to_examples/simple_client_server 下,包含头文件 simple_client_server.h 和两个代码文件 simple_server.c 、 simple_client.c 。显然,我们必要编译的文件就是后两个代码文件。当前文件结构如下图所示:
当然,我们看到这个目录下其实也是有 CMakeLists.txt 的,说明我们同样可以通过 cmake 的方式生成 Makefile 文件,然后使用 make 举行编译。打开 CMakeLists.txt 就可以看到,它在内里也声明白 openfec 的库文件(target_link_libraries):
然而,在这里我们偏不消 make 的方式来编译,而是直接使用 gcc 来编译,领会一下 C++ 代码编译的过程。显然,我们必要声明的库文件即 libopenfec.so,那么这个库在哪呢?
在 openfec 官网中曾提到,可以使用 make 指令来编译 openfec 的源代码,编译效果将会放到 ~/bin/Debug 文件夹下,这个文件夹的内容如下图所示:
可以看到,这里的 libopenfec.so 就是我们想要的文件。当然除了这个文件亦以外,还有一个 libopenfec.so.1,这个文件现实上是运行时的连接文件,也就是 -Wl,-rpath 指定的目标文件,后续可以通过报错看到他们之间的关系。
首先,我们把 libopenfec.so 文件挪到 openfec 提供的例子目录(~/applis/how_to_examples/ simple_client_server)下,此时该目录内容如下:
此时,调用我们的编译语句:
gcc simple_server.c -o simple_server -L. -lopenfec
可以看到,代码顺利生成了 simple_server 的可执行文件,但是我们现实运行时,它会报如下错误:
显然,它没有找到库 libopenfec.so.1 ,那么我们把该目录也放到当前文件夹下,并通过如下指令举行编译:
gcc simple_server.c -o simple_server -L. -lopenfec -Wl,-rpath ./
此时,生成的可执行文件不再堕落,可以正常运行!
4.2 放到默认库目录文件夹下编译
在前面的例子中,我们通过 -L. 指定了库文件的目录,但这似乎不太符合我们一样平常的使用习惯,一样平常我们指定了 -lopenfec 之后就可以直接编译了,在本小节中,我们进一步尝试直接把 .so 文件放到默认库目录文件夹下编译!
在 C++ 编译中,它会按照以下顺序来找库文件位置:
留意,这里说的仅仅是 编译过程中的库文件,而不包含运行过程中的库文件。在C++中,编译过程中要求的库文件叫静态库(例如上面的 libopenfec.so),而在运行过程中要求的库文件叫动态库(例如上面的 libopenfec.so.1)。在编译时指定库文件,静态库通过 -L 指定,而动态库则通过 -Wl,-rpath 指定;同样的对于情况变量,静态库通过 $LIBRARY_PATH 指定,而动态库则通过 $LD_LIBRARY_PATH 指定!下面,我们希望把我们必要的 openfec.so 和 openfec.so.1 放到桌面的目录 ~/Desktop/openFECLib 中,然后我们通过情况变量指定这个目录作为库目录之一,使我们编译时不再必要额外指定库目录路径!
此时,~/Desktop/openFECLib 的内容如下:
现实上就是把库文件(静态库和动态库)都放到了该目录下,然后就很简单,就向情况变量设置该目录即可:
export LIBRARY_PATH=~/Desktop/openFECLib/
export LD_LIBRARY_PATH=~/Desktop/openFECLib/
必要留意的是,我这里是直接重写了该变量,假如只是希望往内里添加新的路径,则通过如下指令:
export LIBRARY_PATH=~/Desktop/openFECLib/LIBRARY_PATH
export LD_LIBRARY_PATH=~/Desktop/openFECLib/LD_LIBRARY_PATH
此时,我们重新对目标文件举行编译,不外我们执行如下指令:
gcc simple_server.c -o simple_server -lopenfec
很好,没有报错,直接就编译乐成了,而且生成的 simple_server 是可以直接运行的!
但是,这还有一个缺陷,我们设置的情况变量是临时变量,我们把当前终端关掉了该变量就丢失了… 那么我们有三种选择:
- 每次 gcc 之前先设置 LIBRARY_PATH 和 LD_LIBRARY_PATH 的路径。
- 设置一个永久的变量
- 把库文件放到默认目录中:/usr/local/lib,就像我们安装应用那样(必要sudo)。
第一种和第三种方法就不多说了,我们试试第二种方法,设置全局情况变量,首先执行指令:
vim ~/.bashrc
打开文件后,在末尾处直接添加:
export LIBRARY_PATH=~/Desktop/openFECLib/
export LD_LIBRARY_PATH=~/Desktop/openFECLib/
保存退出后执行 source .bashrc 令其见效即可!
这样我们就乐成设置了一个持久的变量!
本文档完~
到桌面的目录 ~/Desktop/openFECLib 中,然后我们通过情况变量指定这个目录作为库目录之一,使我们编译时不再必要额外指定库目录路径!
此时,~/Desktop/openFECLib 的内容如下:
[外链图片转存中…(img-rUhDcfVG-1710836785819)]
现实上就是把库文件(静态库和动态库)都放到了该目录下,然后就很简单,就向情况变量设置该目录即可:
export LIBRARY_PATH=~/Desktop/openFECLib/
export LD_LIBRARY_PATH=~/Desktop/openFECLib/
必要留意的是,我这里是直接重写了该变量,假如只是希望往内里添加新的路径,则通过如下指令:
export LIBRARY_PATH=~/Desktop/openFECLib/LIBRARY_PATH
export LD_LIBRARY_PATH=~/Desktop/openFECLib/LD_LIBRARY_PATH
此时,我们重新对目标文件举行编译,不外我们执行如下指令:
gcc simple_server.c -o simple_server -lopenfec
很好,没有报错,直接就编译乐成了,而且生成的 simple_server 是可以直接运行的!
但是,这还有一个缺陷,我们设置的情况变量是临时变量,我们把当前终端关掉了该变量就丢失了… 那么我们有三种选择:
- 每次 gcc 之前先设置 LIBRARY_PATH 和 LD_LIBRARY_PATH 的路径。
- 设置一个永久的变量
- 把库文件放到默认目录中:/usr/local/lib,就像我们安装应用那样(必要sudo)。
第一种和第三种方法就不多说了,我们试试第二种方法,设置全局情况变量,首先执行指令:
vim ~/.bashrc
打开文件后,在末尾处直接添加:
export LIBRARY_PATH=~/Desktop/openFECLib/
export LD_LIBRARY_PATH=~/Desktop/openFECLib/
保存退出后执行 source .bashrc 令其见效即可!
这样我们就乐成设置了一个持久的变量!
本文档完~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |