返回总目录目录
前言
本文介绍Linux环境下的C/C++调试工具--gdb。虽然在Win或MacOS下,可以使用VS或者Xcode进行调试,但是,Linux环境是目前主流的服务器的首选操作系统。因此,在Linux命令行环境下,推荐使用gdb进行调试(也可以使用Vim等进行调试,看个人喜好),为了深入理解C/C++,特别是内存控制,学习gdb是非常有必要的。(如果没有安装,请自行安装,例如ubuntu是sudo apt install gdb)
知识储备
在看这篇文章之前,我假定您已经掌握了如下知识:
知识 | 为什么需要掌握 |
---|---|
Linux或者Unix操作环境 | 我们不在Win上玩,主要是因为,大部分生产环境系统都是Linux或者Unix的,学习知识就是要最大限度的仿真 |
C/C++基础 | 因为,我们需要在Linux环境下操纵C/C++,因此,需要有C/C++的基础 |
指针 | 主要通过gdb学习指针(内存控制),因此需要有指针的知识储备 |
程序准备
我们准备一个含有指针变量的小程序来做这个实验。通过这个程序我们最大限度的了解gdb是如何操纵和使用的。
关于指针变量的初始化
不兼容警告
int *a = 20;
warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'int' [-Wint-conversion]
因此,不要这样定义指针。我们通过,int a = 20;然后通过&a的方式取得地址。否则,很有可能出现Segmentation fault。这种错误,从另外一个层面来说的话,就是,地址指向到了非栈内存空间,例如,代码位内存空间,我们无法直接操作代码位内存空间所致。例如,char *p="llll";这个p就会记录代码位的地址,因此,无法操作p来进行偏移。(也就相当于指针指向了常量)
Segmentation fault
Segmentation fault是访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等等情况。
A segmentation fault (often shortened to segfault) is a particular error condition that can occur during the operation of computer software. In short, a segmentation fault occurs when a program attempts to access a memory location that it is not allowed to access, or attempts to access a memory location in a way that is not allowed (e.g., attempts to write to a read-only location, or to overwrite part of the operating system). Systems based on processors like the Motorola 68000 tend to refer to these events as Address or Bus errors.
Segmentation is one approach to memory management and protection in the operating system. It has been superseded by paging for most purposes, but much of the terminology of segmentation is still used, "segmentation fault" being an example. Some operating systems still have segmentation at some logical level although paging is used as the main memory management policy.
On Unix-like operating systems, a process that accesses invalid memory receives the SIGSEGV signal. On Microsoft Windows, a process that accesses invalid memory receives the STATUS_ACCESS_VIOLATION exception.
下方的程序会支出产生segmentation fault的地方。
代码
#include <stdio.h>
void change(int a,int b){
int temp=a;
a=b;
b=temp;
}
void change_p(int *a,int *b){
int temp=*a;
*a=*b;
*b=temp;
}
int main(){
int a=5;
int b=19;
// int *c=5; // 这样定义c只不过是一个16进制的数而已,因此会造成segmentation fault。
//int *d=17;//所以不要这样定义,
//这样定义的问题还有一点,就是我们认为的将十进制的数,赋值给了16进制的数字,不仅要做类型转换,还会造成segmentation fault的问题
//因为根本找不到地址指向的内容 int *d = 28;我们看到的d = 0x1c,下方有图。
change(a,b);
//change(*c,*d);//不要这样写,会产生segmentation fault
printf("a=%d\nb=%d\n",a,b);
change_p(&a,&b);
//change_p(c,d);
printf("a=%d\nb=%d\n",a,b);
return 0;
}
使用gdb
MacOS下,请使用sudo来调用gdb,或者对gdb授权。
MacOS需要授权gdb
对于MacOS下的gdb调试,需要签名,否则会报错please check gdb is codesigned - see taskgated(8)
这是因为,Darwin默认禁止调试其它进程。需要用系统信任的证书对gdb进行签名。
创建证书
1.钥匙串访问 -> 证书助理 -> 创建证书
2.名称:gdb_c
3.身份类型:自签名根证书
4.证书类型:代码签名
5.钩选:【让我覆盖这些默认设置】
6.一路继续,直到【指定证书位置的步骤】,选择【系统】并继续。
(如果报这样的错,请这样处理
“Unknown Error = -2,147,414,007” on creating certificate with Certificate Assistant)
7.找到刚刚创建的gdb_c证书并双击打开证书信息窗口,展开信任项,设置使用此证书时:为始终信任。
8.sudo codesign -fs gdb_c /usr/local/bin/gdb
使用gcc -g编译
只要通过gcc -g 文件名.c -o 文件民.out
编译的程序(也就是编译的时候,增加了-g参数,详见gcc使用那篇文件),才可以使用gdb调试gdb ./文件名.out
gdb调试工具的使用
命令缩写 | 命令全写 | 说明 |
---|---|---|
l | list | 展示源代码 |
n | next | 向下执行一行语句 |
s | step | 进入函数中调试 |
bt | 展示堆栈 | |
f | 转换回原来的函数,通过bt可以看到不同的函数的栈头f + 函数号 | |
p | 打印日志,p + 变量 | |
x | 显示从某个地址开始的几个数,并且可用指定开始地址和格式,x/3d 0x7ffffffff... | |
break | break + 行号 | 打断点 |
start | start | 开始单步调试,临时断点打在main函数内的第一个指向语句那行 |
q | quit | 退出调试 |
使用gdb产看变量和内存
计算机内存的分配
什么是堆内存
什么是栈内存
内存地址
指针变量的实质是什么东西
指针只传递地址(并且是通过16进制的方式展示的,每两个16进制的字符,对应着一个字节,就是00 - ff,表示0~255供 256个不同的字节,并且,内存地址是有编号的,这个指针,就是展示了内存地址的编号,一个整形占4个字节)
这样的化,对于a来说,a就是地址,表示的是地址指向的值,那么*a表示的就是具体的值。