//之前帮同事调试一段代码的时候,出现了一个让人迷惑的错误:同样的代码,不同的板卡上出现不同情况
//1:使用板卡1刷卡启动充电,刷卡停止,正常
//2:使用板卡1刷卡启动,触发急停停止后,异常,计费线程死机了
//3:使用板卡2,刷卡启动,急停停止,也正常
//常规分析,认为可能是计费线程修改了,导致线程进入死循环,而无法给线程喂狗,于是查找重载的几个计费接口,修改后没有改善
//分析,直流桩无异常,一体化有异常,是否一体化充电控制接口有异常————查看并无明显异常
//最后分析问题如下:
BYTE AxjGetStopChgReason(BYTE bpn)
{
BYTE bSotpReason;
BYTE ReasonNum = 52;
ReadItemEx(BN0, bpn, 0xB885, &bSotpReason);
for (BYTE i = 0; i < ReasonNum; i++)
{
if (g_tStopReason[i].bClou == bSotpReason)
return g_tStopReason[i].bAXJ;
}
return 0;
}
//因为,g_tStopReason在持续的修改过程中,最终并没有52个了(有些屏蔽了),导致访问到超出g_tStopReason数组外的地方————越界了
//修改为:
BYTE ReasonNum = sizeof(g_tStopReason) / sizeof(g_tStopReason[0]);
//那问题来了,一般来说,越界不是会挂掉吗?为啥这里还正常运行,直流可以而交流不行??
//下面的几点将从原理及现场说明,如果发生越界,可能的现象及后果有哪些
C中常见的与内存有关的错误
内存错误可能的后果
可能立刻报段错误,也可能保护故障,也可能长时间不出错(只是运行结果不对),甚至还有可能没有影响,程序也正常运行,那是什么原理呢?
如下一段为通用的内存管理模式:
内核虚拟内存 | 进程相关的内核数据结构 |
---|---|
如页表、task、mmu结构,内核栈等 | |
每个进程一样 | 物理内存 |
内核代码和数据 | |
用户栈 | |
… | |
… | |
共享库映射区 | 共享库如libc.so,每个库包含.data和.text区域 |
… | |
… | |
brk开始 | 运行时malloc分配的堆 |
未初始化的数据 .bss | |
已初始化的数据.data | |
0x400000开始 | 代码段.text |
如果你的内存出错了,具体出错位置:
1:地址定位到其他进程了,地址不是本进程的——segment error
2:地址定位到内核去了————内存保护错误
3:地址定位错了,但还在本进程中,只是指向了别的有用地址————这个后果不唯一,可能的后果包括:
a:引用的地址是本进程的一个堆空间的,那就会改变堆空间的地址的数据(如果应用指针有修改的话)
b:引用的地址是本进程的一个栈空间,修改了栈空间的内容,
但无论怎样,引用内存不会有致命问题(顶多引用数据不正确),修改内存可能会导致:
a:后续真正该内存的主人过来用的时候,引用地址错了,导致segment error或者其他逻辑错误
b:修改了地址的内容,后续程序运行过来的时候,数据被修改了,运行逻辑错误。
错误——间接引用坏指针
正常:scanf("%d", &val);
错误:scanf("%d", val);————此时scanf将val解释为一个地址,
最好情况下,程序立即异常终止(内存到了别的内存区),最糟糕情况下,val对应虚拟内存某个合法读/写区域,于是覆盖了这个内存,以此通常会运行相当长一段时间后造成灾难性、令人困惑的后果。
错误——读未初始化的内存
.bss内存一般被加载器初始化为0,但堆内存不是,常见错误就是假设对内存初始化为0.
int *testvec(int **ptra, int *x, int n)
{
int i,j;
int *ptrb = (int *)malloc(n * sizeof(int));
for (i=0; i<n; i++)
{
for (j=0; j<n; j++)
ptrb[i] += ptra[i][j] * x[j];
}
return y;
}
此事例中,不应该假设ptrb被初始化为0,应该显式初始化为0.
错误——允许栈缓冲区溢出
void bufoverflow()
{
char buf[32];
gets(buf);
return ;
}
//此处,gets获得数据可能超过32字节溢出,应该改用限制输出长度的接口,如fgets
错误——价值指针和他们指向对象大小相同
//创建一个n个指针组成数组,每个指针指向一个包含m个int的数组
int **makArray1(int n, int m)
{
int i;
int **A = (int **)malloc(n * sizeof(int));//(n * sizeof(int *))
for (i=0; i<n; i++)
A[i] = (int *)malloc(m * sizeof(int));
return A;
}
//int **A = (int **)malloc(n * sizeof(int));实际创建的是int数组,在int和指向int的指针大小相同时运行正常,如果64位机器就结果异常了
//A[i]超出了,但可能超出部分是一个分配块边界,所以可以正常运行,之后程序运行很久后释放块时,分配器(alloc)合并空间失败,没有明显原因,可能提示“action at distance” (在远处起作用)
错误——造成错位
int **makArray2(int n, int m)
{
int i;
int **A = (int **)malloc(n * sizeof(int *));
for (i=0; i<=n; i++)
A[i] = (int *)malloc(m * sizeof(int));
return A;
}
//A[i]初始化试图初始化n+1个元素
错误——引用指针,而不是指针指向的对象
int *binheapdel(int **binheap, int *size)
{
int *packet = binheap[0];
binheap[0] = binheap[*size -1];
*size--; //符号优先级,(*size)--
heapify(binheap, *size, 0);
return (packet);
}
//此处,*和--优先级一样,从右向左结合,所以*size--是减少指针自己值,而不是指向整数的值
//幸运的话程序立即失败,更可能发生的是:程序运行很久才有一个不对的结果,此时检查无从查起。
错误——误解指针运算
//忘记了指针的算术运算是以它指向的对象的大小为单位进行的,单位不一定是字节
int *search(int *p, int val)
{
while(*p && *p!=val)
p += sizeof(int); //p++
return p;
}
//每次循环,P越过4个int,实际结果不正确。
错误——引用不存在的位置
int *getptr()
{
int val=100;
return &val;
}
//返回的指针指向栈的局部变量,然后d出栈帧,此时指针还是一个合法地址,但不是合法变量了,后续该帧帧被其他函数使用,此处返回的值如被修改就可能导致莫名修改,轻则异常,潜在灾难性后果啊
错误——引用空闲栈块中的数据
int *heaptest(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]++; //x已经释放了
return y;
}
//释放后,x[i]可能是某个其他函数申请的堆空间(当然如果内存很小,也可能是某个函数用的一个大的栈),x位置被重写了,此错误会在程序执行后面出现。
错误——引起内存泄漏
void leak(int n)
{
int *x = (int *)malloc(n * sizeof(int));
return ;
}
//内存泄漏是缓慢、隐形的杀手
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)