Adam Dunkels的论文中提到,基于Contiki的protothread开发时要特别注意变量的生存周期,分配在栈中的自动变量,随着函数被多次返回与调用,它很容易带来错误。现在,通过分析几个实例程序来总结protothread中定义变量的规律。
如图1所示的程序用于打印当前的时钟值,可得到的结果总是一些错误值,用理解传统函数的眼光来看很难发现错误的原因,查看下图2的程序清单就可以得到更清晰的答案。
图1自动变量使用出错
从图2可以看到,第一次调用函数时,tClock已经赋值正确的时钟值,随着函数返回tClock这种栈变量的空间被回收(本质是该空间被别的变量覆盖占用);后续调用函数时tClock再次被分配空间,它包含的垃圾值被打印出来,然后被赋值正确的时钟值,再随着函数返回而回收空间,如此反复,错误就这样诞生了。
小结:一旦函数退出,自动变量的生存周期结束,它的值将是错误的。
图2绿色是首次调用,红色是后续调用,蓝色是共同调用
在上述的分析中发现protothread的自动变量容易产生错误,那么定义成static的静态变量就可以解决该问题。确实,因为static变量的生存周期不受函数返回的影响,因此不会出错。
小结:static变量在protothread的整个执行周期内都是安全的。
虽然static变量在protothread是安全的,但它会占用过多内存。举例,如果N个protothread都要分配M大小临时变量,如果都定义成static类型,那么这些变量将占用N*M大小的内存;如果定义成自动变量,那么只需要M大小的内存。
图3的代码是对图1出错代码的更改,结合图4可以看出是正确的。为什么这次自动变量tClock使用正确呢?因为它的使用范围在一个执行流之内(函数没有返回)。
小结:protothread中自动变量的安全区域是在一个执行流内,即函数不能返回。
图3自动变量使用正确
总结:在protothread中定义变量,如果该变量的使用范围需要跨越任何“阻塞进程”的函数(API包括:PROCESS_YIELD()、PROCESS_WAIT_EVENT()等),那么只能定义为static类型;否则可以使用自动变量,但仍需仔细辨别。
图4绿色是首次调用,红色是后续调用,蓝色是共同调用
这个道理是很明显的,看看图2和图4的代码就知道:一个protothread程序会预编译成带switch()的函数结构,如果用户再使用switch()结构,那将是一个多么混乱的世界!不仅编译器会报警,可能出错;开发者也很难把握程序的执行流了。替代的办法是使用if()语句,毕竟它能实现和switch()一样的功能。
免费快速下载链接:http://www.rimelink.com/nd.jsp?id=31&_np=105_315