gdb调试数组访问越界记录
综述
问题来源于力扣的一道域名访问统计题目,我本想以建立首字母索引的方式去统计,但是在申请子域名buffer的地方发现使用malloc(sizeof(char)4)申请出来的buffer每次+1只能增加一个内存单元。
原题链接:子域名访问计数
结题思路

疑问点
- malloc(sizeof(char)4)在64位机上申请的内存大小不是32字节(已解决)
- 在一个给定32个字节的存放指针的内存空间怎么使用
- 概率性出现coredump (已解决)
malloc(sizeof(char)4)在64位机上申请的内存大小不是32字节
for(i = 0; i < 26; i++){
pSubDomainBuffer = (char*)malloc(sizeof(char*) * 4);
printf("pSubDomainBuffer[0] address:%p, size: %d\n", pSubDomainBuffer, sizeof(pSubDomainBuffer));
printf("pSubDomainBuffer[1] address:%p, size: %d\n", &pSubDomainBuffer[1], sizeof(pSubDomainBuffer[1]));
printf("pSubDomainBuffer[2] address:%p, size: %d\n", &pSubDomainBuffer[2], sizeof(pSubDomainBuffer[2]));
printf("pSubDomainBuffer[3] address:%p, size: %d\n", &pSubDomainBuffer[3], sizeof(pSubDomainBuffer[3]));
if(NULL == pSubDomainBuffer){
printf("failed malloc subDomain buffer.\n");
return 2;
}
memset(pSubDomainBuffer, 0x0, sizeof(char*) * 4);
pDomainBuffer[i] = pSubDomainBuffer;
}
运行结果:
pSubDomainBuffer[0] address:0x2476010, size: 8
pSubDomainBuffer[1] address:0x2476011, size: 1
pSubDomainBuffer[2] address:0x2476012, size: 1
pSubDomainBuffer[3] address:0x2476013, size: 1
百度到sizeof对于指针的的取值,64位机器上始终为8(申请内存空间为32个字节)。

在一个给定32个字节的存放指针的内存空间怎么使用
pDomainBuffer[i]存放的是4个字节的指针地址空间的内存地址,这片内存空间使用malloc申请,在访问的时候不能像普通赋值那样 pDoaminBuffer[i][j] = pLinkNode,直接赋值左侧j从2,3,4开始都是一个字节,结构体地址赋值会数据丢失。但是使用memcpy的话又会产生段错误。(遗留,我还是太弱了)
for(j = 0; j < 4; j++){
pLinkNode = (myLINKLIST *)malloc(sizeof(myLINKLIST) * 1);
memcpy(pDomainBuffer[i][j], pLinkNode, sizeof(char *));
}
2022/10/7 更新:这个地方我换了另外一种实现方式,结构体。
typedef struct LINKLIST_LEVEL{
struct LINKLIST *pLevel1;
struct LINKLIST *pLevel2;
struct LINKLIST *pLevel3;
struct LINKLIST *pLevel4;
}myLINKLIST_LEVEL;
概率性出现coredump
功能已经实现,但是在字符切割的位置,概率性会出现coredump,目前对于coredump解析只会使用bt查看core的地方,具体为什么会core还不清楚。
uiIndex = 0;
while(NULL != ppDomainNames[uiIndex]){
strDomainName = ppDomainNames[uiIndex];
while('\0' != *strDomainName){
if(*strDomainName == '.'){
strDomainName++;
printf("Domain Name: %s\n", strDomainName);
uiLevel = getDomainLevel(strDomainName);
if(0 == uiLevel){
printf("failed get domain level\n");
return 2;
}
if(1 == isExistDomainName(&pDomainBuffer, strDomainName, uiLevel))
{
if(0 != appendDomainName(&pDomainBuffer, strDomainName, uiLevel)){
printf("failed append domain name\n");
return 0;
}
}
}
strDomainName++;
}
uiIndex++;
}

根本原因:数组访问越界, strDomainName = ppDomainNames[uiIndex]; 当uiIndex = 5时,访问的地址已经不再是数组内保存的地址,而是野地址,如果这个野地址为为空,且可以访问,那么程序直接执行通过。如果野地址是非法地址,只允许特定的权限程序访问,那么这时候就会出现coredump。
char *ppDomainNames[5]={"www.baidu.com", "www.leetcode.com", "meituan.com", "disscuss.leetcode.com", "www.flask.org"};
下面记录一下gdb调试该程序的方法
- coredump环境设置
- 调试开启debug
- 设置断点
- 执行断点
coredump环境设置
- 检查环境是否有开启coredump, 返回unlimited表示开启了coredump捕捉。
检查
ulimit -c
开启
ulimit -c unlimited
- 设置coredump转储文件格式
echo "coredump_%e_%p_%t" > /proc/sys/kernel/core_pattern
%e 产生coredump的文件名
%p 产生coredump的进程PID
%t 时间戳(1970年1月1日开始以秒算)
调试开启debug
使用gdb调试程序,需要在程序的编译阶段开启debug模式,gcc开启debug模式参数为 -g
Debugging with GDB: the GNU Source-Level Debugger
4 Running Programs Under GDB
4.1 Compiling for debugging
GCC, the gnu C compiler, supports
'-g' with or without '-O', making it possible to
debug optimized code.
gcc -g test.c
设置断点
gdb打开coredump文件,显示程序是在源文件test.c的getDomainLevel的103行 if判断位置异常中断,提示strDomainName=0x34000000340是不能够访问非法地址。我在该函数入参位置处设置断点,检查每次入参的值就能判断为什么会访问到非法地址了。

入参赋值在test.c的294行,使用 break filename:linenum设置断点

- 添加断点
(gdb) break test.c:294
Breakpoint 1 at 0x40107f: file test.c, line 294.
- 查看已经添加的断点,可以看到断点已经设置好,在test.c main函数弟294行
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040107f in main at test.c:294
run命令运行程序,程序会在第一个断点位置停下
(gdb) run
Starting program: /home/slowfei/project/c/a.out
Breakpoint 1, main () at test.c:294
294 strDomainName = ppDomainNames[uiIndex];
p variable查看程序值、info break断点命中次数。 下图显示第一次命中断点:uiIndex的值为0, 断点命中次数为一次。
(gdb) p uiIndex
$1 = 0
(gdb) p ppDomainNames
$2 = {0x401421 "www.baidu.com", 0x40142f "www.leetcode.com", 0x401440 "meituan.com", 0x40144c "disscuss.leetcode.com", 0x401462 "www.flask.org"}
(gdb) p ppDomainNames[0]
$3 = 0x401421 "www.baidu.com"
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040107f in main at test.c:294
breakpoint already hit 1 time
- 指令
c继续下一次命中该断点
(gdb) c
Continuing.
Breakpoint 1, main () at test.c:294
294 strDomainName = ppDomainNames[uiIndex];
持续命中6次该断点后,程序访问到了非法地址空间,使用p 查看变量值,提示非法地址,无权限查看。按照我程序的设置是在所有数据都访问完了后,下一次没有数据可以访问了,那么这个地方的值必然NULL。
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400a66 in getDomainLevel (strDomainName=0x34000000340 <Address 0x34000000340 out of bounds>) at test.c:103
103 if(NULL == strDomainName || 0 == strlen(strDomainName)){
(gdb) p strDomainName
$5 = 0x34000000340 <Address 0x34000000340 out of bounds>
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040107f in main at test.c:294
breakpoint already hit 6 times
事先的设想是在293当uiIndex = 4的时候,所有的数组元素均被访问完毕。下一次应该为NULL。但由于自己在数组赋值的时候,5个字符串只给了5个存储空间,没有保留NULL位置。

修改:为字符串数组存储空间保留一个NULL位置
char *ppDomainNames[6]={"www.baidu.com", "www.leetcode.com", "meituan.com", "disscuss.leetcode.com", "www.flask.org"};
相关文件
源码合集
小结
毕业第一年入职智能家居,做路由器运营商平台对接,代码纯C写了一年,之后转岗测试,C语言基本也就一年左右没使用,现在使用感觉好陌生,怎么申请指针数组,给一块内存要进行指针地址赋值也差不多忘记了。难受啊
gdb调试数组访问越界记录的更多相关文章
- Linux C 程序 函数,数组,指针,gdb调试器(SEVEN)
函数,数组,指针,gdb调试器 1.函数定义 如果明确指定返回类型,默认为int 参数传递:实参对形参的参数传递是单向的,实参只是把自己的值赋给形参. 形参的 ...
- gdb调试3_显示变量 和 数组
http://www.cnblogs.com/shipfi/archive/2008/08/04/1260293.html 感谢作者! 程序变量查看文件中某变量的值:file::variablefu ...
- GDB调试字符数组时指针和数组区别的体现
测试ftell函数时发现报错,先贴源码 // File Name: ftell.c #include <stdio.h> #include <stdlib.h> int mai ...
- gdb调试常用命令
gdb 调试常用命令 gcc -g mian.c -o main.out -o (定制生成的可执行文件的名称,缺省时为a.out) -g 使gdb可调试,在编译的时候,产生调试信息 gdb main. ...
- core dump gdb调试
core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIG ...
- Linux GDB调试全面解析
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能: 启动程序,可以按照工程师自定义的要求随心所欲的运行程序. 让被调试的程序在工程师指定的断 ...
- 比较全面的gdb调试命令 (转载)
转自http://blog.csdn.net/dadalan/article/details/3758025 用GDB调试程序 GDB是一个强大的命令行调试工具.大家知道命令行的强大就是在于,其可以形 ...
- gdb调试整理
调试环境:linux调试工具:gdb 调试类别 1.调试core文件 gdb 应用程序名 core文件名2.调试正在执行的程序 gdb 应用程序名 pid 3.gdb 应用程序名 4 ...
- gdb常用命令及使用gdb调试多进程多线程程序
一.常用普通调试命令 1.简单介绍GDB 介绍: gdb是Linux环境下的代码调试⼯具.使⽤:需要在源代码⽣成的时候加上 -g 选项.开始使⽤: gdb binFile退出: ctrl + d 或 ...
- 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试
. 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...
随机推荐
- AngularJS UI
1, angular ui 自定义弹框 <script type="text/ng-template" id="stackedModal.html"> ...
- Codeforces Global Round 17 - D. Not Quite Lee
裴蜀定理 + lowbit Problem - D - Codeforces 题意 定义一个包含 \(m\) 个元素的数组 \(b\) 是好的,当且仅当满足以下两个条件 对于 \(b[i]\), 存在 ...
- PMP学习:弱势乙方如何利用阶段性需求固化搞定甲方
- Java方法之方法的重载
方法的重载 重载就是在一个类中,有相同的函数名称,但形参不同的函数. 方法的重载的规则: 1.方法名称必须相同. 2.参数列表必须不同(个数不同.或类型不同.参数排列顺序不同等). 3.方法的返回类型 ...
- Java流程控制之Scanner的进阶使用
Scanner的进阶使用 import java.util.Scanner; public class Demo04 { public static void main(String[] args) ...
- Go语言互斥锁(sync.Mutex)和读写互斥锁(sync.RWMutex)
暴力锁 package main import ( "fmt" "sync" "time" ) /* Go语言包中的 sync 包提供了两种 ...
- Unity 纯C# 完成 APK从下载到 自安装
最简单的就是用androidStudio 进行编辑,打个aar 包,在Unity中调用方法,很便捷以下内容均转载Unity论坛,Android API24版本下可用,android API 24以上版 ...
- Unity多线程使用(线程池)
1.在C#中使用线程池需要以下这个类库using System.Threading 2.开单个线程(unity程序停止前 线程一定要关闭) private Thread tempThread; voi ...
- 一文学会使用pip
pip介绍 pip的全称:package installer for python,是 Python包管理工具,该工具提供了对Python包的查找.下载.安装.卸载的功能.Python官方的PyPi仓 ...
- pg_freespacemap查看表膨胀
pg_freespacemap模块提供一种检查自由空间映射(FSM)的手段.它提供一个名为pg_freespace的函数,或精确的说是两个重载函数.该函数在一个给定的页面或关系中的所有页面的自由空间映 ...