2. For 循环的隧道
数据传入传出循环结构可以通过移位寄存器(Shift Register)和隧道(Tunnel)两种方式。隧道又有两种类型:带索引的和不带索引的。
移位寄存器一般用在需要局部变量的情况下,循环运行一次的输出数据要作为下次运行的输入数据使用;循环外的数组数据通过带索引的隧道在循环体内就可以直接得到数组元素;除此之外,简单地在循环内外传递数据,使用一般的隧道就可以了。
值得一提的是,如果一个数据传入循环体,又传出来,那么就应该使用移位寄存器或带索引的隧道来传递这个数据,尽量不要使用不带索引的隧道。因为 For 循环在运行时,循环次数有可能为0。在循环次数为0时,大多数情况,用户还是希望传出循环的数据就是传入值,但使用不带索引隧道时,输入值有时会被丢失的。如果使用移位寄存器,即使循环次数为0,也不会丢失传入的数据。因为移位寄存器在循环上的两个接线柱指向的实际是同一块内存(参考:LabVIEW 程序的内存优化),而输入输出两个隧道指向的是不同的内存,数据不一定相同。
图2:For 循环上的隧道
图2中的程序, vi reference 传入,再传出循环均使用了隧道。如果循环次数为0(Controls数组为空),vi reference 再传出循环时,信息就丢失了。这不但有可能造成后续程序的错误,而且由于 vi reference 的信息丢失,再无法关闭打开的 vi,造成了程序泄漏。
Error 数据线(黄绿色的粗线)在传入传出数组时,一定要使用移位寄存器。原因还不仅是为了防止在循环次数为0时,错误信息丢失。通常一个节点的 Error Out 有错误输出,意味着后续的程序都不应该执行。在错误的情况下继续执行程序代码,风险非常大,可能会引起程序,甚至系统崩溃。只有使用移位寄存器,某次循环产生的错误才会被传递到后续的循环中,从而及时阻止后续循环中的代码被运行。
3. 循环次数
LabVIEW 的 For 循环与其它语言相比,有一大特点是在某些情况下不一定要输入循环次数,For 循环可以根据输入数组的大小决定循环次数。把一个数组通过带索引的隧道把数组分解成元素传递到循环体内,同时不要设置循环次数N,这时,循环的次数就是数组的长度。每次循环,带索引的隧道给出一个元素。
容易引起错误的情况是,循环体上有两个或更多的输入数组使用了带索引的隧道。这时,循环的次数是几个数组中长度最短的那个数组的长度。如果还设置了循环次数N,那么循环的循环次数是N、输入数组长度中最小值。在调试的时候,如果发现一个本该运行多次的循环没有运行,那么很可能就是因为它的一个输入数组是空的。
While 循环同样也可以使用带索引的隧道,但是我不建议大家这么用——如果需要用到带索引的隧道,那么就使用 For 循环。原因是 while 循环的循环次数不由数组个数决定,而是由停止条件决定的。如使用带索引的隧道,你还需要考虑在数组大于、小于循环次数时,程序如何处理,这样还是在循环体内作索引比较方便。如果希望循环次数与数组大小保持一致,那自然是用 For 循环,程序更加清晰易懂。
4. 移位寄存器的初始化
图3:没有初始化的移位寄存器
看图3中这个程序,因为它在 while 循环上使用了带索引的隧道,所以可读性不那么好。array out 的运行结果是什么,还真的想一阵才能给出答案。实际上这个程序,即使输入不变,每运行一次,array out 的结果都不一样,他的长度一致在增加。这个问题就出在没有给程序中的移位寄存器一个初始值。
没有初始化的移位寄存器,总是保存上次运行结束时的数据。这点在某些情况下可以别程序员利用,比如用它当作全局变量,随时把数据存入或取出。(一个例子是《如何使用 VI 的重入属性》中的图4)但多数情况下移位寄存器还是被用作为循环内部的局部变量的,这是就一定要对它初始化,以防止潜在的错误。