近期做了浙大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