最近做了浙大oj的第1011道题,遇见一件奇怪的事。这道题我用c++和php做,提交后都正确。但是用完全相同的逻辑改写成python代码提交后却产生了Non-zero Exit Code的判题结果。python的这一判题结果大多表示程序在运行过程发生了未捕捉的异常。经反复提交排查后确定未捕捉的异常是IndexError,也就是数组下标越界异常。进一步排查后确定应该是因为在测试用例的树结点中有超过第一行所给的参数k的范围的字母存在。解决方法是在使用结点作为的下标之前先判断该下标是否越界,若越界,则直接return 0。
但是让我奇怪的是为什么这一错误没有在c++和php代码中体现出来?原因是因为在这些语言中根本没有下标越界这一说法!比如下面的c++代码是可以运行,完全没有问题的,只是输出的结果可能是不确定的而已。
#include <iostream> using namespace std; int main() { int a[2]; cout << a[-1]; return 0; }
这里数组名a其实相当于一个常量地址值,a[-1]代码表示的是a这个地址值偏移一个整型变量大小的地址处的内容所表示的整型值。同样下面的php代码也是可以运行的:
<?php $a = [1,2]; print $a[-1]; ?>
输出的结果是一个字符串空串,也许在php中,对于下标越界的元素默认返回空串。
也就是说c++和php代码能通过完全靠的是“幸运的巧合”,c++中越界地址处恰好储存着null,php则恰好返回空串,这样的值在后面的判断中恰好能够使得函数最终返回正确的结果。而事实是,在c++和php代码中同样是需要检查是否越界的!
这件事让我重新认识到了主动检查一些异常,做逻辑保护的重要性。像python,java这类较健壮的语言,对于程序运行时的异常还会自动检测和抛出。而像c和c++这类语言则很可能让程序在错误的状态下“悄无声息”地运行,这样在得到错误的结果后,去确定错误的地方都很难。编程需要谨小慎微,用户输入的数据是什么都有可能,只有尽可能提前考虑周全,才能避免日后bug的发生。
最后附上我的zoj1011的python代码:
import sys class React: leftSig = 0 rightSig = 0 def __init__( self, leftSig, rightSig ): self.leftSig = leftSig self.rightSig = rightSig reactTable = [ [ [] for col in range(10) ] for row in range(15) ] tree = [ 0 for x in range(2100) ] signalNum = 0 acceptedNum = 0 elementNum = 0 nodeN = 0 def readTable(): global reactTable, tree, signalNum, acceptedNum, elementNum, nodeN for i in range(signalNum): for j in range(elementNum): line = sys.stdin.readline().strip() a = line.split() reactTable[i][j] = [] for tmp in range( 0, len(a), 2 ): reactTable[i][j].append( React( int(a[tmp]), int(a[tmp+1]) ) ) def readTree( level ): global reactTable, tree, signalNum, acceptedNum, elementNum, nodeN nodeN = 0 for i in range(level+1): line = sys.stdin.readline().strip() a = line.split() for tmp in a: tree[nodeN] = tmp nodeN += 1 def displayTableAndTree( level ): global reactTable, tree, signalNum, acceptedNum, elementNum, nodeN print "--------------------" for i in range(signalNum): for j in range(elementNum): for react in reactTable[i][j]: print react.leftSig, react.rightSig, print c = 0 for i in range(level+1): for j in range(1<<i): print tree[c], c += 1 print def judge( signal, eleIdx ): global reactTable, tree, signalNum, acceptedNum, elementNum, nodeN if eleIdx >= nodeN and signal >= signalNum-acceptedNum: return 1 if eleIdx >= nodeN: return 0 if tree[eleIdx] == '*' and signal >= signalNum-acceptedNum: return 1 if tree[eleIdx] < 'a' or tree[eleIdx] >= chr(ord('a') + elementNum): return 0 for react in reactTable[signal][ord(tree[eleIdx]) - ord('a')]: #try: if judge( react.leftSig, 2*eleIdx+1 ) and judge( react.rightSig, 2*eleIdx+2 ): return 1 #except IndexError: #return 0 return 0 kcase = 1 while 1: line = sys.stdin.readline().strip() a = line.split() signalNum = int(a[0]) acceptedNum = int(a[1]) elementNum = int(a[2]) if signalNum == 0 and acceptedNum == 0 and elementNum == 0: break if kcase > 1: print print "NTA%d:" %(kcase) readTable() while 1: line = sys.stdin.readline().strip() level = int(line) if level == -1: break readTree( level ) #displayTableAndTree( level ) if judge( 0, 0 ): print "Valid" else: print "Invalid" kcase += 1