PROCESS_YIELD()宏和C语言的switch语句< contiki学习笔记之七>
写在前面: 按照main()函数的代码一行一行的分析,该是看到了 etimer_process 这个位置。但是etimer_process实现里的一个宏 PROCESS_YIELD()引出了很多故事,于是单独把整个宏的东西整理成笔记,贴出来,和学习contiki的伙伴分享。
在说这个宏之前,得先记下c 语言的switch()遭遇。
switch()从表面上来看,或许应该是非常简单的问题--C语言的基本功吧。它的使用方式,按照常规来说,如下图所示:

好吧,那就贴一段常规的代码:
int main(void)
{
int k = ;
switch(k) {
case : printf("good job!\n");break;
case : printf("hello world!\n");break;
default:break;
}
return ;
}
很规矩的一段switch()的代码,几乎是所有C语言书上的教程。输出结果就不再说了。Hello world 嘛。
再来看一段:
int main(void)
{
int k = ;
switch(k) {
case :
do{
printf("good job !\n");
case : printf("Hello world!\n");
}while();
};
return ;
}
这段呢? 能编译通过吗?能执行吗?或者,结果如何?
在QQ群里问了一些,众说纷纭,其中有怀疑者,有否定者,还有猜测者。最难堪的是,有大牛知道这个程序的原理,然后直接让我看C语言书..
在这种鄙视下,果断的使用以下这个命令:
gcc -S test.c
生成了 test.s文件
打开如下:
.file "test.c"
.section .rodata
.LC0:
.string "good job !"
.LC1:
.string "Hello world!"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset
.cfi_offset , -
movq %rsp, %rbp
.cfi_def_cfa_register
subq $, %rsp
movl $, -(%rbp)
movl -(%rbp), %eax
testl %eax, %eax
je .L3
cmpl $, %eax
je .L4
jmp .L2
.L3:
movl $.LC0, %edi
call puts
.L4:
movl $.LC1, %edi
call puts
.L2:
movl $, %eax
leave
.cfi_def_cfa ,
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.2 20131212 (Red Hat 4.8.2-7)"
.section .note.GNU-stack,"",@progbits
好吧,简略的说下这个汇编代码,其中 .LC0 .LC1里面放了我们要打印的字符串。第10行开始是main()的入口地址。然后下面的第26行.L3 和第29行.L4,就是分别打印了.LC0和.LC1里面的字符串。
像C语言一样,从Main()开始看吧 <中间杂乱的就略过了>,
第21行, testl 指令开始测试 %eax是正数负数还是0,然后下一条 je 指令,在汇编中表示相等/为0 的话就跳转到某个地方去,这里就跳转到.L3里面去。
第23行,cmpl 指令,就是检测 %eax 寄存器的值是否为 1,。若是,则跳转到某个地方去,这里就跳转到.L4里面去。
第31行和36行,都call 了 puts函数,其实就是gcc 把printf()偷换成了puts吧。
解释的就这么多,其他无非就是push pop的操作。这是汇编最爱干的事情。
另外就是,上面的解释中,不断的使用了 "跳转" 一词,那么,还有一个关键字,在C语言中也是跳转功能,那就是 goto。
那么,按照上面的思路来说,switch()语句的底层实现,其实就是一个goto原理:先是判断一个值,然后根据这个值跳到某个地方去,这个地方用标签标出来。那么switch(k)这个时候就是在对值进行判断,case 就是那个标签,至于其中goto 被隐含了。一般的人不知道,但是编译器知道---这就足够了。
一言以蔽之: switch() = 判断 + Lable + goto. --->case语句随便写在switch(){}里面的某个位置,都无所谓了(如果没有break 和return的话)。
那么,这还是教科书上的switch()吗? 答案有待商榷。但是,按照这段汇编,我们上面那段怪怪的C语言,结果有了,当K = 1的时候,"Hello world";当k = 0的时候,"good job" "Hello world". 现在是case 0语句里面套了一个case 1语句,若有兴趣,可以在case 1里面再套一个case 2子句,然后把k = 2 看看结果如何....
好吧,switch()就先说在这里。接下来看PROCESS_YIELD()宏的展开。
--------------------------------------------------------------------------------------------------
PROCESS_YIELD()宏
contiki/./core/sys/process.h:
#define PROCESS_YIELD() PT_YIELD(process_pt)
PROCESS_YIELD 被 PT_YIELD替换掉:
PT_YIELD(pt)
contiki/./core/sys/pt.h
#define PT_YIELD(pt) \
do { \
PT_YIELD_FLAG = ; \
LC_SET((pt)->lc); \
if(PT_YIELD_FLAG == ) { \
return PT_YIELDED; \
} \
} while()
先把其中的 LC_SET()宏也打开吧:
contiki/./core/sys/lc-switch.h
#define LC_SET(s) s = __LINE__; case __LINE__:
回溯一下,PROCESS_YIELD()宏被替换成这个样子:
do {
PT_YIELD_FLAG = ;
(process_pt)->lc = __LINE__;
case __LINE__:
if(PT_YIELD_FLAG == ) {
return PT_YIELDED;
}
}while();
说明:
PT_YIELD_FLAG 这个是在 PROCESS_BEGIN()宏中的产物,一个char 型变量,在PROCESS_BEGIN()中被初始化成了 1。当然在PROCESS_END()里面也有它,并把它置为0了。当然,在PROCESS_YIELD()这里也有它。
(process_pt)->lc = __LINE__; 就是把程序当前行给保存下来了。<但并没有保存现场>
case __LINE__: 这个肯定要和switch()配合用,而且,当switch()的测试值为这个 __LINE__的时候,就跳到这个case里面去执行。
if(PT_YIELD_FLAG == 0) {
return PT_YIELDED;
}
这就没解释的必要了,无非就是在某种条件下,是继续执行程序还是直接返回去了。
但,就上面展开的8行代码,铺陈开了故事的所有脉络....
关键字: contiki switch 汇编
PROCESS_YIELD()宏和C语言的switch语句< contiki学习笔记之七>的更多相关文章
- C语言-switch语句
switch (表达式的值) { case 1: 语句1 break; case 2: 语句2 break; case 3: 语句3 break; case 4: 语句4 break; ...... ...
- PROCESS_YIELD()宏使用及过程分析<contiki学习笔记之八>
好吧,昨晚上研究了switch()的底层实现原理--发现它并不是一般C语言教科书上那样所言,当然,这对于本身就非常熟悉汇编的同学来说,是小菜一碟.世界上,很多事情是巧合与必然的结合体,没有无缘无故的爱 ...
- Go语言核心36讲(Go语言基础知识四)--学习笔记
04 | 程序实体的那些事儿(上) 还记得吗?Go 语言中的程序实体包括变量.常量.函数.结构体和接口. Go 语言是静态类型的编程语言,所以我们在声明变量或常量的时候,都需要指定它们的类型,或者给予 ...
- Go语言核心36讲(Go语言基础知识六)--学习笔记
06 | 程序实体的那些事儿 (下) 在上一篇文章,我们一直都在围绕着可重名变量,也就是不同代码块中的重名变量,进行了讨论.还记得吗? 最后我强调,如果可重名变量的类型不同,那么就需要引起我们的特别关 ...
- Go语言核心36讲(Go语言进阶技术十三)--学习笔记
19 | 错误处理(上) 提到 Go 语言中的错误处理,我们其实已经在前面接触过几次了. 比如,我们声明过error类型的变量err,也调用过errors包中的New函数. 我们说过error类型其实 ...
- 从实例学习 Go 语言、"基础与进阶" 学习笔记及心得体会、Go指南
第一轮学习 golang "基础与进阶"学习笔记,Go指南练习题目解析.使用学习资料 <Go-zh/tour tour>.记录我认为会比较容易忘记的知识点,进行补充,整 ...
- JavaScript权威设计--JavaScript表达式与运算符,语句(简要学习笔记六)
1.delete是一元操作符,用来删除对象属性或者元素. var a={ x:1, y:2 } delete a.x; //删除x属性 “x”in a //false:a对象中已经不存在x属性 ale ...
- 01.C语言关于结构体的学习笔记
我对于学习的C语言的结构体做一个小的学习总结,总结如下: 结构体:structure 结构体是一种用户自己建立的数据类型,由不同类型数据组成的组合型的数据结构.在其他高级语言中称为记录(record) ...
- 【百度文库课程】Java语言基础与OOP入门学习笔记一
一. Java的历史与由来 原名Oak,针对嵌入式系统开发设计,语法与C/C++基本一致 二. Java语言特点 Java由四方面组成:Java编程语言.Java类文件格式.Java虚拟机和Java应 ...
随机推荐
- LeetCode Contains Duplicate II (判断重复元素)
题意:如果有两个相同的元素,它们之间的距离不超过k,那么返回true,否则false. 思路:用map记录每个出现过的最近的位置,扫一边序列即可.扫到一个元素就判断它在前面什么地方出现过.本题数据有点 ...
- CentOS SVN服务器安装配置小记
SVN的安装 安装很简单,尤其对于CentOS这种,直接: # yum install subversion# yum install mod_dav_svn 不同发行版的Package安装方法参见h ...
- css的框架——global.css
global.css,一般这个css文件是用于装全站主要框架css样式代码. “global”翻译为全局.全部.从翻译中大家也能理解global.css用于做什么.大站常常用于装全站公共的CSS样式选 ...
- Informatica 9.1常用查询
select a.mapping_name, a.mapping_id, a.subject_id, a.is_valid, b.pv_precision, c.pv_value, b.pv_defa ...
- mysql索引与优化
mysql 索引与优化 http://www.cnblogs.com/hustcat/archive/2009/10/28/1591648.html
- String.valueOf()
1. 由 基本数据型态转换成 String String 类别中已经提供了将基本数据型态转换成 String 的 static 方法 也就是 String.valueOf() 这个参数多载的方法 有下 ...
- IOS 第三方开源库记录
网易客户端使用 1.ZipArchive 2.wax 3.TTTAttributedLabel 4.SSKeychain 5.SDWebImage 6.RegexKitLite 7.pop 8.NJK ...
- 约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
以数组的方法: public static void main(String[] args) { final int n = 10; final int k = 1; ...
- linux-制作linux启动U盘
1. 使用的制作工具 Ø 下载需要制作启动盘的linux的iso文件 Ø 制作启动盘的软件linux usb creater Ø U盘(大小差不多需要4G的空间) 软件可以的下载的地址:http:// ...
- 限制波尔兹曼机(Restricted Boltzmann Machines)
能量模型的概念从统计力学中得来,它描述着整个系统的某种状态,系统越有序,系统能量波动越小,趋近于平衡状态,系统越无序,能量波动越大.例如:一个孤立的物体,其内部各处的温度不尽相同,那么热就从温度较高的 ...