编程-死循环
例如在C语言程式中,语句“while(1)printf(*);”就是一个死循环,运行它将无休止地列印*号。不存在一种算法,对任何一个程式及相应的输入数据,都可以判断是否会出现死循环。因此,任何编译系统都不做死循环检查。
在设计程式时,若遇到死循环,我们可以通过按下Ctrl+Pause/Break的方法,结束死循环。
然而,在编程中死循环并不是一个需要避免的问题,相反,在实际套用中,经常需要用到死循环。例如,我们使用的Windows作业系统下的视窗程式中的视窗都是通过一个叫讯息循环的死循环实现的。在单片机、嵌入式编程中也经常要用到死循环。在各类程式语言中,死循环都有多种实现的方法,以C语言为例,可分别使用while.for,goto实现。
死循环的C语言实现:
1、while(1);
2、for(;;);
3、goto
Loop:
...
goto Loop;
页面-死循环
在网站页面设计当中,导航的设计不可忽视。导航的作用除了给用户寻找相关信息文字性的提示以外。也是增加此页面连结其他页面的入口。如果此页面的导航点击进入时连结地址还是本页面,就会造成死循环。页面死循环不利于网站以及网页的最佳化。
死循环在系统的套用非常多,也非常重要,所有的套用系统都需要设定一个死循环来保证系统的正常运行,如果没有死循环,那么你会一开机马上就关机,因为这个程式已经运行完毕,所以在系统开发中死循环有着极其重要的作用!
举例
以下是一些死循环的例子。
C++的例子
#includeintmain(){for(inti=0;;i++){cout< C语言的死循环:
#includemain(){inta=0;while(a<10){printf(%d\n,a);if(a=5){printf(aequals5!\n);}a++;}return0;} 上述程式会一直显示Infinite Loop字元串。
BASIC语言的死循环:
10 PRINTInfinite Loop
20 GOTO10'跳到行号=10的位置
X86彙编语言的例子:
loop:
; Code to loop here
jmploop
Python的例子:
whileTrue:
print(Infinite Loop)
逻辑错误
以下是一个Visual Basic死循环的例子:
dimxasintegerdountilx > 5'根本不会有x>5的情形x = 1 x = x + 1loop
每一次运行循环时x会先设定为1,然后变为2,因为数值未大于5,所以永远不会退出。若将x = 1由循环内部移到循环之前即可以改善此一情形。
有些程式设计师可能因为不熟悉特定程式语言的语法而造成死循环,例如以下是一段C语言的程式:
#includemain(){inta=0;while(a<10){printf(%d\n,a);if(a=5){printf(aequals5!\n);//a设定为5,进入无穷迴圈} a++;}return0;}其预期输出是数字0至9,其中5和6中间会显示a equals 5!,但程式设计师在编写程式时将设定用的=运算符及判断相同的==运算符弄混了,因此程式会在每次运行循环时都会将a设定为5,因此变数a永远无法到达10,此循环就变成了死循环。
变数处理错误
有时不适当的循环退出条件也可能会造成无预期的死循环,例如以下C语言的例子:
floatx=0.1;while(x!=1.1){//可能会因为浮点运算的误差而出现问题printf(x=%f\n,x);x=x+0.1;}在有些作业系统中,上述程式会运行10次循环然后退出,但有些系统中,上述程式却可能会一直运行,无法退出,问题主要在循环的退出条件(x != 1.1)要在二个浮点数相等时才退出,结果会依系统处理浮点数的方式而定,只要系统运行10次循环后的结果和1.1差一点点,上述程式就会变成死循环。
若将退出条件改为(x < 1.1)就没有这个问题,程式可能会多运行一次循环,但不会变成死循环。另一种解决方式则是用一个整数变数作为循环变数,再依此变数判断是否要退出循环。
在数值分析程式中也可能会出现无预期的死循环,例如程式需一直叠代到误差小于某特定值为止,但若因为运算中的捨去误差,使得误差一直无法小于该特定值,就会产生死循环。
奥尔德森循环
奥尔德森循环(Alderson loop)是指一个循环有设定退出条件,但因为程式的写法(多半是编程错误),造成永远无法满足退出条件,在针对用户界面程式调试时最容易出现这类的问题。以下C的伪代码中有一个奥尔德森循环,程式是要计算用户输入一串数字的和,用户输入0时退出循环,但程式中用了不正确的运算符:
sum=0;while(true){printf(Input a number to add to the sum or 0 to quit);i=getUserInput();if(i*0){// 若i乘0为真,则使sum加上i的值sum+=i;// 但这不可能发生,因为不论i为何值(i * 0)都是0。如果条件中用的是!=而非*,代码就能正常运行}if(sum>100){break;// 终止循环。结束条件存在,但从来没有达到过,因为sum永远不会增加}}
“奥尔德森循环”是来自一个Microsoft Access的程式设计师,他编写的程式产生一个有模式的对话框,用户需要回应,程式才能继续运作,但对话框没有OK键或取消键,因此只要此对话窗出现,Access程式就无法继续运作。
无穷递归
无穷递归是一种由递归造成的死循环。例如以下计算阶乘的C语言程式
unsignedintfac(unsignedinta){//n!=n*(n-1)!return(fac(a-1)*a);}一般递归的程式会有一特定条件,此条件成立时直接计算结果,而不是通过递归来计算结果,若程式中未定义此条件,就会出现无穷递归。
无穷递归会造成堆叠溢出,而无穷递归不会退出,因此也是死循环的一种。不过若递归程式是使用尾部递归的处理方式,在有些程式语言(如Scheme)中会最佳化成循环,因此不会造成堆叠溢出。
上述的程式可以修改成没有无穷递归的程式。
unsignedintfac(unsignedinta){if(a==0){//定义0!=1return1;}else{return(fac(a-1)*a);}}多个模组产生的死循环
死循环也可能因为多个模组之间的互动而产生。考虑一台伺服器若收到无法理解的需求时,会回应错误信息,此架构中不会有死循环。但若有二台上述的伺服器(A和B),互相交换数据,A收到由B所提交无法理解的需求,会回应错误信息给B,但若B也无法理解A提交的需求(其实是A的错误信息),会再以自己的格式回应错误信息给,A收到后无法理解,会再回应错误信息给B……。像邮件循环就是这类的例子。
假死循环
假死循环是指一个循环看似不会退出,但只是一个运行很长时间,最后仍会退出的循环。
以下是一个C语言for循环的程式:
unsigned int i;
for(i=1;i!=0;i++)
{/* loop code */}
上述程式每次运行时都将i加1,若i等于0时才会退出循环,此程式看似不会退出,但最后还是会退出。程式中型态为unsigned int的变数,其数值有一定上限,当数值已到上限,再加1时,变数数值就会变为0,因此让程式退出。实际的上限值依系统及编译器而不同,假如unsigned int是一个16个比特的字元组,上述的循环会运行65536次。若使用高精度计算,程式会一直运行到存储器无法存储i为止。


















