上篇日志中提出了一个EMC某年的笔试题目,题目如下:

题目:

函数声明如下:

int func(int i, int N);

其中,i<=N, 功能输出i递增到N再递减到i的整数,每行输出一个数。比如func(1, 5)的输出是:

1
2
3
4
5
4
3
2
1

要求:

  1. 函数中只能有一个语句,即1个分号。
  2. 不能使用do while until goto for if关键字,不能使用?:和逗号操作符。
  3. 唯一能使用的库函数为printf。

按照常规的解法,答案就应该是上一篇日志(这里)所解答的答案,利用&&和||以及递归。

但是,在满足题目要求的情况下,还有一种方法也可以完成该题,那就是使用嵌入式汇编,虽然可能不能迎合出题者的初衷,但是使用嵌入式汇编来写却是一件十分有意思的事情。

通过嵌入式汇编来打印数字,可以通过库函数printf,也可以直接通过int指令来调用中断。相比之下,调用printf因为压栈规则等,会稍微复杂些,下面的代码是我通过调用printf来实现程序功能:

int func(int i, int N){
	__asm__(
		"sub $0x20, %%esp\n\t"
		"movl $0xa6425, 0x1c(%%esp)\n\t"
		"lea 0x1c(%%esp), %%ebx\n\t"
		"movl %%ebx, (%%esp)\n\t"
		"mov %0, %%ebx\n\t"
 		"mov %%ebx, 0x4(%%esp)\n\t"
		"call *%2\n\t"
		"mov %1, %%edx\n\t"
		"cmp %%ebx, %%edx\n\t"
		"jne L1\n\t"
		"jmp LEND\n\t"
		"L1:\n\t"
		"inc %%ebx\n\t"
		"mov %%ebx, 0x4(%%esp)\n\t"
		"call *%2\n\t"
		"mov %1, %%edx\n\t"
		"cmp %%ebx, %%edx\n\t"
		"jne L1\n\t"
		"L2:"
		"dec %%ebx\n\t"
		"mov %%ebx, 0x4(%%esp)\n\t"
		"call *%2\n\t"
		"mov %0, %%ecx\n\t"
		"cmp %%ebx, %%ecx\n\t"
		"jne L2\n\t"
		"LEND:\n\t"
		"leave\n\t"
		"ret \n\t"
		::"m"(i),"m"(N),"S"(printf)
 
		);
}

这个函数完全满足题目的三个要求,只不过有点(可能不止一点)长而已。

代码第三行设置好栈,防止栈越界。第4行中,立即数“0xa6425”表示的是"%d\n",放在栈中,第6,7行将他的地址作为printf的第一个参数存储在栈上合适的位置。8,9,10行取i的值存在栈上第二个参数位置,并调用printf函数(gcc中参数从右向左压栈,所以第二个参数的内存位置比第一个高)。

第8到第29行完成了类似下面代码的功能:

int a=i;
int b=N;
printf("%d\n", a);
if(a == b)
        goto END;
 
L1:
        a ++;
        printf("%d\n", a);
        if( a != b)
               goto L1;
 
L2:
        a --;
        printf("%d\n",a);
        if(a != i)
                goto L2;
 
END
}

对照上面的伪代码看源程序,我相信大多数人都能看懂了。

另外,通过直接调用中断的方法我就不列举出来了。其实可以想想,是不是还有别的方法能够完成这个题目呢?我暂时没想出来。

anyShare分享到:

6 Responses to “实现int func(int i, int N)题目的另一个方法(见上篇日志)”

  1. 编译点滴 says:

    赞深挖精神。你语法高亮插件用的什么?

  2. 匿名 says:

    博客挺好,以后多交流

Leave a Reply

Note: Commenter is allowed to use '@User+blank' to automatically notify your reply to other commenter. e.g, if ABC is one of commenter of this post, then write '@ABC '(exclude ') will automatically send your comment to ABC. Using '@all ' to notify all previous commenters. Be sure that the value of User should exactly match with commenter's name (case sensitive).