上篇日志中提出了一个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个分号。
- 不能使用do while until goto for if关键字,不能使用?:和逗号操作符。
- 唯一能使用的库函数为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 } |
对照上面的伪代码看源程序,我相信大多数人都能看懂了。
另外,通过直接调用中断的方法我就不列举出来了。其实可以想想,是不是还有别的方法能够完成这个题目呢?我暂时没想出来。
赞深挖精神。你语法高亮插件用的什么?
WP-Syntax 这个吧应该
我这儿都没啥人来,以后多来逛逛
博客需要写上半年才会有稳定的读者群,别急。坚持住:)
恩 我慢慢来把 呵呵
博客挺好,以后多交流
恩 以后常来逛逛