C语言内存使用的常见问题及解决之道

本文所讨论的“内存”主要指(静态)数据区、堆区和栈区空间(详细的布局和描述参考《Linux虚拟地址空间布局》一文)。数据区内存在程序编译时分配,该内存的生存期为程序的整个运行期间,如全局变量和static关键字所声明的静态变量。函数执行时在栈上开辟局部自动变量的储存空间,执行结束时自动释放栈区内存。堆区内存亦称动态内存,由程序在运行时调用malloc/calloc/realloc等库函数申请,并由使用者显式地调用free库函数释放。堆内存比栈内存分配容量更大,生存期由使用者决定,故非常灵活。然而,堆内存使用时很容易出现内存泄露、内存越界和重复释放等严重问题。

本文将详细讨论三种内存使用时常见的问题及其对策,并对各种内存问题给出简单的示例代码。示例代码的运行环境如下:

C语言内存使用的常见问题及解决之道

二  内存问题 2.1 数据区内存 2.1.1 内存越界

内存越界访问分为读越界和写越界。读越界表示读取不属于自己的数据,如读取的字节数多于分配给目标变量的字节数。若所读的内存地址无效,则程序立即崩溃;若所读的内存地址有效,则可读到随机的数据,导致不可预料的后果。写越界亦称“缓冲区溢出”,所写入的数据对目标地址而言也是随机的,因此同样导致不可预料的后果。

内存越界访问会严重影响程序的稳定性,其危险在于后果和症状的随机性。这种随机性使得故障现象和本源看似无关,给排障带来极大的困难。

数据区内存越界主要指读写某一数据区内存(如全局或静态变量、数组或结构体等)时,超出该内存区域的合法范围。

写越界的主要原因有两种:1) memset/memcpy/memmove等内存覆写调用;2) 数组下标超出范围。

1 #define NAME_SIZE 5 2 #define NAME_LEN NAME_SIZE-1/*Terminator*/ 3 char gszName[NAME_SIZE] = "Mike"; 4 char *pszName = "Jason"; 5 int main(void) 6 { 7 memset(gszName, 0, NAME_SIZE+1); //越界1 8 gszName[NAME_SIZE] = 0; //越界2 9 10 if(strlen(pszName) <= NAME_SIZE) //越界3(注意\'=\'号) 11 strcpy(gszName, pszName); 12 13 int dwSrcLen = strlen(pszName); 14 if(dwSrcLen < NAME_SIZE) 15 memcpy(gszName, pszName, dwSrcLen); //未拷贝结束符(\'\0\') 16 17 return 0; 18 }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjzwz.html