有关C语言的一较高级玩法

OceanStack 发布于 2014/10/26 21:12
阅读 1K+
收藏 11

如下C函数:


int function(void) {
  static int i, state = 0;
  switch (state) {
    case 0: /* start of function */
    for (i = 0; i < 10; i++) {
      state = 1; /* so we will come back to "case 1" */
      return i;
      case 1:; /* resume control straight after the return */
    }
  }
}
实现功能调用10次,依次反回0-9 ,为什么 return i 语句后面会有个 case 1,而如果不加此句函数运行就不正确,谁能给出较详细的解答。


另外当我在main函数中如下调用时


int main(void)
{
	int i ;
	for(i=0; i<20; i++){
		printf("%d\t",function());
	}
	return 0;
}

它会打印输出0 - 19 这20个数,而这又是为什么呢 ?



加载中
0
娱乐你我
娱乐你我

function函数 vc2005下反汇编代码:

00416050  /> \55            PUSH EBP
00416051  |.  8BEC          MOV EBP,ESP
00416053  |.  51            PUSH ECX
00416054  |.  A1 A4234200   MOV EAX,DWORD PTR DS:[4223A4]            ;  state赋给eax寄存器
00416059  |.  8945 FC       MOV DWORD PTR SS:[EBP-4],EAX
0041605C  |.  837D FC 00    CMP DWORD PTR SS:[EBP-4],0               ;  state是否为0(对应case 0:)
00416060  |.  74 08         JE SHORT s.0041606A                      ;  state为0则跳转
00416062  |.  837D FC 01    CMP DWORD PTR SS:[EBP-4],1               ;  对应case 1:
00416066  |.  74 4B         JE SHORT s.004160B3                      ;  state为1则跳转
00416068  |.  EB 5E         JMP SHORT s.004160C8
0041606A  |>  C705 A0234200>MOV DWORD PTR DS:[4223A0],0              ;  i = 0 ;
00416074  |.  EB 0F         JMP SHORT s.00416085
00416076  |>  8B0D A0234200 MOV ECX,DWORD PTR DS:[4223A0]            ;  ecx寄存器 <=== i
0041607C  |.  83C1 01       ADD ECX,1
0041607F  |.  890D A0234200 MOV DWORD PTR DS:[4223A0],ECX            ;  i++ ;
00416085  |>  833D A0234200>CMP DWORD PTR DS:[4223A0],0A             ;  i与10比较
0041608C  |.  7D 3A         JGE SHORT s.004160C8                     ;  若i>=10则跳转
0041608E  |.  C705 A4234200>MOV DWORD PTR DS:[4223A4],1              ;  state=1 ;
00416098  |.  8B15 A0234200 MOV EDX,DWORD PTR DS:[4223A0]            ;  edx寄存器<===静态变量i的值
0041609E  |.  52            PUSH EDX
0041609F  |.  68 701F4200   PUSH s.00421F70                          ;  ASCII "case 0: %d
"
004160A4  |.  E8 33B0FEFF   CALL s.004010DC                          ;  printf("case 0: %d\n", i) ;
004160A9  |.  83C4 08       ADD ESP,8
004160AC  |.  A1 A0234200   MOV EAX,DWORD PTR DS:[4223A0]            ;  function函数返回值
004160B1  |.  EB 1A         JMP SHORT s.004160CD
004160B3  |>  A1 A0234200   MOV EAX,DWORD PTR DS:[4223A0]            ;  eax寄存器<===静态变量i的值
004160B8  |.  50            PUSH EAX
004160B9  |.  68 7C1F4200   PUSH s.00421F7C                          ;  ASCII "case 1: %d
"
004160BE  |.  E8 19B0FEFF   CALL s.004010DC                          ;  printf("case 1: %d\n", i) ;
004160C3  |.  83C4 08       ADD ESP,8
004160C6  |.^ EB AE         JMP SHORT s.00416076                     ;  for循环跳转(i<10时)
004160C8  |>  A1 A0234200   MOV EAX,DWORD PTR DS:[4223A0]
004160CD  |>  8BE5          MOV ESP,EBP
004160CF  |.  5D            POP EBP
004160D0  \.  C3            RETN



0
中山野鬼
中山野鬼

你确定你的代码能编译过?哈。我指,没有waring !!!

你的代码我特地测试了一下。有逻辑错误。虽然不是error 

warning: control may reach end of non-void function [-Wreturn-type]


至于为什么那样打印,很简单你把  i变量的 static 去掉,看看编译器给什么提示,哈。。。

另外,我喷一句,这样写代码的,基本直接裁掉。属于脑袋坏掉的。。。。绝对不用。

Feng_Yu
Feng_Yu
用gcc 4.8.2编译了一下,还真没有warning
0
OceanStack
OceanStack

这段代码我在Dev-C++平台上编译过的,linux下gcc肯定没问题,我不知道你用的什么编译器。

另外这段代码是出自天才级黑客Simon Tatham 之手,具体可参见这里 ,对于普通程序员肯定不能这样写的。但这是实现协程的基本技巧。

0
PYPlus
PYPlus
你这段代码 质量真糟糕,函数的末尾没有return 导致编译器报警。把你这段代码放到不同的编译器下编译运行试试? 在gcc4.8.3和clang3.4中编译得到的可执行文件的执行结果是不一样的。 这样的代码谁敢用?
OceanStack
OceanStack
哦,我研究研究,谢谢!
0
诸葛非卿
诸葛非卿
兄弟,我不知道这段代码是不是什么天才写的,也不知道具体是不是有特别的应用,但是可以可以肯定的说,就算是天才也有拉屎放屁的时候!
诸葛非卿
诸葛非卿
作为一个悲哀的模板人我只能说我很无奈,一直都是在自己写些东西,可就是发现自己不论代码有多么健壮,项目速度总是慢,后来用了模板项目速度快,而且质量还挺好!现在不但没有进步反而越来没有出息了,我正打算将自己原来写的些东西做成框架模板,看样子我这模板男是到了无可救药的地步了!
B
BZQ31
悲哀的模板人
0
木子叶
木子叶

最近比较认真地在看《C与指针》,对作用域、链接属性以及存储类型又有了更深一步的认识,所以斗胆来回答一下好了。

根据作用域的定义,main()函数里面的 i 其实与function()函数里面的i是不同的,虽然后者的存储类型是静态的,也就是说,如果把main()函数里面的 i 改为别的变量名,例如a,运行结果也是0~19。

接下来解释运行结果为什么是0~19,为了避免拗口,先把main()函数里面的变量i改为a好了。

a = 0,首次进入function()函数,声明静态变量i和state,并初始化state为0,case 0就进入for循环,i = 0,state = 1,至此,以后每次调用function()函数,都有state = 1了,不过这次先return i;

a = 1,再次进入function()函数时,因为 i 和state是静态变量,所以直接进行switch语句,直接跳到case 1,然后是i++,接着返回 i,说到这里,其实function()函数的逻辑可以这样写:

int function(void)
{
    static int i = -1;
    i += 1;
    return i;
}

因此,就有你的运行结果了。

0
习家家天下
我记得酷壳上有对这段代码的详细介绍。可用来模拟ruby中的yield。确实不是一般人能想出来的。
OceanStack
OceanStack
嗯,我也是在酷壳《 一个“蝇量级” C 语言协程库》这文中看到的 。
wei2011
wei2011
的确很像ruby,python里的yield.py3中也是通过yield实现并发(协程)的
0
wei2011
wei2011
有些人一看到奇怪一点的写法就开始说教“你不能这样写”,”脑袋坏掉了“....A片尚且能以批判的眼光看,平时不能这么归平时不能这么写,以探索的眼光看问题不行么?
PYPlus
PYPlus
请把lz的代码放到clang3.4 gcc4.8 微软的编译器等编译器下编译运行看看。
OceanStack
OceanStack
酷!
0
wharf_zhang
wharf_zhang
1、此代码在没有上下文的情况下,看起来就是垃圾。但如果被置于特定环境中,也许就是很牛逼的。2、有些人喜欢琢磨,喜欢尝试各种特殊的写法,其不循常规的用法,显然不是单纯为了可运行,往往是为了性能、攻击、测试等特殊目的。3、如果没有这些人,世界会清净很多,因为黑客们都是此类人。4、当然,一些致力于或热衷于发现漏洞的特殊人士也属于此类,即便能够消灭所有黑客,他们的存在也还是有意义的,毕竟他们可以提高工质量。5、一般我们反对这么写东西。
诸葛非卿
诸葛非卿
我完全同意你的观点,在没有上下文,或者没有说明这段代码的特殊应用场景时,亦或两者兼有时,光看这段代码本身我实在是看不到这段代码的高明处!也许是是我才疏学浅。但是写程序这么久,就算只会偷,偷多了这种眼界还是有的!同时我很反感一些人只看人不看代码本身!
0
娱乐你我
娱乐你我

加点输出的代码就会看的懂了,如下:

#include <stdio.h>

int function(void)
{
	static int i, state = 0;
	
	switch (state)
	{
	case 0: /* start of function */
		for (i = 0; i < 10; i++)
		{
			state = 1; /* so we will come back to "case 1" */
			printf("case 0: %d\n", i) ;
			return i;
			
			case 1:
				printf("case 1: %d\n", i) ; /* resume control straight after the return */
		}
	}
	return i ;
}

int main(void)
{
	int i ;
    for(i=0; i<20; i++)
	{
		printf("begin\n") ;
        printf("%d\n",function());
        printf("end\n") ;
    }
    return 0;
}



OceanStack
OceanStack
但可否从汇编的角度再解释下,我还是没有理解很透彻 。
OceanStack
OceanStack
酷,简洁明了,但把代码执行过程看清楚了,非常感谢 。
返回顶部
顶部