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 ...
随机推荐
- java 守护线程的关闭
在进程内所有用户线程 全部消亡后,如果 守护线程仍在执行 ( 注意: 守护线程并不是一直运行中,守护线程中的代码执行完毕,则守护线程自然消亡. ),则会被强制消亡.
- 慢SQL
慢SQL 目录 慢SQL 一.慢SQL概念 2.参数 3.配置 3.1.slow_query_log 3.2.slow_query_log_file 3.3.long_query_time 3.4.l ...
- 89. 格雷编码 (Medium)
问题描述 89. 格雷编码 (Medium) n 位格雷码序列 是一个由 2ⁿ 个整数组成的序列,其中: 每个整数都在范围 [0, 2ⁿ - 1] 内(含 0 和 2ⁿ - 1) 第一个整数是 0 一 ...
- 【python学习】数据结构第一节
1.访问元组或列表中的值 (注:元素是从0开始从左往有编号的,或者从右向左编号[-1开始]) 列表 x1 = [1,2,3,4] 元组 x2 =(1,2,3,4) print(x2[0]) ...
- PaddleOCR(PaddleHub Serving)离线部署包制作
PaddleOCR(PaddleHub Serving)离线部署包制作 环境与版本: 系统 CPU架构 Anaconda3 PaddlePaddle PaccleOCR 银河麒麟Server V10 ...
- HTTP相关知识学习
五层网络模型 应用层 包括HTTP,FTP... HTTP支持客户/服务器模式 简单快速有getpost方法 灵活,可以传输任意类型 无连接,每次连接只处理一个请求,收到应答就关闭 无状态,对于事物处 ...
- python import导入失败 相对路径 绝对路径
首先我们有这样一个目录结构 经过我无数次的调试,我发现从test3调用spider以及downloadss文件可以直接import调用 但是从外部的app.py文件调用,spider.py文件死活调用 ...
- CCF 201803-4 棋局评估
一. 对抗搜索的适用范围 在博弈论题目中,如果决策双方的获胜条件是截然相反的,即一方要求得分越高越好,另一方要求得分越低越好,这时我们就可以用上对抗搜索算法. 二.对抗搜索的主要思想 对抗搜索的核心思 ...
- CSS中Position几种属性的总结
定位position 定位方向:left right top bottom 静态定位:static 默认值,就是文档流 绝对定位:absolute 特点: 1.不占据原来的位置(脱标) ...
- ajax异步参数详解及alax错误信息error分析
一.$.ajax()的参数列表 ↑ 下面是Jquery中AJAX参数详细列表: 参数名 类型 描述 url String (默认: 当前页地址) 发送请求的地址. type String (默认: & ...