程序调试教程(2)——MLE
Contents
MLE
前面一讲已经说了,MLE 代表程序运行内存超过限制。
一般来说,MLE 主要由变量占用过多内存造成,当然需要留下一点空间供栈空间和头文件中的变量使用。
首先我们要对各种类型变量的占用空间有一个清晰的了解。
常见类型的占用字节数在语法教程(1)中已经有过详细介绍,这里只补充一下没有说的指针类型。
指针类型
指针类型占用字节数跟其是哪种类型的指针无关,只跟系统的寻址能力有关。
比如在 $64$ 位系统中,int*
跟 long long*
的占用字节数均为 $8$,而在 $32$ 位系统中均为 $4$。
### 一些技巧
- 对于一些不记得占用字节数或者不确定的数据类型,可以用
sizeof
函数输出确认。- 比如
sizeof(int*)
在 $64$ 位电脑上就会输出 $8$。
- 比如
- 除此之外,在一些可能由很多数组的程序中,可以先用常量定义几个最常用的数组长度,然后将类似的数组(长度均为其倍数,类型相同)的长度相加,一起计算占用内存。
- 比如
const int N=100009;
。
- 比如
- 因为一般程序最常用
int
类型,且一般内存限制的单位为MB
,故一般就会将得到的数组长度去掉后 $6$ 位,再乘 $4$,有时可以方便计算(数组长度一般尽量取整)。 因为数组长度和内存计算的误差,以及一些编译过程中必要的内存占用,一般程序内存尽量不要超过内存限制的 $90\%$。
- 对于一些不是数组,通过
new
方法得到的变量或指针,通过计算、估计调用次数来计算占用内存,有时候需要用到时间复杂度计算和常数估计的方法。
问题处理方法
- 如果计算出来的内存远大于内存限制,思考是不是算法出现问题,需要考虑重构算法。
- 如果计算出来的内存在超出限制边缘,考虑减少不必要的数组的删去,以及部分数组长度的缩小,甚至可以考虑将一些不在同一时间段使用的数组合并成一个共用。
- 如果计算出来的内存远不到限制,检查计算方法是否有误,或者是否看错了限制,或者漏算了某种非数组型变量。
总结
对于 MLE,我们主要的策略就是要先判断是算法问题(空间复杂度超标)还是实现问题(空间常数过大),然后根据结果调整。
这类问题应该先于之后所讲的问题检查,因为 MLE 的一般程序无论实际对错,都不可能继续运行乃至获得分数。
Comments: 2
看不懂
有啥看不懂的。