目录
- 一、gcc编译流程
- 1.1 预处理阶段
- 1.2 预编译阶段
- 1.3 汇编阶段(最耗时)
- 1.4 链接阶段
- 二、gcc的相关参数
- 三、Linux下静态库的制作和使用
- 3.1 制作静态库
- 3.2 使用静态库
- 四、Linux下动态库的制作和使用
- 4.1 制作动态库
- 4.2 使用动态库
- 五、makefile的编写
- 5.1 创建makefile生成模板
- 5.2 使用演示
- 5.3 改进makefile文件的编写
- 六、makefile的语法
- 七、gdb调试
- 7.1 常用的调试命令介绍
- 7.2 gdb跟踪core错误
一、gcc编译流程
1.1 预处理阶段
宏定义展开,宏定义替换,展开include的文件
gcc -E -o hello.i hello.c
例如源文件hello.c内容如下:
#include <stdio.h>
int main()
{
printf("hello test");
return 0;
}
经过预处理后生成的hello.i内容如下:
# 1 "hello.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 391 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "hello.c" 2
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/stdio.h" 1 3 4
# 64 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/stdio.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_stdio.h" 1 3 4
# 68 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_stdio.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h" 1 3 4
# 649 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/_symbol_aliasing.h" 1 3 4
# 650 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h" 2 3 4
# 715 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/_posix_availability.h" 1 3 4
# 716 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h" 2 3 4
# 69 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_stdio.h" 2 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/Availability.h" 1 3 4
# 135 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/Availability.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/AvailabilityVersions.h" 1 3 4
# 136 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/Availability.h" 2 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/AvailabilityInternal.h" 1 3 4
# 137 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/Availability.h" 2 3 4
# 70 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_stdio.h" 2 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_types.h" 1 3 4
# 27 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/_types.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/_types.h" 1 3 4
# 33 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/_types.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/machine/_types.h" 1 3 4
# 34 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/machine/_types.h" 3 4
# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arm/_types.h" 1 3 4
# 13 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arm/_types.h" 3 4
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef short __int16_t;
typedef unsigned short __uint16_t;
typedef int __int32_t;
typedef unsigned int __uint32_t;
typedef long long __int64_t;
typedef unsigned long long __uint64_t;
///内容太长了....这里省略....
typedef long __darwin_intptr_t;
typedef unsigned int __darwin_natural_t;
# 46 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arm/_types.h" 3 4
typedef int __darwin_ct_rune_t;
# 2 "hello.c" 2
int main()
{
printf("hello test");
return 0;
}
可以看到预处理阶段后,源码的内容变多了,因为这里面包含了宏定义和include的文件的内容
1.2 预编译阶段
在这个阶段,gcc才会去检测代码的规范,语法是否有错误,gcc会把代码翻译成汇编
gcc -S -o hello.s hello.i
生成的汇编代码在hello.s文件内
.section __TEXT,__text,regular,pure_instructions
.build_version macos, 11, 0 sdk_version 11, 3
.globl _main ; -- Begin function main
.p2align 2
_main: ; @main
.cfi_startproc
; %bb.0:
sub sp, sp, #32 ; =32
stp x29, x30, [sp, #16] ; 16-byte Folded Spill
add x29, sp, #16 ; =16
.cfi_def_cfa w29, 16
.cfi_offset w30, -8
.cfi_offset w29, -16
mov w8, #0
str w8, [sp, #8] ; 4-byte Folded Spill
stur wzr, [x29, #-4]
adrp x0, l_.str@PAGE
add x0, x0, l_.str@PAGEOFF
bl _printf
ldr w0, [sp, #8] ; 4-byte Folded Reload
ldp x29, x30, [sp, #16] ; 16-byte Folded Reload
add sp, sp, #32 ; =32
ret
.cfi_endproc
; -- End function
.section __TEXT,__cstring,cstring_literals
l_.str: ; @.str
.asciz "hello test"
.subsections_via_symbols
1.3 汇编阶段(最耗时)
把.s文件翻译成二进制.o文件(机器指令)
gcc -c -o hello.o hello.s
1.4 链接阶段
计算逻辑地址,合并数据段,有些函数是在另外一个so库文件中的
gcc -o hello hello.o
经过这一步之后就会生成可执行文件hello了.然后在控制台上输入./hello就可以执行了.
通常用的比较多的就是-o指定生成可执行文件的名字, 例如
gcc -o hello hello.c
就可以生成可执行文件hello了
二、gcc的相关参数
- -I参数
-I (大写的i):用于指定当前的.c文件用到的.h文件所在的目录,这样就不会报找不到头文件的错误了.
例如某个目录下有一个a.h文件
#pragma once
int sum(int a,int b); //声明sum方法
然后hello.c文件如下:
#include<stdio.h>
#include "a.h" //包含a.h头文件
int main()
{
int ret = sum(1,2);
printf("ret=%d\n",ret);
return 0;
}
int sum(int a,int b) //实现sum方法
{
return a+b;
}
如果直接用gcc hello.c会报这个错误
然后使用-I指定a.h的所在的目录
gcc hello.c -I ../12/
这样编译就OK了
- -o选项
用于指定编译后生成的文件名称,不指定的话默认是a.out - -D选项
通过-D可以在编译的时候设置宏定义
例如hello.c的代码如下:
#include<stdio.h>
#include "a.h"
int main()
{
int ret = sum(1,2);
#ifdef DEBUG
printf("hello world\n");
#endif
printf("ret=%d\n",ret);
return 0;
}
int sum(int a,int b)
{
return a+b;
}
上面代码的意思就是如果存在DEBUG宏,那么就会多打印hello world
通过下面命令可以编译的时候添加宏定义
- -L选项
用于指定包含库的路径 - -l选项(小写的L)
用于指定库的名(通常是libxxx.so或者libxxx.a 使用时是-lxxx) - -g选项
用于gdb调试,不加此选项不能调试 - -Wall选项
用于显示更多提示 - -lstdc++选项
用于编译c++文件,当然c++文件一般用g++编译. - -O选项
优化代码的选项,有1-3级别,例如-O1表示用1级别优化 - -c选项
将.c文件或.s汇编文件编译生成.o二进制文件 - -fPIC选项
将.c文件编译成与位置无关的代码的.o文件 (PIC是Position Independent Code的简写) - -shared
将与位置无关代码的.o文件打包成.so动态库文件.
三、Linux下静态库的制作和使用
3.1 制作静态库
静态库的命名规范为:libXxx.a 对应Windows的.lib文件.
制作步骤:
a.通过gcc -c选项将.c文件编译成.o文件
b.使用ar rcs 命令将.o文件打包,使用命令:
ar rcs libxxx.a file1.o file2.o ...
假设当前工程的.c和.h文件结构如下
其中a,b,d,e的c文件都包含了include目录下的.h文件,那么打包静态库的步骤如下:
首先,将所有的.c文件编译成.o文件
用-I(大写的i)指定头文件的路径,用*.c代替src目录下的所有.c文件, 当然你不嫌麻烦的话也可以逐个进行.o文件的编译.然后,将所有的.o文件进行打包,库名叫做libCalc.a
同样,如果你不嫌麻烦也可以这样打包: ar rcs libCalc.a a.o b.o d.o e.o
如何查看.a库文件的内容呢?
通过nm命令可以查看.a文件内包含了哪些.o文件,以及用到了什么函数,例如:
3.2 使用静态库
将上面步骤生层的libCalc.a文件移动到工程的lib目录内,同时在工程的根目录下创建main.c文件,内容如下:
#include <stdio.h>
#include "head.h"
int main()
{
int ret1 = add(10,10);
int ret2 = mul(10,10);
int ret3 = div(10,10);
int ret4 = sub(10,10);
printf("10+10=%d\n",ret1);
printf("10*10=%d\n",ret2);
printf("10/10=%d\n",ret3);
printf("10-10=%d\n",ret4);
return 0;
}
此时的工程目录结构如下:
然后编译main.c文件的时候需要输入下面命令
gcc -o main main.c -I ./include/ -L ./lib/ -lCalc
然后输入./main就可以运行程序了.
ps:使用-l的时候,格式是固定的,库文件需要省略前缀lib和后缀.a,并且需要和-l(小写的L)连着写.
四、Linux下动态库的制作和使用
4.1 制作动态库
动态库的命名规范为:libXxx.so 对应Windows的.dll文件.
制作步骤:
a.通过gcc -c -fPIC 将.c文件编译与位置无关的代码的.o文件,关键参数 -fPIC (PIC是Position Independent Code的简写)
b.通过gcc -shared -o将位置无关的.o文件打包.so文件,so文件的命名格式:libXxx.so
ps:上面2个步骤也可以合成一个步骤,就是 -fPIC 和 -shared 一起使用
例如还是上面那个工程
下面开始制作动态库
首先,将.c文件生成位置无关代码的.o文件,进入src目录,使用下面命令
gcc -c *.c -fPIC -I ../include/
然后,将所有.o文件打包成一个.so文件,使用下面命令
gcc -shared -o libCalc.so *.o
然后将生成的libCalc.so文件拷贝到当前工程的lib目录内, 然后在工程根目录,使用下面命令进行编译生成可执行程序
gcc -o main main.c -I include/ -L lib/ -lCalc
此时的项目结构:
现在你尝试./main运行会发现运行出错,并且使用ldd命令查看main会发现libCalc.so文件找不到
这时就需要介绍如何使用动态库了
4.2 使用动态库
方式一,将libCalc.so文件直接拷贝到系统的/lib或者/usr/lib目录内,或者创建软连接(不推荐)
方式二,将库路径添加到环境变量 LD_LIBRARY_PATH中(这种方式是临时的,下次登录就没了,不推荐),使用如下命令添加
export LD_LIBRARY_PATH=/home/chenys/work/WorkSpace/C/14/day01/lib:$LD_LIBRARY_PATH
当然这种方式还可以将配置添加到home目录下的.bashrc文件中,这样就是永久的.
方式三,配置/etc/ld.so.conf文件,在文件末尾增加库路径,例如/home/chenys/work/WorkSpace/C/14/day01/lib
(ps:路径随你定,只要有.so文件存在这个路径即可.)
然后执行sudo ldconfig -v刷新下配置.
配置完成后,就可以运行main程序了,并且用ldd命令也能查看到libCalc.so正确找到了.
五、makefile的编写
makefile的好处是一次编写,终身受益
a.命名规则:makefile 或者 Makefile
b.三要素: 目标,依赖,规则, 其中目标是必须的,其它2个非必须.
c.写法:
目标:依赖
tab键规则命令
d.如何执行makefile文件:
控制台输入make [目标] ,如果不写目标,那么默认执行makefile的第一个目标,带上目标后则执行指定的目标
5.1 创建makefile生成模板
除了手动创建makefile文件外,我们还可以拷贝makefile的模板来生成,为了操作方便,我这里通过在~/.bashrc文件中添加一个alias命令来简化这一过程,添加如下语句到~/.bashrc
文件中:alias echomake='cat ~/template/makefile.template >> makefile'
注意等号2边不要留有空白符,然后在创建~/template/makefile.template文件
SrcFiles=$(wildcard *.c)
TargetFiles=$(patsubst %.c,%,$(SrcFiles))
all:$(TargetFiles)
%:%.c
gcc -o $@ $< -g
.PHONY:clean
clean:
-@rm -rf $(TargetFiles)
最后在控制台输入echomake就可以自动使用模板创建一个makefile了
5.2 使用演示
假设工程的目录结构如下
其中head.h的内容如下:
int add(int a,int b);
int mul(int a,int b);
int div(int a,int b);
int sub(int a,int b);
a.c、b.c、d.c、e.c的内容如下:
//a.c
#include "../include/head.h"
int add(int a,int b){
return a+b;
}
//b.c
#include "../include/head.h"
int mul(int a,int b){
return a+b;
}
//d.c
#include "../include/head.h"
int div(int a,int b){
return a+b;
}
//e.c
#include "../include/head.h"
int sub(int a,int b){
return a+b;
}
main.c内容如下:
#include <stdio.h>
#include "include/head.h"
int main(int argc,char* args[])
{
if(argc >1)
{
for(int i=1;i<argc;i++)
{
printf("第%d个参数=%s\n",i,args[i]);
}
}
int ret1 = add(10,10);
int ret2 = mul(10,10);
int ret3 = div(10,10);
int ret4 = sub(10,10);
printf("10+10=%d\n",ret1);
printf("10*10=%d\n",ret2);
printf("10/10=%d\n",ret3);
printf("10-10=%d\n",ret4);
return 0;
}
然后在工程根目录下通过vi makefile
打开编辑文本,内容例如:
main:main.c ./src/a.c ./src/b.c ./src/d.c ./src/e.c
gcc -o main -I ./include main.c ./src/a.c ./src/b.c ./src/d.c ./src/e.c
注意命令前是一个tab键
保存退出后,输入make命令会执行makefile的内容.
可以看到make执行的内容,就是makefile定义的规则,同时在当前目录下生层了main可执行文件.
如果执行后看到Makefile:2:*** missing separator. Stop.
错误,那么很有可能是你的makefile文件在命令前的tab键格式不对,需要在vi编辑模式下按下tab,不要自己打空格.
上面这种编写方式的缺点是,如果更改其中一个文件,那么所有的源码都需要重新编译.
5.3 改进makefile文件的编写
考虑采用编译过程分解,先生成.o文件,然后使用.o文件得到结果.
修改项目结构,同时修改a.c、b.c、d.c、e.c里面的include路径
在项目根目录下重新创建一个makefile文件
main:main.o a.o b.o d.o e.o
gcc -o main -I ./include main.o a.o b.o d.o e.o
main.o:main.c
gcc -c main.c -I ./include
a.o:a.c
gcc -c a.c -I ./include
b.o:b.c
gcc -c b.c -I ./include
d.o:d.c
gcc -c d.c -I ./include
e.o:e.c
gcc -c e.c -I ./include
保存后,输入make,等到下面结果
执行main,得到结果.
这样看似和之前的没啥区别, 但是当我们修改a.c代码后, 再次执行make命令,你会发现输出的命令变少了
可以看到仅输出了2条,第一条是生成a.o的; 第2条是生成main可执行程序的, 这样就实现了增量编译
了.效率也得到了提升.
六、makefile的语法
- 1)定义变量
变量名=值 - 2)注释
#号表示注释 - 3)使用变量
$(变量名)
例如:
#定义一个变量,存放依赖的路径
objFile = main.o a.o b.o d.o e.o
#通过&(变量名)来使用变量
main:$(objFile)
gcc -o main -I ./include main.o a.o b.o d.o e.o
main.o:main.c
gcc -c main.c -I ./include
a.o:a.c
gcc -c a.c -I ./include
b.o:b.c
gcc -c b.c -I ./include
d.o:d.c
gcc -c d.c -I ./include
e.o:e.c
gcc -c e.c -I ./include
- 4)函数
wildcard:可以进行文件匹配,用法:$(wildcard 要查找的文件)
patsubst:内容替换(翻译:subst 字符串替换 . patsubst 带模式的字符串替换),用法:$(patsubst 替换前的规则,替换后的规则,需要替换的内容)
例如:
#查找当前目录下所有的.c文件,保存到SrcFiles变量中
SrcFiles=$(wildcard *.c)
#将SrcFiles变量内的.c后缀修改成.o后缀,然后保存到ObjFiles变量中,这里用到了%通配符
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
#main的依赖以及规则中用到的文件名就可以直接使用ObjFiles变量的内容了来写活了.
main:$(ObjFiles)
gcc -o main -I ./include $(ObjFiles)
main.o:main.c
gcc -c main.c -I ./include
a.o:a.c
gcc -c a.c -I ./include
b.o:b.c
gcc -c b.c -I ./include
d.o:d.c
gcc -c d.c -I ./include
e.o:e.c
gcc -c e.c -I ./include
- 5)模式推导和makefile变量结合使用
使用%号来代替符合一定规律的内容,相当于通配符, 通常会和makefile的变量一起使用,常用的makefile变量有:
$@ :代表目标
$^ :代表全部依赖
$< :代表第一个依赖,当只有一个依赖的时候用$<和$^都可以.
$? :第一个变化的依赖
注意:这些变量只能在规则中使用.
例如:
#get all c files
SrcFiles=$(wildcard *.c)
#all c files -> o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
#主目标
main:$(ObjFiles)
gcc -o main -I ./include $(ObjFiles)
#子目标:依赖 tab键规则
%.o:%.c
gcc -o $@ -c $< -I ./include
执行结果如下:
- 6)@和-符号在规则中的作用
@在规则前可以使规则中的命令不在控制台中显示, 但是还是会执行
-在规则前可以使规则中的命令执行失败时不影响makefile后面的指令执行.
例如设置一个clean目标
这样做的好处是,可以清理一些临时文件,例如.o文件,可以这样编写
#get all c files
SrcFiles=$(wildcard *.c)
#all c files -> o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
main:$(ObjFiles)
gcc -o main -I ./include $(ObjFiles)
%.o:%.c
gcc -o $@ -c $< -I ./include
clean:
-@rm -rf *.o
-@rm -rf main
可以看到mak主目标的时候会生成很多临时文件,执行make clean的时候就会清理掉这些临时文件.
- 7)设置伪目标
假设有些目标我们只是想执行一些命令,并不是用来生成某个目标文件,那么这个时候就可以定义伪目标.
用法:.PHONY:目标名1 目标名2 ...
好处:这样可以避免目标名和当前目录下存在同名的文件名起冲突.
例如,我们可以将clean目标设置为伪目标
#get all c files
SrcFiles=$(wildcard *.c)
#all c files -> o files
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
main:$(ObjFiles)
gcc -o main -I ./include $(ObjFiles)
%.o:%.c
gcc -o $@ -c $< -I ./include
#定义伪目标
.PHONY:clean
clean:
-@rm -rf *.o
-@rm -rf main
- 8)all目标
all目标不需要指定规则,它通常用来指定多个依赖,并且作为makefile的首目标, 这样在执行的make的时候就会默认指向all目标.然后就可以让多个依赖都得到执行.
通常会将all目标设置伪伪目标
例如:
#获取当前目录下所有.c文件,保存到SrcFiles变量中
SrcFiles=$(wildcard *.c)
#将变量SrcFiles中的所有字符串的.c后缀修改为.o后缀
ObjFiles=$(patsubst %.c,%.o,$(SrcFiles))
#all目标
all:main
main:$(ObjFiles)
gcc -o $@ -I ./include $(ObjFiles)
%.o:%.c
gcc -o $@ -c $< -I ./include
#all目标也设置为伪目标,多个伪目标空格分开
.PHONY:clean all
clean:
-@rm -rf *.o
-@rm -rf main
七、gdb调试
修改main.c的代码如下:
#include <stdio.h>
#include "include/head.h"
int main(int argc,char* args[])
{
if(argc >1)
{
for(int i=1;i<argc;i++)
{
printf("第%d个参数=%s\n",i,args[i]);
}
}
int ret1 = add(10,10);
int ret2 = mul(10,10);
int ret3 = div(10,10);
int ret4 = sub(10,10);
printf("10+10=%d\n",ret1);
printf("10*10=%d\n",ret2);
printf("10/10=%d\n",ret3);
printf("10-10=%d\n",ret4);
return 0;
}
使用gdb调试可执行程序前,可执行程序编译的时候需要带-g
选项,例如:
gcc -o main main.c a.c b.c d.c e.c -I include/ -g
然后启动gdb的命令是:gdb 可执行程序名称
,例如
gdb main
会看到这样的界面,等待用户输入调试命令
7.1 常用的调试命令介绍
- 1)run命令
用于运行程序,可以简写为r,例如: - run命令后面还可以带参数,相当于给主函数的args设置参数,例如:
- 2)start命令
让程序执行到main函数的第一条语句 - 此模式下按下n (next的简写)就会执行下一条指令,第二次的时候可以继续按n执行下一条,也可以直接按回车键表示重复上一个命令,也就是n命令.
- 如果某条语句调用了一个函数,如果想进入这个函数,那么可以按下step进入,但是系统的库函数进不了.
- 3)quit命令
退出gdb,也可以简写为q. - 4)set命令
set命令用于指定main函数的参数,例如
set args [参数1] [参数2] ...
启动调试后,就可以使用set命令设置参数了
使用start命令启动程序后,使用set命令还可以给变量设置值,例如:
- 5)list命令
可以简写为l, 用于查看入口函数所在的源文件的代码,默认显示10行 - 此模式下按下"回车"可以继续查看后10行的内容. 如下所示,一直按回车就可以查看所有的代码了.
- 如果想查看其他源文件的代码,可以使用
list 目标文件名:行号
来查看,表示从目标文件的第1行开始查看10行内容,例如: - 6)设置断点
用法:b 行号
就可以设置断点了,b是break的简写,这种方式设置的断点默认设置的是主函数所在的文件,例如 - 表示在第17行设置了一个断点.当设置了断点后,输入run命令后会停留在断点处.
- 此时想要执行下一步,直接按
n
键即可.用法和start命令执行下一行一样.
如果想指定文件设置断点,用法为:b 文件名:行号
例如: - 还可以设置条件断点,用法为:
b 行号 if 条件
,通常用于设置循环语句的某个条件成立时插入断点,例如: - 7)跳到下一个断点处
输出c
命令, c是continue的意思,如下所示一直按c,直到程序结束 - 8)查看断点
通过info b
命令查看所有设置的断点 - 9)删除断点
d(del)
断点的编号,例如: - 10)查看变量的值
通过p
命令,也就是print命令查看变量的值 - 如果查看int类型数据想转成八进制查看,可以用
p/o 变量名
来查看,如果变量是一个结构体或者类对象,那么可以通过p 变量名.成员属性
的方式查看结构体或者类的成员变量. - 11)查看变量的类型
- 12)display命令
用于跟踪某个变量,每次执行下一条语句的时候都会显示被跟踪的变量的值.例如: - 13)undisputed命令
用于取消跟踪某个变量,通过info dispaly可以查看当前跟踪的变量及编号,然后就可以用undisplay 编号来取消了
7.2 gdb跟踪core错误
- 1)设置生成core文件
命令:ulimit -c unlimited
(其中unlimited的意思是不限制大小,如果是具体的数字,那么大小就是具体的数值), 当设置了core之后, 程序报错就会生成一个core文件,记录错误信息.
假设main.c代码是这样的
#include <stdio.h>
#include "head.h"
int main(int argc,char* args[])
{
char *bug = "I am bug";
bug[0] = '1'; //这里会发生core错误
if(argc >1)
{
for(int i=1;i<argc;i++)
{
printf("第%d个参数=%s\n",i,args[i]);
}
}
int ret1 = add(10,10);
int ret2 = mul(10,10);
int ret3 = div(10,10);
int ret4 = sub(10,10);
printf("10+10=%d\n",ret1);
printf("10*10=%d\n",ret2);
printf("10/10=%d\n",ret3);
printf("10-10=%d\n",ret4);
return 0;
}
编译的时候需要带上-g
选项, 例如:
gcc -o main main.c a.c d.c b.c e.c -I include/ -g
main.c的代码编译没有报错,但是执行main程序的时候就core异常了,如果在此之前你设置了core跟踪,那么此时就会出现一个core文件,例如:
查看core的内容,使用命令 gdb 程序名称 core
查看,例如:
- 2)指定core文件生成的文件名
编辑/proc/sys/kernel/core_pattern
文件,不能使用vi编辑,需要切换到root用户,输入下面命令:
echo "core-%e-%p-%t" > /proc/sys/kernel/core_pattern
其中:
%p 表示添加pid
%u 表示添加当前uid
%g 表示添加当前gid
%s 表示添加导致生成core的信号
%t 表示添加core文件生成时的unix时间
%h 表示添加主机名
%e 表示添加命令名
此时再次设置core文件,并执行main程序,发生core错误时,生成的core文件命名格式就变成上面设置的格式了