综述

问题来源于力扣的一道域名访问统计题目,我本想以建立首字母索引的方式去统计,但是在申请子域名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环境设置

  1. 检查环境是否有开启coredump, 返回unlimited表示开启了coredump捕捉。
检查
ulimit -c
开启
ulimit -c unlimited
  1. 设置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设置断点

  1. 添加断点
(gdb) break test.c:294
Breakpoint 1 at 0x40107f: file test.c, line 294.
  1. 查看已经添加的断点,可以看到断点已经设置好,在test.c main函数弟294行
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040107f in main at test.c:294
  1. run命令运行程序,程序会在第一个断点位置停下
(gdb) run
Starting program: /home/slowfei/project/c/a.out Breakpoint 1, main () at test.c:294
294 strDomainName = ppDomainNames[uiIndex];
  1. 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
  1. 指令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调试数组访问越界记录的更多相关文章

  1. Linux C 程序 函数,数组,指针,gdb调试器(SEVEN)

    函数,数组,指针,gdb调试器 1.函数定义 如果明确指定返回类型,默认为int 参数传递:实参对形参的参数传递是单向的,实参只是把自己的值赋给形参.                      形参的 ...

  2. gdb调试3_显示变量 和 数组

    http://www.cnblogs.com/shipfi/archive/2008/08/04/1260293.html  感谢作者! 程序变量查看文件中某变量的值:file::variablefu ...

  3. GDB调试字符数组时指针和数组区别的体现

    测试ftell函数时发现报错,先贴源码 // File Name: ftell.c #include <stdio.h> #include <stdlib.h> int mai ...

  4. gdb调试常用命令

    gdb 调试常用命令 gcc -g mian.c -o main.out -o (定制生成的可执行文件的名称,缺省时为a.out) -g 使gdb可调试,在编译的时候,产生调试信息 gdb main. ...

  5. core dump gdb调试

    core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIG ...

  6. Linux GDB调试全面解析

    GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具,GDB主要可帮助工程师完成下面4个方面的功能: 启动程序,可以按照工程师自定义的要求随心所欲的运行程序. 让被调试的程序在工程师指定的断 ...

  7. 比较全面的gdb调试命令 (转载)

    转自http://blog.csdn.net/dadalan/article/details/3758025 用GDB调试程序 GDB是一个强大的命令行调试工具.大家知道命令行的强大就是在于,其可以形 ...

  8. gdb调试整理

    调试环境:linux调试工具:gdb 调试类别 1.调试core文件  gdb 应用程序名 core文件名2.调试正在执行的程序 gdb 应用程序名 pid 3.gdb 应用程序名         4 ...

  9. gdb常用命令及使用gdb调试多进程多线程程序

    一.常用普通调试命令 1.简单介绍GDB 介绍: gdb是Linux环境下的代码调试⼯具.使⽤:需要在源代码⽣成的时候加上 -g 选项.开始使⽤: gdb binFile退出: ctrl + d 或 ...

  10. 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试

    . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...

随机推荐

  1. Xpath 常用语法展示

    非标准代码处理 from lxml import etree #导入lxml 中erree模块 parser = etree.HTMLParser(encoding="utf-8" ...

  2. 使用 Nginx 如何部署 web 项目

    第一步:前往 Nginx 官方 下载 Nginx 资源包,建议下载 Stable version(长期稳定版本)  第二步:将 Nginx 压缩包解压到本地目录中(D:\Tools)  第三步:进入到 ...

  3. CG3Y/ CG3Z 程序迁移

    /usr/sap/trans/cofiles/K900075.S4Q 固定值  文件目录(AL11)下去找 C:\Users\lenovo\Desktop\publici\K900075.S4Q /u ...

  4. 关于echart折线图只有2个点时的平滑曲线问题

    问题描述: 折线图,设置平滑曲线,多个点时没有问题, 可当只有两个点(数据)的时候,这时光靠 smooth: true  就不管用了. 解决方法: 还另需设置   smoothMonotone 单调性 ...

  5. Unity 纯C# 完成 APK从下载到 自安装

    最简单的就是用androidStudio 进行编辑,打个aar 包,在Unity中调用方法,很便捷以下内容均转载Unity论坛,Android API24版本下可用,android API 24以上版 ...

  6. DDR内存256M16、512M8含义

    256M16,后面的16是代表数据位数,也可以认为是16个bit.一般一个Byte是8个bit.例如256M4的容量为256*16 bit = 4096Mb = 512MB所以 256M16和 512 ...

  7. vue3+ts+vant制作音乐播放器(进度条拖拽、倍速切换、上一曲、下一曲)完整版

    1.进度条的用的是vant的Progress组件,比手写进度条方便很多,有自带的事件 2.H5页面兼容pc 效果展示 上代码 一.template模块 <template lang=" ...

  8. 逆向学习物联网-网关ESP8266-05课程小结

    1. 移花接木 本章利用自己设计的网关代替体验系统中的网关,开启了分模块设计系统的设计模式. 2.透明传输 终端传输来的数据,以MQTT协议透明传输到云及其他订阅者,简化终端系统的设计. 3. 利用状 ...

  9. 逆向学习物联网-网关ESP8266-00课程目的

  10. Study python_03

    函数 基本思想---函数是用来重复使用的 def shili(input_): print("我了个去 %s"%input_) shili('你竟然') 当一个函数中即有默认参数, ...