生成后缀为“.i”的文件
生成后缀为“.s”的文件
生成后缀为“.o”的文件
生成后缀为“.out”的文件
使用 g++
编译C++源代码的时候,我们可使用以下命令 即可完成编译C++源代码文件,并且直接产生可执行的二进制文件
# 编译test.cpp 文件,在 Linux 下,默认产生名为 a.out 的二进制可执行文件
g++ test.cpp
实际上,上面的一步编译指令包含了以下几个过程
第一步:预处理 Pre-processing,生成.i 文件
# -E 选项指示编译器仅对输入文件进行预编译
g++ -E test.cpp -o testr.i
第二步:编译-Compiling,生成.s 文件
# -S 编译选项告诉 g++ 在为 c++ 代码产生了汇编语言文件后停止编译
# g++ 产生的汇编语言文件的缺省扩展名是 .s
g++ -S test.i -o test.s
第三步:汇编-Assembing,生成.o 文件
# -c 选项告诉 g++ 仅把源代码编译为机器语言的目标代码
# 缺省时 g++ 建立的目标代码文件有一个 .o 的扩展名
g++ -c test.s -o test.o
第四步:链接-Lingking,生成 bin 二进制文件
# -o 编译选项来为将产生的可执行文件指定文件名,如果不使用-o参数,在Linux下默认输出名为 a.out 的可执行文件
g++ test.o -o test
VSCode 是通过调用 GCC 编译器来实现 C/C++的编译工作的。
gcc无法进行库文件的连接;而g++则能完整编译出可执行文件。(实质上,g++从步骤1-步骤3均是调用gcc完成,步骤4连接则由自己完成)
对于 .c和.cpp文件,使用gcc编译会分别当做c和cpp文件编译(cpp的语法规则比c的更强一些)
对于 .c和.cpp文件,使用g++则统一当做cpp文件编译
在编译c++代码时,g++会调用gcc,所以两者是等价的,但是因为gcc命令不能自动和C++程序使用的库链接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接都用g++,这就给人一种错觉,好像cpp程序只能用g++似的。
gcc在编译.c文件时,可使用的预定义宏是比较少的,很多都是未定义的。 gcc在编译cpp文件时、以及g++在编译c文件和cpp文件时(这时候gcc和g++调用的都是cpp文件的编译器),会加入一些额外的宏,这些宏如下:
#define __GXX_WEAK__ 1
#define __cplusplus 1
#define __DEPRECATED 1
#define __GNUG__ 4
#define __EXCEPTIONS 1
#define __private_extern__ extern
所以,既然使用g++编译时,会统一当做cpp文件编译,那么为了对c编译的支持,利用默认的宏定义__cplusplus,在c代码中添加如下内容。
#ifdef __cplusplus
extern "C" {
#endif
//一段c代码
#ifdef __cplusplus
}
#endif
gcc [options] file
编译参数 | 详细说明 |
---|---|
-o | Object的简写,指定编译的⽬标文件名,否则会⽣成的⽬标⽂件名是a.out;eg: gcc main.c -o main |
-S | 把源文件编译成汇编代码 |
-E | 只执行预处理 |
-include | 包含头文件,功能如同在源码的语句。eg:#include |
-I (大写i) | include简写,指定程序包含头文件的路径,一般用于指定第三方库的头文件。 默认在/usr/include ,没有就要用 -I 参数指定了 |
-L | 编译时,用于指定程序第三方库的查找路径。 |
-l | 链接时,指定程序需要进行链接的库。注:一般库文件名是libxxx.so,-I指定xxx即可。如-Ixxx ,在/lib和/usr/lib和usr/local/lib里面的库直接用-l参数就能链接,如果库文件没有放在系统库目录中,需要使用-L 参数(大写l)指定库文件所在目录 |
-rpath | 程序执⾏需要指定动态库的路径,但是可以⽤-rpath参数在编译时指定程序运⾏时需要加载的库的路径。 |
-D | 程序编译阶段可以定义一些宏,该方法可以让程序有选择性的运行代码。 |
-0n | 这是程序的优化等级。n的范围是0-3。n越大优化等级越高,程序运行的越快。否则越慢,n==0时是关闭优化。利于程序的调试,一般程序调试阶段会关闭优化等级,发布程序会把优化等级设为-O2。-O0:不进行优化处理。-O 或 -O1:优化生成代码。-O2:进一步优化。-O3 比 -O2 更进一步优化,包括 inline 函数。 |
-g | 打印程序的调试信息,如果需要使⽤gdb⼯具进⾏调试程序,程序编译的时候,需要加上该参数。 |
-share | 编译的时候尽量使用动态库。(除非只有静态库,没有动态库) |
-static | 禁止使用动态库,编译的时候只加载静态库,这会导致执行件很大。 |
-w | 不生成任何的警告信息。 |
-Wall | 生成所有的警告信息。 |
-fpic | 使输出的对象模块可重定位地址方式行成的。 |
-shared | 把对应的源文件形成对应的动态链接库。 |
-std=c++11 | 设置编译标准 |
只执行预处理,输出 hello.i 源文件
gcc -E hello.c -o hello.i
只执行预处理和编译,输出 hello.s 汇编文件
gcc -S hello.c
也可以由 hello.i 文件生成 hello.s 汇编文件
gcc -S hello.i -o hello.s
只执行预处理、编译和汇编,输出 hello.o 目标文件
gcc -c hello.c
也可以由 hello.i或hello.s生成目标文件hello.o
gcc -c hello.i -o hello.o
gcc -c hello.s -o hello.o
由目标文件hello.o链接成可执行文件 hello
gcc hello.o -o hello
使用-std
设置编译标准
# 使用 c++11 标准编译 test.cpp
g++ -std=c++11 test.cpp
-D
定义宏,为了演示宏的作用,创建源码文件 gcc_02_test/test.cpp
,并添加以下C++源代码
#include
int main()
{
// 根据是否存在 DEBUG 进行逻辑处理
#ifdef DEBUG
printf("DEBUG LOG\n");
#endif
printf("in\n");
return 0;
}
使用 g++ -DDEBUG test.cpp
编译的同时定义DEBUG
宏,执行编译后的可执行文件可以看到 “DEBUG LOG” 被输出。原因是我们使用-DDEBUG
参数定义 DEBUG
宏,在执行程序的时候,程序检测到了 DEBUG
宏的存在,并执行了对应的逻辑。
test.c
int add(int a,int b)
{
retrun a+b;
}
test.h
#ifndef _TEST_H_
#define _TEST_H_
extern int add(int a,int b);
main.c
#include
#include
int main()
{
int a,b;
printf("please input a and b\n");
scanf("%d %d",&a,&b);
printf("The add:%d\n",add(a,b));
}
在此例中,test.c用于编译生成静态库libtest.a,test.h为libtest.a对应的头文件。
生成test.o目标文件
gcc -c test.c -o test.o
使用ar将test.o打包成libtest.a静态库
ar rcs -o libtest.a test.o
生成libtest.a静态库后,可以使用命令查看libtest.a文件中包含哪些文件
ar t libtest.a
知识点补充:
Linux 中 ar命令:
-功能:创建静态库.a文件 -指令参数
-d 删除静态库中的成员文件。
-m 变更成员文件在静态库中的次序。
-p 显示静态库中的成员文件内容。
-q 将文件附加在静态库的末端。
-r 将文件插入静态库中。
-t 显示静态库中所包含的文件。
-x 静态库中取出成员文件。
编译 main.c, 并链接静态库 libtest.a,生成的可执行文件名为app_static
gcc -o app_static main.c libtest.a
可以加参数指定路径和库的名称
链接时,-l参数后不加空格指定所需要链接的库名
,这里库名是libtest.a,但是只需要给出-ltest即可,ld会以libtest作为库的实际名字。链接时,-L 指定库的搜索路径
,.表示的当前路径gcc -o app_static main.c -L. -ltest
运行app_static 【静态库编译得到的文件可以直接执行,而动态库则不行
】
$ ./app_static
查看文件描述
$ file app_static
hello: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=b72236c2211dd8f0c3003bc02ad5e70bb2354e8c, for GNU/Linux 3.2.0, not stripped
file:用来识别文件类型,也可用来辨别一些文件的编码格式。它是通过查看文件的头部信息来获取文件类型。
生成test.o目标文件,-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
由于动态库可以被多个进程共享加载,所以需要使用-fPIC选项生成位置无关的代码
gcc -c -o test.o -fPIC test.c
使用-shared参数生成动态库
gcc -shared -o libmyshare.so test.o
上述两个命令可以连在一起
gcc -shared -fPIC -o libmyshare.so test.c
编译main.c,使用libmyshare.so动态库,命令如下
gcc -o app_share main.c -L. -lmyshare
查看app_share使用动态库,
ldd app_share
ldd:用来打印或者查看程序运行所需的共享库(访问共享对象依赖关系),常用来解决程序因缺少某个库文件而不能运行的一些问题。
如果libmyshare.so无法找到,直接执行./app_share
就会出现错误。解决方法:首先使用export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
将动态库的目录加入LD_LIBRARY_PATH变量中。再次运行ldd app_share
另一种编译main.c,并链接libmyshare.so的方式如下(该方式通过./libmyshare.so直接指定使用当前目录下的libmyshare.so文件)
gcc -o app_share main.c ./libmyshare.so
直接运行会报错,执行如下指令
LD_LIBRARY_PATH=动态库路径 可执行文件名路径
例如:
LD_LIBRARY_PATH=src ./app_share
LD_LIBRARY_PATH是Linux环境变量名,该环境变量主要用于指定查找共享库(动态链接库)时,除了默认路径之外的其他路径。
export LD_LIBRARY_PATH=$(pwd)
将 libmyshare.so 所在的当前目录添加到 LD_LIBRARY_PATH 变量,再次执行 app_share
gcc hello.c -L. -lfoo -Wl,-rpath=`pwd` -o hello
rpath 即 run path,是种可以将共享库位置嵌入程序中的方法,从而不用依赖于默认位置和环境变量。
这里在链接时使用 -Wl,-rpath=xxxx 选项,-Wl 会发送以逗号分隔的选项到链接器,注意逗号分隔符后面没有空格。pwd:打印当前目录
这种方式要求共享库必须有一个固定的安装路径,欠缺灵活性,不过如果设置了 LD_LIBRARY_PATH,程序加载时也是会到相应路径寻找共享库的。
sudo cp libmyshare.so /usr/lib/
执行程序,如果程序仍然运行失败,请尝试执行 ldconfig
命令更新共享库的缓存列表。
此时,再次查看程序的共享库依赖
$ ldd app_share
linux-vdso.so.1 (0x00007ffecfbb1000)
libmyshare.so => /lib/libmyshare.so (0x00007f3f3f1ad000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3f3efbb000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3f3f1d6000)
可以看到 libmyshare.so已经被发现了,其中 /lib 是 /usr/lib 目录的软链接。
-g
选项告诉GCC产生能被 GNU调试器(DGB) 使用的调试信息,以调试程序
# 产生带调试信息的可执行文件test
g++ -g test.cpp -o test
所谓优化,是指如省略代码中从来未使用过的变量、直接常量表达式用结果替代等操作,这些操作会缩减目标文件所含的代码,提高最终生成的可执行文件的运行效率。
O
参数告诉 g++ 对源代码进行基本优化。这些优化在大多数情况下都使程序执行得更快,常用优化级别如下:
-O
: 同时减少代码的长度和执行时间,其效果等价于 -O1
-O0
: 表示不做优化
-O1
: 表示默认优化
-O2
: 告诉 g++ 产生尽可能小和尽可能快的代码。除了完成-O1
的优化之外,还进行一些额外的调整工作,如指令调整等
-O3
: 包括循环展开和其他一些与处理性相关的优化工作,选项将使编译的速度比 -O
慢,但通常产生的代码执行速度会更快。
# 使用 -O2 优化源代码,并输出可执行文件。
g++ -O2 test.cpp
编译环境设置:windows安装minggw支持linux程序编译
创建c++文件
终端输入指令:g++ -g .\main.cpp
进入调试界面:快捷键F9是设置短点,F10是设置下一步,
launch.json中最重要的信息:【第一个生成可调试的可执行文件,和main.cpp同一个路径下,第二个在编译前指定要执行task.exe任务,在.vscode路径下】
采用上述单文件的操作会报错,需要手动写launch.json和task.json
参考博文:
树莓派——Linux共享库、静态库、动态库详解
静态库和动态库的区别
gcc与g++