GCC/G++用法
麒麟下安装gcc
方法一:终端命令:sudo apt-get build-dep gcc
方法二:
终端命令:sudo apt-get install build-essential
方法三:
终端命令:sudo apt install gcc g++
安装后查看版本 :
终端命令:gcc --version
1.下载gcc-4.6.1安装包
gcc各版本浏览地址:http://ftp.gnu.org/gnu/gcc/
gcc-4.6.1浏览地址:http://ftp.gnu.org/gnu/gcc/gcc-4.6.1/
gcc-4.6.1下载地址:http://ftp.gnu.org/gnu/gcc/gcc-4.6.1/gcc-4.6.1.tar.bz2
2.将gcc-4.6.1.tar.bz2放到/opt文件夹下解压
[root@rekfan.com ~]# cd /opt
[root@rekfan.com opt]# tar xjvf gcc-4.6.1.tar.bz2
3.创建安装目录
[root@rekfan.com opt]# mkdir /usr/local/gcc-4.6.1/
4.进入安装目录
[root@rekfan.com opt]# cd /usr/local/gcc-4.6.1/
5.配置安装文件
[root@rekfan.com gcc-4.6.1]# /opt/gcc-4.6.1/configure --prefix=/usr/local/gcc-4.6.1
(执行源目录 /opt/gcc-4.6.1/中的configure命令,配置将gcc安装到目标目录/usr/local/gcc-4.6.1/下,这里–prefix选项代表要将该库安装在哪里,我是装在/usr/local/gcc-4.6.1目录下,后面的安装都会用到这个选项)
g++和gcc的区别
GCC:GNU Compiler Collection(GUN 编译器集合),它可以编译C、C++、JAV、Fortran、Pascal、Object-C、Ada等语言;
gcc:是GCC中的GUN C Compiler(C 编译器);
g++:是GCC中的GUN C++ Compiler(C++编译器);
一个有趣的事实就是,就本质而言,gcc和g++并不是编译器,也不是编译器的集合,它们只是一种驱动器,根据参数中要编译的文件的类型,调用对应的GUN编译器而已,比如,用gcc编译一个c文件的话,会有以下几个步骤:
Step1:Call a preprocessor, like cpp.
Step2:Call an actual compiler, like cc or cc1.
Step3:Call an assembler, like as.
Step4:Call a linker, like ld.
g++编译器是GCC的一部分,GCC编译工作一般分为四个步骤:
(1)预处理(Preprocessing)。由预处理器cpp完成,将.cpp源文件预处理为.i文件。
g++ -E test.cpp -o test.i //生成预处理后的.i文件
(2)编译(Compilation)。将.i文件编译为.s的汇编文件。使用-S选项,只进行编译而不进行汇编,生成汇编代码。
g++ -S test.i -o test.s //生成汇编.s文件
(3)汇编(Assembly)。由汇编器as完成,将.s文件汇编成.o的二进制目标文件。
g++ -c test.s -o test.o //生成二进制.o文件
(4)链接(Linking)。由链接器ld,将.o文件连接生成可执行程序。
g++ test.o -o test.out //生成二进制.out可执行文件
由于编译器是可以更换的,所以gcc不仅仅可以编译C文件,所以,更准确的说法是:gcc调用了C compiler,而g++调用了C++ compiler。
c和g++的主要区别:
1、对于*.c和*.cpp文件,gcc分别当做c和cpp文件编译(c和cpp的语法强度是不一样的);
2、对于 *.c和*.cpp文件,g++则统一当做cpp文件编译;
3、使用g++编译文件时,g++会自动链接标准库STL,而gcc不会自动链接STL;
4、gcc在编译C文件时,可使用的预定义宏是比较少的;
5、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
6、在用gcc编译c++文件时,为了能够使用STL,需要加参数 –lstdc++ ,但这并不代表 gcc –lstdc++ 和 g++等价,它们的区别不仅仅是这个。
误区:
误区一:.c文件只能用gcc进行编译,.cpp文件只能用g++进行编译。
(1)gcc和g++均可以编译c代码和c++代码,后缀为.c的,gcc把它当作C程序,而g++当作c++程序;后缀为.cpp的,两者均当作c++程序进行编译。虽然c++是c的超集,但两者对语法的要求是有区别的。C++的语法规则更加严谨一些。
(2)编译阶段,g++会调用gcc,对于c++代码两者是等价的,但因为gcc命令不能自动和c++程序使用的库链接,所以通常用g++来完成链接,为了统一起见,干脆编译、链接统统用g++了,这就给人一种错觉好像.cpp文件只能用g++。
误区二:gcc不会定义__cplusplus__宏,而g++会。
(1)这个宏只是标志着编译器会把代码按照C/C++语法规则进行解释、编译。故此,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。
误区三:编译只能用gcc,链接只能用g++.
(1)混淆概念。应说为:编译可以用gcc/g++,而链接可以用g++或者gcc –lstdc++.因为gcc命令不能自动和c++程序使用的库链接,所以通常使用g++来完成链接。但在编译阶段,g++会自动调用gcc,二者等价。
误区四:extern “C”和gcc/g++有关系。
(1)实际上并无关系,无论是gcc还是g++,用extern “C”时,都是以C的命名方式来为symbol命名,否则,都是以c++方式命名。
二 gcc的参数
1 库和头文件
1、-l:用来指定程序要链接的库,-l参数紧接着就是库名。那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的头lib和尾.so去掉就是库名了。
2、-L:跟着的是库文件所在的目录名。比如我们把libtest.so在/aaa/bbb/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest。
3、-I(大写的i):编译程序按照-I指定的路进去搜索头文件。-I/home/include/表示将-I/home/include/目录作为第一个寻找头文件的目录,寻找的顺序是: /home/include/ -->/usr/include–>/usr/local/include。
2 警告
-Wall开启所有警告;-w关闭警告。
3 操作大文件
gcc -o ser ser.c pub.h -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
三 优化选项
gcc默认提供了5级优化选项的集合:
-O0:无优化(默认)。
-O和-O1:使用能减少目标文件大小以及执行时间并且不会使编译时间明显增加的优化。在编译大型程序的时候会显著增加编译时内存的使用和时间。
o1是最基本的优化,主要对代码的分支,表达式,常量来进行优化,编译器会在较短的时间下将代码变得更加短小,这样体积就会变得更小,会减少内存的占用率,在操作系统进行内存调度时就会更快。但是事情没有绝对的优点,当一个庞大的程序被拆碎细分的话,内存占用会大大增加,由于当今系统大多数都是多线程,就会出现卡顿和反应延迟。
当在机子上,运行无法干涉到调试时,-O选项也会添加-fomit-frame-pointer选项。在ADA编译器上,-O不会添加-ftree-sra选项,想在ADA编译器下使用该选项,必须明确的在命令行指出。
-O2: 此选项将增加编译时间和目标文件的执行性能。
包含-O1的优化并增加了不需要在目标文件大小和执行速度上进行折衷的优化。
编译器不执行循环展开以及函数内联。
-Os:专门优化目标文件大小,执行所有的不增加目标文件大小的-O2优化选项,并且执行专门减小目标文件大小的优化选项。
-O3:打开所有-O2的优化选项并且增加 -finline-functions, -funswitch-loops,-fpredictive-commoning,-fgcse-after-reload and -ftree-vectorize优化选项。
链接:
gcc优化选项:https://blog.csdn.net/zhang626zhen/article/details/52988242
gCC优化选项的各种含义以及潜藏风险:https://blog.csdn.net/findaway123/article/details/41752857
四 gcc / g++ Debug 模式
1 Debug/release模式
Debug模式下的代码比release模式具有更多的检测,比如在windows平台上assert宏只在release模式下有效。Linux上则没有这个限制,无论Debug还是release模式assert都生效,但是如果你想让assert失效,编译的时候 加上 -DNDEBUG 即可。
2 Debug检测
我们都说了Debug模式可以帮我们检测一些东西,但是可能会有额外的性能开销(虽然微不足道)。默认下Linux的动态库文件是集于Debug 和 release于一身的。看看这个列子:
#include<vector>
using namespace std;
int main()
{
vector<int> v;
v.reserve(10); //容量为10 但是size 为0
v[3] = 1;
return 0;
}
下面这份代码在windows 下的Debug模式下会崩溃,release模式下不会崩溃,这是因为windows下的vector对operator[] 做了assert的检测。那么我们跑这份代码在Linux下,它并不会报错,所以那我们如何开启真真的Debug 模式呢?
3 _GLIBCXX_DEBUG
如果我们想开启Debug模式必须定义上面的宏。
4 debug模式下的容器
gcc是有专门的debug容器的,这些容器使用起来和标准容器一模一样,所以放心使用就好,但是有个准则就是俩个不同的.o文件内不能一个使用了debug模式的容器,一个没使用debug模式下的容器,这俩个.o文件是不能链接到一起的。
5 非c++11的
6 c++11的Debug容器
7 demo
#include<debug/vector>
#define _GLIBCXX_DEBUG
int main()
{
__gnu_debug::vector<int> v;
v.reserve(10);
v[3]=1;
return 0;
}
$ ./a.out
/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/debug/vector:265:
error: attempt to subscript container with out-of-bounds index 3, but
container only holds 0 elements.
Objects involved in the operation:
sequence "this" @ 0x0x7ffeae481db0 {
type = NSt7__debug6vectorIiSaIiEEE;
}
Aborted (core dumped)
五 静态库的创建
动态库:windows:.dll后缀,Linux:.so后缀;静态库:windows:.lib后缀,Linux:.a后缀。
1 静态库的创建
有源文件test.c和test.h:
1、编译test.c生成目标文件:gcc -o test.o -c test.c;
2、将目标文件加入到静态库中:ar rcv libtest.a test.o;
3、连接:gcc -o test test.c -lmytest(如果库不在默认路径,需要用-L表明路径)。
2 ar参数
参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
参数c:创建一个库。不管库是否存在,都将创建。
参数s:创建目标文件索引,这在创建较大的库时能加快时间。(补充:如果不需要创建索引,可改成大写S参数;如果.a文件缺少索引,可以使用ranlib命令添加)。
参数q:在文件包尾加入文件。
参数v:将文件插入库文件中。
六 动态库的创建
动态库:windows:.dll后缀,Linux:.so后缀;静态库:windows:.lib后缀,Linux:.a后缀。
1 动态库的编译
下面通过一个例子来介绍如何生成一个动态库。这里有一个头文件:so_test.h,三个.c文件:test_a.c、test_b.c、test_c.c,我们将这几个文件编译成一个动态库:libtest.so。
//so_test.h:
#ifndef __SO_TEST_H__
#define __SO_TEST_H__
void test_a();
void test_b();
void test_c();
#endif
//test_a.c:
#include "so_test.h"
void test_a()
{
printf("this is in test_a...\n");
}
//test_b.c:
#include "so_test.h"
void test_b()
{
printf("this is in test_b...\n");
}
//test_c.c:
#include "so_test.h"
void test_c()
{
printf("this is in test_c...\n");
}
将这几个文件编译成一个动态库:libtest.so
$ gcc test_a.c test_b.c test_c.c -fPIC -shared -o libtest.so
2 动态库的链接
//test.c:
#include "so_test.h"
int main()
{
test_a();
test_b();
test_c();
return 0;
}
将test.c与动态库libtest.so链接生成执行文件test:
$ gcc test.c -L. -ltest -o test
测试是否动态连接,如果列出libtest.so,那么应该是连接正常了:
$ ldd test
3 编译参数解析
最主要的是GCC命令行的一个选项:
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。
-fPIC:表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L.:表示要连接的库在当前目录中
-ltest:编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,不过如果没有root权限,那么只能采用输出LD_LIBRARY_PATH的方法了。
4 注意
调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。
链接:
用gcc编译生成动态链接库*.so文件的方法:http://www.linuxdiyf.com/viewarticle.php?id=3354
六 gdb
1 参数
-tui启动UI选项;-q(quit)代表不打印那一大串版本版权信息之类的刷屏字幕;切换调试文件:(gdb)file test2或者(gdb)exec(-file) test2。
2 list
list(l) 查看最近10行源码;
list fun 查看fun函数源码;
list file:fun 查看file文件中的fun函数源码;
list num1 num2 查看num1~num2行的源码;
3 break
break 行号;
break fun 在函数处设置断点;
break file:行号;
break file:fun;
break if <condition> 条件成立时程序停住;
info break (i b) 查看断点;
watch expr 当expr的值发生改变时,程序停住;
delete n 删除断点;
4 step & stepi
(1)step [count] 如果没有指定count, 则继续执行程序,直到到达与当前源文件不同的源文件中时停止;如果指定了count, 则重复行上面的过程count次。
(2)stepi [count] 如果没有指定count, 继续执行下一条机器指令,然后停止;如果指定了count,则重复上面的过程count次。
(3)step比较常见的应用场景:在函数func被调用的某行代码处设置断点,等程序在断点处停下来后,可以用step命令进入该函数的实现中,但前提是该函数编译的时候把调试信息也编译进去了,负责step会跳过该函数。
5 next & nexti
(1)next [count] 如果没有指定count, 单步执行下一行程序;如果指定了count,单步执行接下来的count行程序。
(2)nexti [count] 如果没有指定count, 单步执行下一条指令;如果指定了count, 音频执行接下来的count条执行。
(3)stepi和nexti的区别:nexti在执行某机器指令时,如果该指令是函数调用,那么程序执行直到该函数调用结束时才停止。
6 disassemble
用 gdb 查看汇编代码,可以采用 disassemble 和x命令。/m源码和汇编一起排列,/r还可以看到16进制代码。
调试示例:
(gdb) disassemble /m main
Dump of assembler code for function main:
{
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
0x00000000004004c8 <+4>: sub $0x20,%rsp
0x00000000004004cc <+8>: mov %edi,-0x14(%rbp)
0x00000000004004cf <+11>: mov %rsi,-0x20(%rbp)
int size=sizeof("hjj");
=> 0x00000000004004d3 <+15>: movl $0x4,-0x4(%rbp)
printf("size is %d\n",size);
0x00000000004004da <+22>: mov $0x4005f8,%eax
0x00000000004004df <+27>: mov -0x4(%rbp),%edx
0x00000000004004e2 <+30>: mov %edx,%esi
0x00000000004004e4 <+32>: mov %rax,%rdi
0x00000000004004e7 <+35>: mov $0x0,%eax
0x00000000004004ec <+40>: callq 0x4003b8 <printf@plt>
return 0;
0x00000000004004f1 <+45>: mov $0x0,%eax
}
0x00000000004004f6 <+50>: leaveq
0x00000000004004f7 <+51>: retq
End of assembler dump.
用 x/i 可以查看指令:
(gdb) x/15i main
0x4004c4 <main>: push %rbp
0x4004c5 <main+1>: mov %rsp,%rbp
0x4004c8 <main+4>: sub $0x20,%rsp
0x4004cc <main+8>: mov %edi,-0x14(%rbp)
0x4004cf <main+11>: mov %rsi,-0x20(%rbp)
=> 0x4004d3 <main+15>: movl $0x4,-0x4(%rbp)
0x4004da <main+22>: mov $0x4005f8,%eax
0x4004df <main+27>: mov -0x4(%rbp),%edx
0x4004e2 <main+30>: mov %edx,%esi
0x4004e4 <main+32>: mov %rax,%rdi
0x4004e7 <main+35>: mov $0x0,%eax
0x4004ec <main+40>: callq 0x4003b8 <printf@plt>
0x4004f1 <main+45>: mov $0x0,%eax
0x4004f6 <main+50>: leaveq
0x4004f7 <main+51>: retq
$pc 指向当前程序运行地址:
(gdb) x/5i $pc
=> 0x4004d3 <main+15>: movl $0x4,-0x4(%rbp)
0x4004da <main+22>: mov $0x4005f8,%eax
0x4004df <main+27>: mov -0x4(%rbp),%edx
0x4004e2 <main+30>: mov %edx,%esi
0x4004e4 <main+32>: mov %rax,%rdi
(gdb)
p $edi 等可以查看寄存器。
7 layout
layout 用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:
layout src 显示源代码窗口
layout asm 显示汇编窗口
layout regs 显示源代码/汇编和寄存器窗口
layout split 显示源代码和汇编窗口
layout next 显示下一个layout
layout prev 显示上一个layout
Ctrl + L:刷新窗口
Ctrl + x,再按1:单窗口模式,显示一个窗口
Ctrl + x,再按2:双窗口模式,显示两个窗口
Ctrl + x,再按a:回到传统模式,即退出layout,回到执行layout之前的调试窗口。
8 函数调用命令
Backtrace(bt) 查看栈帧信息;
frame i调出第i层栈信息,可以结合bt使用;
调出后可以通过print <var>来打印想要的变量;
select-frame args 移动到指定的帧中去,不打印信息。
9 其他
run/r 启动程序;
continue(c) 运行至下一个断点;
finish 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数信息;
until 运行程序直到程序退出循环体;
print i (p i) 查看变量的值;
ptype 查看变量类型;
print array 查看数组;
print *array@len 查看动态内存;
print x=5 改变运行时的数据;
print &array 查看数组的地址;
回车代表上一个命令;
七 core文件
1 概念
当程序运行的过程中异常终止或崩溃(1、内存访问越界;2、非法指针;3、堆栈溢出),操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump(中文有的翻译成“核心转储”)。我们可以认为 core dump 是“内存快照”,但实际上,除了内存信息之外,还有些关键的程序运行状态也会同时 dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。core文件记录的是程序崩溃时的内存映像,并加入调试信息。
2 开启core dump
在终端输入:ulimit -c,输出结果为0,则系统默认关闭core dump,即使程序异常终止,也不会生成core dump文件。
ulimit -c unlimited ,开启core dump功能,且不限制core文件大小。
ulimit -c filesize,开启core dump功能,且限制core文件大小,filesize的单位为kbyte。如果生成的信息超过此大小,将会被裁剪,最终生成一个不完整的core文件。在调试此 core文件的时候,gdb会提示错误。
上面命令开启,只会在此终端生效,如果想让修改永久生效,则需要修改配置文件,如 .bash_profile、/etc/profile或/etc/security/limits.conf。增加一行:
* soft core unlimited
3 修改core文件生成路径和名称
默认生成的core文件,在可执行文件的目录下,默认文件名为core。新的core文件生成将覆盖原来的core文件。
/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展。文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.pid;为0则表示生成的core文件同一命名为core。可通过以下命令修改此文件:
$: echo "1" > /proc/sys/kernel/core_uses_pid
/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式。可通过以下命令修改此文件:
$: echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳。
以下是参数列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix
时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名
4 程序中开启core dump
通过如下API可以查看和设置RLIMIT_CORE:
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
#define CORE_SIZE 1024 * 1024 * 500
int main()
{
struct rlimit rlmt;
if (getrlimit(RLIMIT_CORE, &rlmt) == -1) {
return -1;
}
printf("Before set rlimit CORE dump current is:%d, max is:%d\n", (int)rlmt.rlim_cur, (int)rlmt.rlim_max);
rlmt.rlim_cur = (rlim_t)CORE_SIZE;
rlmt.rlim_max = (rlim_t)CORE_SIZE;
if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {
return -1;
}
if (getrlimit(RLIMIT_CORE, &rlmt) == -1) {
return -1;
}
printf("After set rlimit CORE dump current is:%d, max is:%d\n", (int)rlmt.rlim_cur, (int)rlmt.rlim_max);
/*测试非法内存,产生core文件*/
int *ptr = NULL;
*ptr = 10;
return 0;
}
5 gdb调试core文件
gdb test test.core,进入test可执行文件的core文件。
Backtrace(bt) 查看栈帧信息;
6 where命令
where命令,可以显示导致段错误的执行函数处。
7 问题
要保证存放coredump的目录存在且进程对该目录有写权限。存放coredump的目录即进程的当前目录,一般就是当初发出命令启动该进程时所在的目录。但如果是通过脚本启动,则脚本可能会修改当前目录,这时进程真正的当前目录就会与当初执行脚本所在目录不同。这时可以查看”/proc/<进程pid>/cwd“符号链接的目标来确定进程真正的当前目录地址。通过系统服务启动的进程也可通过这一方法查看。
若程序调用了seteuid()/setegid()改变了进程的有效用户或组,则在默认情况下系统不会为这些进程生成Coredump。很多服务程序都会调用seteuid(),如MySQL,不论你用什么用户运行mysqld_safe启动MySQL,mysqld进行的有效用户始终是msyql用户。如果你当初是以用户A运行了某个程序,但在ps里看到的 这个程序的用户却是B的话,那么这些进程就是调用了seteuid了。为了能够让这些进程生成core dump,需要将/proc/sys/fs /suid_dumpable文件的内容改为1(一般默认是0)。
设置足够大的Core文件大小限制 了。程序崩溃时生成的Core文件大小即为程序运行时占用的内存大小。但程序崩溃时的行为不可按平常时的行为来估计,比如缓冲区溢出等错误可能导致堆栈被 破坏,因此经常会出现某个变量的值被修改成乱七八糟的,然后程序用这个大小去申请内存就可能导致程序比平常时多占用很多内存。因此无论程序正常运行时占用 的内存多么少,要保证生成Core文件还是将大小限制设为unlimited为好。
八 多线程调试
info threads 显示当前可调试的所有线程;
thread ID 切换当前调试的线程为指定ID的线程;
attach process-id 在gdb状态下,开始调试一个正在运行的进程;
thread apply all command 所有线程执行command;
thread apply all bt 查看所有线程堆栈信息。
九 多进程调试
1 单独调试子进程
2 启动选项follow-fork-mode
follow-fork-mode允许选择程序在执行fork系统调用后是继续调试父进程还是调试子进程。
十 跨平台编译器
1 MinGw
安装:
https://zhuanlan.zhihu.com/p/76613134
https://www.mingw-w64.org/downloads/#mingw-builds
2 Cygwin
安装:
https://blog.csdn.net/xiaojin21cen/article/details/125146944
3 msys2
4 MinGW-w64 + CMake
因为 windows 版的 CMake 支持很多的编译器,而且其默认的一般是最新的 vs 工具,所以,需要指定 Makefile 的格式:
cmake –G”MinGW Makefiles” ..
make:
mingw32-make
5 差异
https://www.zhihu.com/question/22137175
https://blog.csdn.net/u010168781/article/details/121764725
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/essity/article/details/82944115
//////////////////////////////////////////////////////////////////////////////////////////////////////
1.g++简介
g++是GNU开发的C++编译器,是GCC(GNU Compiler Collection)GNU编译器套件的组成部分。另外,gcc是GNU的C编译器。
看官方手册你会发现g++的命令选项真的多如繁星,令人头皮发麻。但是常用的命令选项也就那几个,完成我们的日常编译,g++使用起来还是比较简单的!
g++编译器是GCC的一部分,GCC编译工作一般分为四个步骤: (1)预处理(Preprocessing)。由预处理器cpp完成,将.cpp源文件预处理为.i文件。
g++ -E test.cpp -o test.i //生成预处理后的.i文件
(2)编译(Compilation)。将.i文件编译为.s的汇编文件。使用-S选项,只进行编译而不进行汇编,生成汇编代码。这里的编译器具体是什么,我暂时还不清楚,知道的请留言告知,万分感谢。百度百科说是egcs,但是我在Linux并没有查到该命令。
g++ -S test.i -o test.s //生成汇编.s文件
(3)汇编(Assembly)。由汇编器as完成,将.s文件汇编成.o的二进制目标文件。
g++ -c test.s -o test.o //生成二进制.o文件
(4)链接(Linking)。由链接器ld,将.o文件连接生成可执行程序。
g++ test.o -o test.out //生成二进制.out可执行文件
2.命令格式
gcc [-c|-S|-E] [-std=standard]
[-g] [-pg] [-Olevel]
[-Wwarn...] [-pedantic]
[-Idir...] [-Ldir...]
[-Dmacro[=defn]...] [-Umacro]
[-foption...] [-mmachine-option...]
[-o outfile] [@file] infile...
3.命令选项
关于g++的命令选项,大家可以参考g++百度百科或者GCC官方手册,或者使用man g++单独查看g++使用手册。
下面列出常用的命令选项。
(1)总体选项
-E
只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面。例子用法:
gcc -E hello.c > pianoapan.txt
gcc -E hello.c | more
慢慢看吧,一句`hello word`也要预处理成800行的代码。
-S
只激活预处理和编译,就是指把文件编译成为汇编代码。例子用法:
gcc -S hello.c
将生成.s的汇编代码,可以用文本编辑器查看。
-c
只激活预处理,编译,和汇编,也就是他只把程序做成obj文件。例子用法:
gcc -c hello.c
将生成.o的目标文件(object file)。
-o
指定目标名称,缺省的时候,gcc/g++编译出来的文件是a.out。例子如下:
g++ -o hello.out hello.cpp
g++ -o hello.asm -S hello.cpp
(2)目录选项
-I[dir]
在你是用#include "file"的时候,gcc/g++会先在当前目录查找你所指定的头文件,如果没有找到,会到系统默认的头文件目录找。如果使用-I指定了目录,编译器会先在指定的目录查找,然后再去系统默认头文件目录查找。对于#include <file>,gcc/g++会到-I指定的目录查找,查找不到,然后再到系统默认的头文件目录查找。
-include [file]
相当于“#include”,用于包含某个代码,简单来说,就是编译某个文件,需要另一个文件的时候,就可以
用它设定,功能就相当于在代码中使用#include。例子用法:
gcc hello.c -include /root/pianopan.h
-I-
就是取消前一个参数的功能,所以一般在-Idir之后使用
-idirafter [dir]
在-I的目录里面查找失败,将到目录dir里面查找。
-iprefix [prefix],-iwithprefix [dir]
一般一起使用,当-I的目录查找失败,会到prefix+dir下查找。
-L[dir]
编译的时候,指定搜索库的路径。比如你自己的库,可以用它指定目录,不然编译器将只在标准库的
目录找。这个dir就是目录的名称。
-l[library]
指定编译的时使用的库,例子用法
gcc -lcurses hello.c
使用curses库编译连接,生成程序。
(3)预处理选项
-Dmacro
相当于C语言中的#define macro。
-Dmacro=defn
相当于C语言中的#define macro=defn。
-Umacro
相当于C语言中的#undef macro。
-undef
取消对任何非标准宏的定义。
(4)链接方式选项
-static
此选项将禁止使用动态库。优点:程序运行不依赖于其他库。缺点:可执行文件比较大。
-shared
此选项将尽量使用动态库,为默认选项。优点:生成文件比较小。缺点:运行时需要系统提供动态库。
-symbolic
建立共享目标文件的时候,把引用绑定到全局符号上。对所有无法解析的引用作出警告(除非用连接选项,
'-Xlinker -z -Xlinker defs'取代)。注:只有部分系统支持该选项。
-Wl,-Bstatic
告诉链接器ld只链接静态库,如果只存在动态链接库,则链接器报错。
-Wl,-Bdynamic
告诉链接器ld优先使用动态链接库,如果只存在静态链接库,则使用静态链接库。
(5)错误与告警选项
-Wall
一般使用该选项,允许发出GCC能够提供的所有有用的警告。也可以用-W{warning}来标记指定的警告。
-pedantic
允许发出ANSI/ISO C标准所列出的所有警告。
-pedantic-errors
允许发出ANSI/ISO C标准所列出的错误
-werror
把所有警告转换为错误,在警告发生时中止编译过程。
-w
关闭所有警告,建议不要使用此项。
(6)调试选项
-g
指示编译器,在编译时,产生调试信息。
-gstabs
此选项以stabs格式生成调试信息,但不包括gdb调试信息。
-gstabs+
此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息.
-ggdb
此选项将尽可能的生成gdb可以使用的调试信息。
-glevel
请求生成调试信息,同时用level指出需要多少信息,默认的level值是2。
(7)优化选项
-O0
-O1
-O2
-O3
编译器优化选项分为4个级别,-O0表示没有优化,-O1为缺省值,建议使用-O2,-O3优化级别最高。
(8)其他选项
-fpic
编译器就生成位置无关目标码.适用于共享库(shared library).
-fPIC
编译器就输出位置无关目标码.适用于动态连接(dynamic linking),即使分支需要大范围转移。
-v 显示详细的编译、汇编、连接命令
-pipe
使用管道代替编译过程中的临时文件,在使用非gnu汇编工具的时候,可能有些问题
g++ -pipe -o hello.out hello.cpp
-ansi
关闭gnu c中与ansi c不兼容的特性,激活ansi c的专有特性(包括禁止一些asm inline typeof关键字,以及
UNIX,vax等预处理宏。
-fno-asm
此选项实现ansi选项功能的一部分,它禁止将asm,inline和typeof用作关键字。
-fno-strict-prototype
只对g++起作用,使用这个选项,g++将对不带参数的函数,都认为是没有显式的对参数的个数和类型说明,而不是没有
参数.而gcc无论是否使用这个参数,都将对没有带参数的函数,认为没有显式说明的类型。
-fthis-is-varialble
就是向传统c++看齐,可以使用this当一般变量使用。
-fcond-mismatch
允许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型。
-funsigned-char
-fno-signed-char
-fsigned-char
-fno-unsigned-char
这四个参数是对char类型进行设置,决定将char类型设置成unsigned char(前两个参数)或者signed char(后
两个参数)。
-imacros file
将file文件的宏,扩展到gcc/g++的输入文件,宏定义本身并不出现在输入文件中
-nostdinc
使编译器不在系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头文件的位置。
-nostdin C++
规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,此选项在创建libg++库使用。
-C
在预处理的时候,不删除注释信息,一般和-E使用,有时候分析程序,用这个很方便的。
-M
生成文件依赖的信息,包含目标文件所依赖的所有源文件。你可以用gcc -M hello.c来测试一下,很简单。
-MM
和上面的那个一样,但是它将忽略由#include造成的依赖关系。
-MD
和-M相同,但是输出将导入到.d的文件里面。
-MMD
和-MM相同,但是输出将导入到.d的文件里面。
-Wa,option
此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然后传递给会汇编程序。
-Wl.option
此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然后传递给会连接程序。
-x language filename
设定文件所使用的语言,使后缀名无效,对以后的多个有效.也就是根据约定C语言的后缀名称是.c的,而C++的后缀
名是.C或者.cpp。如果你很个性,决定你的C代码文件的后缀名是.pig,那你就要用这个参数,这个参数对他后面
的文件名都起作用,除非到了下一个参数的使用。可以使用的参数有下面的这些:
c,objective-c,c-header,c++,cpp-output,assembler,assembler-with-cpp。
看到英文,应该可以理解的。例子用法:
gcc -x c hello.pig
-x none filename
关掉上一个选项,也就是让gcc根据文件名后缀,自动识别文件类型,例子用法:
gcc -x c hello.pig -x none hello2.c
4.FAQ
4.1编译选项疑问
4.1.1-Wno-unknown-pragmas和-Wno-format -pg
-Wno-unknown-pragmas:查了大量资料和官方的手册,我觉得这个应该是实验室的师兄写错了,貌似没有这个警告命令选项。官方手册中有如下两个设置警告的命令选项。
(1)-Wunknown-pragmas
Warn when a #pragma directive is encountered that is not understood by GCC. If this
command-line option is used, warnings are even issued for unknown pragmas in system
header files. This is not the case if the warnings are only enabled by the ‘-Wall’
command-line option.
遇到GCC无法识别的编译指导指令,发出警告。在使用了-Wall选项时,就不需要使用该命令选项了。
(2)-Wno-pragmas
Do not warn about misuses of pragmas, such as incorrect parameters, invalid
syntax, or conflicts between pragmas. See also '-Wunknown-pragmas'.
遇到GCC无法识别的编译指导指令,不发出警告。
-pg作用:编译的过程中加入额外的代码, 供性能分析工具gprof剖析程序的耗时情况。
4.2链接注意事项
4.2.1指定静态与动态的链接方式
g++链接库时,默认优先链接动态链接库。静态库与动态库混合链接时,有如下两种方法: (1)静态链接库使用绝对路径,动态链接库使用-l。以boost库为例,如果我们要使用静态库则可书写如下:
g++ main.cpp -pthread /usr/lib64/libboost_thread.a /usr/lib64/libboost_system.a
(2)使用-Wl,-Bstatic告诉链接器ld链接静态库,不存在静态库,则ld报错,只存在动态链接库也报错。使用-Wl,-Bdynamic告诉链接器优先使用动态链接库,如果只存在静态库,则链接静态库,不报错。示例如下:
g++ main.cpp -Wl,-Bstatic -lboost_system -lboost_thread -Wl,-Bdynamic
注意: (1)命令末尾-Wl,-Bdynamic,作用是告诉链接器,后续系统库的链接默认使用动态链接,否则会出现找不到系统库的错误,诸如:
/usr/bin/ld: cannot find -lgcc_s
collect2: ld returned 1 exit status
(2)链接时,库要放在目标文件的后面,否则会报”undefined reference to: xxx”错误。具体参见gcc手册的如下描述:
the linker searches and processes libraries and object files in the order they are
specified. Thus, `foo.o -lz bar.o' searches library `z' after file foo.o but before
bar.o. If bar.o refers to functions in `z', those functions may not be loaded.
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2301_78835635/article/details/133493803