C语言程序内存泄漏修复及数组越界无警告问题咨询
C代码内存泄漏与数组越界问题修复
一、原解析代码
#include <stdio.h> #include <stdlib.h> #include<string.h> #include<ctype.h> #include<limits.h> #include<uchar.h> #include<assert.h> #define MAXW 128 #define MAXC 112 int main(int argc,char **argv) { int readdef = 0; /* flag for reading definition */ size_t offset = 0 /* offset for each part of definition */ ,len = 0; /* length of each line */ char buf[MAXC] = ""; /* read (line) buffer */ char word[MAXW] = ""; /* buffer storing word */ char defn[MAXC] = ""; /* buffer storing definition */ /* open filename given as 1st argument, (or read stdin by default) */ FILE *fp = argc > 1 ? fopen(argv[1],"r") : stdin; if(!fp){ /* validate file open for reading */ fprintf(stderr,"error: file '%s' open failed\n",argv[1]); exit(EXIT_FAILURE); } while(fgets(buf,MAXC,fp)) { char *p = buf; /* pointer to parse word & 1st part of defn */ if(*buf == '\n') { defn[offset-1] = 0; printf("defn:%s\n\n",defn); readdef = 0; offset= 0; } else if(readdef == 0) { while(*p && *p != '.') p++; if(p-buf+1 > MAXW){ fprintf(stderr,"error: word exceeds %d chars.\n",MAXW-1); exit(EXIT_FAILURE); } snprintf(word,p-buf+1,"%s",buf); /* copy to word */ printf("word=%s|\n",word); while(ispunct(*p) || isspace(*p)) p++; len = strlen(p); if(len && p[len-1] == '\n') p[len-1] = ' '; strcpy(defn,p); offset +=len; readdef = 1; } else{ len = strlen(buf); /*get the line length */ if(len && buf[len-1] == '\n') /* chk \n, overwrite w/' ' */ buf[len-1] = ' '; if(offset + len + 1 > MAXC){ fprintf(stderr,"error: definition exceed %d chars\n",MAXC-1); // free(buf); exit(EXIT_FAILURE); } snprintf(defn+offset,len+1,"%s",buf); /* append to defn */ offset += len; /*update offset*/ } } if(fp != stdin) fclose(fp); defn[offset - 1] = 0; printf("defn: %s\n\n",defn); exit(EXIT_SUCCESS); }
二、MAXC不足时的内存泄漏问题
泄漏现象
当MAXC取值较小时,valgrind检测到仍有内存未释放:
error: definition exceed 111 chars ==28017== ==28017== HEAP SUMMARY: ==28017== in use at exit: 472 bytes in 1 blocks ==28017== total heap usage: 3 allocs, 2 frees, 5,592 bytes allocated ==28017== ==28017== 472 bytes in 1 blocks are still reachable in loss record 1 of 1 ==28017== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==28017== by 0x48ED6CD: __fopen_internal (iofopen.c:65) ==28017== by 0x48ED6CD: fopen@@GLIBC_2.2.5 (iofopen.c:86) ==28017== by 0x109285: main (in /home/jian/helloc/a.out) ==28017== ==28017== LEAK SUMMARY: ==28017== definitely lost: 0 bytes in 0 blocks ==28017== indirectly lost: 0 bytes in 0 blocks ==28017== possibly lost: 0 bytes in 0 blocks ==28017== still reachable: 472 bytes in 1 blocks ==28017== suppressed: 0 bytes in 0 blocks ==28017== ==28017== For lists of detected and suppressed errors, rerun with: -s ==28017== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
原因分析
程序检测到定义长度超限后直接调用exit退出,但如果是通过命令行参数打开的文件(fp != stdin),文件指针未被关闭,导致文件关联的内存资源未释放。
修复方案
在所有触发exit的分支前,先关闭已打开的文件指针:
// 单词长度超限分支修改 if(p-buf+1 > MAXW){ fprintf(stderr,"error: word exceeds %d chars.\n",MAXW-1); if(fp != stdin) fclose(fp); // 新增关闭文件操作 exit(EXIT_FAILURE); } // 定义长度超限分支修改 if(offset + len + 1 > MAXC){ fprintf(stderr,"error: definition exceed %d chars\n",MAXC-1); if(fp != stdin) fclose(fp); // 新增关闭文件操作 exit(EXIT_FAILURE); }
三、首行空换行时的数组越界问题
问题场景
当输入文件dat/definitions.txt首行为空换行时,代码执行defn[offset-1] = 0,此时offset值为0。由于offset是无符号的size_t类型,offset-1会溢出为极大值,导致访问defn数组的越界位置,触发未定义行为。
GCC未警告的原因
GCC默认静态分析不会检测这种仅在运行时触发的动态越界,尤其是涉及无符号整数溢出的场景。需要开启更高等级的编译警告才能触发提示,比如添加编译选项:-Wall -Wextra -Warray-bounds,其中-Warray-bounds会对数组越界做更严格的检查。
修复方案
在执行数组操作前,先判断offset是否大于0,避免越界:
// 空行处理分支修改 if(*buf == '\n') { if(offset > 0) { // 新增判断 defn[offset-1] = 0; printf("defn:%s\n\n",defn); } readdef = 0; offset= 0; } // 文件末尾处理修改 if(offset > 0) { // 新增判断 defn[offset - 1] = 0; printf("defn: %s\n\n",defn); }
内容的提问来源于stack exchange,提问作者jian
相关产品推荐
相关产品推荐

