文章目录
在日常开发中,与内存有关的错误最令人烦恼。出现内存问题,程序经常在距离错误源一段距离之后才表现出来。下面列举并分析了与内存有关的几种错误:
一、间接引用非法指针
如果间接引用一个指向没有任何意义的数据的指针,那么操作系统会以段错误异常终止程序
例如:
scanf("%d", value);
这种情况下,scanf将把value内容认为是一个地址,并试图将一个整型数据写到这个位置。这会导致程序出现异常,立即终止。
二、操作未初始化的内存
int *func(int **A, int *x, int n)
{
int i, j;
int *p = (int *)malloc(n * sizeof(int));
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
p[i] += A[i][j] * x[j]
}
}
return y;
}
示例中新申请的内存地址(p指向的地址)并没有被初始化为零应该显式地将y[i]memset为零,或者使用calloc函数申请内存。
三、栈缓冲区溢出
void func()
{
char buf[10];
gets(buf);
return;
}
这个函数没有检查输入字符串的大小就写入栈中目标缓冲区,会出现缓冲区溢出错误,因为gets函数只是简单复制一个任意长度的字符串到缓冲区,不限制输入串的大小。解决这个问题的方法是,可以用限制了输入串大小的fgets函数。
四、指针以及指针指向的对象大小不一导致堆溢出
常见的错误是,假设指向对象的指针和它们所指向的对象是相同大小的,示例程序:
int **makeArray(int n, int m)
{
int i;
int **A = (int **)malloc(n * sizeof(int));
for(i = 0; i < n; i++)
{
A[j] = (int *)malloc(m * sizeof(int));
}
return A;
}
此程序的目的是创建一个由n个指针组成的数组,每个指针都指向一个包含m个int类型数据的数组。然而,然后程序在初始化中将sizeof(int *)写成了sizeof(int),代码实际上创建的是一个int的数组,而非设计程序时候设想的有n个int类型的指针。因此这段代码只有在int和指向int的指针大小相同的机器上运行良好,否则就会出现错误。
五、循环边界导致内存越界
int **makeArray(int n, int m)
{
int i;
int **A = (int **)malloc(n * sizeof(int *));
for(i = 0; i <= n; i++) /* 注意循环终止条件 */
{
A[j] = (int *)malloc(m * sizeof(int));
}
return A;
}
程序循环最后一步,由于边界值不正确,会试图初始化数组的第n+1个元素,这个过程会覆盖A数组后面的某个内存位置。
六、操作符的优先级导致操作指针和指针指向的对象
如果不太注意C操作符的优先级和结合性,我们就会错误地操作指针或者指针所指向的对象。
例如:减少某个指针指向的整数的值的代码书写如下:
*ptr--;
然而,由于一元运算符“--”
和“*”
的优先级相同,且从右向左结合。那么上述代码实际的效果为*(ptr--)
,即减少的是指针自己的值,移动了指针地址,而不是它所指向的整数的值。
修正后的代码如下:
(*ptr)--;
七、指针运算单位出错
指针的算术运算操作是指针指向的对象的大小为单位进行的,不一定一定是一个字节
例如,循环一个int类型数组,并返回一个val首次出现的指针:
int *func(int *p, int val)
{
while(*p && *p != val)
{
p += sizeof(int);
}
return p;
}
如果是64位的机器,每次循环时,都把指针p加了4(一个int类型的字节数),而int类型指针是8个字节,这样p指针就移动到4个int数据后,不正确地扫描了数组中每4个整数
八、操作不在生命周期内的局部变量
int *func()
{
int val;
return &val;
}
这个函数返回一个指针(假设为ptr),指向栈里的一个局部变量,然后弹出它的栈帧。尽管ptr仍然指向一个合法的内存地址,但它已经不再指向一个合法的变量了。
以后在程序中调用其他函数时,内存将重用它们的栈帧。如果程序赋值给*ptr,那么它可能实际上正在修改另一个含的栈帧中的数据,从而潜在地带来灾难性的后果。
九、引用经被释放了的堆块中的数据
int *func(int n, int m)
{
int i;
int *x, *y;
x = (int *)malloc(n * sizeof(int));
free(x);
y = (int *)malloc(m * sizeof(int));
for(i = 0; i < m; i++)
{
y[i] = x[i]++;
}
return y;
}
在free(x)后操作x[i],其内容可能已经是某个其他已分配堆块的一部分了,导致程序运行结果与预期不符合,出现错误
十、内存泄漏
当开发时候不小心忘记释放已分配的内存块,而在堆里创建了垃圾时,会发生内存泄漏
void func(int n)
{
int *x = (int *)malloc(n * sizeof(int));
return;
}
如果经常调用这个函数,渐渐地堆里会充满了垃圾,造成内存泄漏。另外,有时也会引起程序终止或其他问题。