程序调试教程(2)——MLE

程序调试教程(2)——MLE

Contents

MLE

前面一讲已经说了,MLE 代表程序运行内存超过限制。

一般来说,MLE 主要由变量占用过多内存造成,当然需要留下一点空间供栈空间和头文件中的变量使用。

首先我们要对各种类型变量的占用空间有一个清晰的了解。

常见类型的占用字节数在语法教程(1)中已经有过详细介绍,这里只补充一下没有说的指针类型。

指针类型

指针类型占用字节数跟其是哪种类型的指针无关,只跟系统的寻址能力有关。

比如在 $64$ 位系统中,int*long long* 的占用字节数均为 $8$,而在 $32$ 位系统中均为 $4$。

### 一些技巧

  1. 对于一些不记得占用字节数或者不确定的数据类型,可以用 sizeof 函数输出确认。
    • 比如 sizeof(int*) 在 $64$ 位电脑上就会输出 $8$。
  2. 除此之外,在一些可能由很多数组的程序中,可以先用常量定义几个最常用的数组长度,然后将类似的数组(长度均为其倍数,类型相同)的长度相加,一起计算占用内存。
    • 比如 const int N=100009;
  3. 因为一般程序最常用 int 类型,且一般内存限制的单位为 MB,故一般就会将得到的数组长度去掉后 $6$ 位,再乘 $4$,有时可以方便计算(数组长度一般尽量取整)。

  4. 因为数组长度和内存计算的误差,以及一些编译过程中必要的内存占用,一般程序内存尽量不要超过内存限制的 $90\%$。

  5. 对于一些不是数组,通过 new 方法得到的变量或指针,通过计算、估计调用次数来计算占用内存,有时候需要用到时间复杂度计算和常数估计的方法。

问题处理方法

  1. 如果计算出来的内存远大于内存限制,思考是不是算法出现问题,需要考虑重构算法。
  2. 如果计算出来的内存在超出限制边缘,考虑减少不必要的数组的删去,以及部分数组长度的缩小,甚至可以考虑将一些不在同一时间段使用的数组合并成一个共用。
  3. 如果计算出来的内存远不到限制,检查计算方法是否有误,或者是否看错了限制,或者漏算了某种非数组型变量。

总结

对于 MLE,我们主要的策略就是要先判断是算法问题(空间复杂度超标)还是实现问题(空间常数过大),然后根据结果调整。

这类问题应该先于之后所讲的问题检查,因为 MLE 的一般程序无论实际对错,都不可能继续运行乃至获得分数。

 

点赞 0

Comments: 2

Add your comment