综述
问题来源于力扣的一道域名访问统计题目,我本想以建立首字母索引的方式去统计,但是在申请子域名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语言基本也就一年左右没使用,现在使用感觉好陌生,怎么申请指针数组,给一块内存要进行指针地址赋值也差不多忘记了。难受啊
浙公网安备 33010602011771号