@yiltoncent
2015-11-18T17:08:56.000000Z
字数 5436
阅读 2617
C语言基础
故事从C专家编程的第三章分析C语言的声明开始,书中给出了cdecl代码,正好昨天把整章看完,所以今天就想着把程序抄一边,然后搞懂一下怎么回事。代码可以参考此链接。
这儿只将涉及到我的问题的部分代码摘录下来,原始代码:
void deal_with_declarator(){/*处理标识符之后可能存在的数组/函数*/switch(this.type){case '[':deal_with_arrays();break;case '(':deal_with_function_args();break;}deal_with_pointers();/*处理在读入到标识符之前压入到堆栈的符号*/while(top>=0){if(stack[top].type=='('){pop;gettoken();/*读取')'之后的符号*/deal_with_declarator();}else{printf("%s ",pop.string);}}}int main(){/*将标记呀入堆栈中,直到遇见标识符*/read_to_first_identifier();deal_with_declarator();printf("\n");return 0;}
而我在抄的时候将deal_with_declarator函数内部的一个函数调用忘记了():
if(stack[top].type=='('){pop;gettoken; //问题正出在这儿,虽然没有括号,但依然能编译通过deal_with_declarator();}
我们先看一下,原始代码编译后是什么样的,记住加上-g选项:
(gdb) disassemble /m deal_with_declaratorDump of assembler code for function deal_with_declarator:109 void deal_with_declarator(void) {0x08048a0e <+0>: push %ebp0x08048a0f <+1>: mov %esp,%ebp0x08048a11 <+3>: sub $0x8,%esp110 //printf("\nthis.type %c\n",this.type);111 switch(this.type)0x08048a14 <+6>: movzbl 0x804a060,%eax0x08048a1b <+13>: movsbl %al,%eax0x08048a1e <+16>: cmp $0x28,%eax0x08048a21 <+19>: je 0x8048a2f <deal_with_declarator+33>0x08048a23 <+21>: cmp $0x5b,%eax0x08048a26 <+24>: jne 0x8048a34 <deal_with_declarator+38>112 {113 case '[' :114 deal_with_arrays(); break;0x08048a28 <+26>: call 0x80488fb <deal_with_arrays>0x08048a2d <+31>: jmp 0x8048a34 <deal_with_declarator+38>115 case '(' :116 deal_with_function_args(); break;0x08048a2f <+33>: call 0x804898c <deal_with_function_args>117 }118 deal_with_pointers();0x08048a34 <+38>: call 0x80489bc <deal_with_pointers>119120 while( top >= 0 ){0x08048a39 <+43>: jmp 0x8048a9b <deal_with_declarator+141>0x08048a9b <+141>: mov 0x804a038,%eax0x08048aa0 <+146>: test %eax,%eax0x08048aa2 <+148>: jns 0x8048a3b <deal_with_declarator+45>121 if( stack[top].type == '(') {0x08048a3b <+45>: mov 0x804a038,%edx0x08048a41 <+51>: mov %edx,%eax0x08048a43 <+53>: shl $0x6,%eax0x08048a46 <+56>: add %edx,%eax0x08048a48 <+58>: add $0x804a0c0,%eax0x08048a4d <+63>: movzbl (%eax),%eax0x08048a50 <+66>: cmp $0x28,%al0x08048a52 <+68>: jne 0x8048a6d <deal_with_declarator+95>122 pop;---Type <return> to continue, or q <return> to quit---0x08048a54 <+70>: mov 0x804a038,%eax0x08048a59 <+75>: sub $0x1,%eax0x08048a5c <+78>: mov %eax,0x804a038123 gettoken();0x08048a61 <+83>: call 0x8048774 <gettoken> #函数调用124 deal_with_declarator();0x08048a66 <+88>: call 0x8048a0e <deal_with_declarator>0x08048a6b <+93>: jmp 0x8048a9b <deal_with_declarator+141>125 }else {126 printf("%s ", pop.string);0x08048a6d <+95>: mov 0x804a038,%edx0x08048a73 <+101>: lea -0x1(%edx),%eax0x08048a76 <+104>: mov %eax,0x804a0380x08048a7b <+109>: mov %edx,%eax0x08048a7d <+111>: shl $0x6,%eax0x08048a80 <+114>: add %edx,%eax0x08048a82 <+116>: add $0x804a0c0,%eax0x08048a87 <+121>: add $0x1,%eax0x08048a8a <+124>: sub $0x8,%esp0x08048a8d <+127>: push %eax0x08048a8e <+128>: push $0x8048be40x08048a93 <+133>: call 0x8048410 <printf@plt>0x08048a98 <+138>: add $0x10,%esp127 }128 }129 }0x08048aa4 <+150>: nop0x08048aa5 <+151>: leave0x08048aa6 <+152>: retEnd of assembler dump.
其中第123行是调用函数gettoken。
我们再看看错误代码的汇编结果:
(gdb) disassemble /m deal_with_declaratorDump of assembler code for function deal_with_declarator:109 void deal_with_declarator(void) {0x08048a0e <+0>: push %ebp0x08048a0f <+1>: mov %esp,%ebp0x08048a11 <+3>: sub $0x8,%esp110 //printf("\nthis.type %c\n",this.type);111 switch(this.type)0x08048a14 <+6>: movzbl 0x804a060,%eax0x08048a1b <+13>: movsbl %al,%eax0x08048a1e <+16>: cmp $0x28,%eax0x08048a21 <+19>: je 0x8048a2f <deal_with_declarator+33>0x08048a23 <+21>: cmp $0x5b,%eax0x08048a26 <+24>: jne 0x8048a34 <deal_with_declarator+38>112 {113 case '[' :114 deal_with_arrays(); break;0x08048a28 <+26>: call 0x80488fb <deal_with_arrays>0x08048a2d <+31>: jmp 0x8048a34 <deal_with_declarator+38>115 case '(' :116 deal_with_function_args(); break;0x08048a2f <+33>: call 0x804898c <deal_with_function_args>117 }118 deal_with_pointers();0x08048a34 <+38>: call 0x80489bc <deal_with_pointers>119120 while( top >= 0 ){0x08048a39 <+43>: jmp 0x8048a96 <deal_with_declarator+136>0x08048a96 <+136>: mov 0x804a038,%eax0x08048a9b <+141>: test %eax,%eax0x08048a9d <+143>: jns 0x8048a3b <deal_with_declarator+45>121 if( stack[top].type == '(') {0x08048a3b <+45>: mov 0x804a038,%edx0x08048a41 <+51>: mov %edx,%eax0x08048a43 <+53>: shl $0x6,%eax0x08048a46 <+56>: add %edx,%eax0x08048a48 <+58>: add $0x804a0c0,%eax0x08048a4d <+63>: movzbl (%eax),%eax0x08048a50 <+66>: cmp $0x28,%al0x08048a52 <+68>: jne 0x8048a68 <deal_with_declarator+90>122 pop;---Type <return> to continue, or q <return> to quit---0x08048a54 <+70>: mov 0x804a038,%eax0x08048a59 <+75>: sub $0x1,%eax0x08048a5c <+78>: mov %eax,0x804a038123 gettoken; #看到没,啥也没做124 deal_with_declarator();0x08048a61 <+83>: call 0x8048a0e <deal_with_declarator>0x08048a66 <+88>: jmp 0x8048a96 <deal_with_declarator+136>125 }else {126 printf("%s ", pop.string);0x08048a68 <+90>: mov 0x804a038,%edx0x08048a6e <+96>: lea -0x1(%edx),%eax0x08048a71 <+99>: mov %eax,0x804a0380x08048a76 <+104>: mov %edx,%eax0x08048a78 <+106>: shl $0x6,%eax0x08048a7b <+109>: add %edx,%eax0x08048a7d <+111>: add $0x804a0c0,%eax0x08048a82 <+116>: add $0x1,%eax0x08048a85 <+119>: sub $0x8,%esp0x08048a88 <+122>: push %eax0x08048a89 <+123>: push $0x8048be40x08048a8e <+128>: call 0x8048410 <printf@plt>0x08048a93 <+133>: add $0x10,%esp127 }128 }129 }0x08048a9f <+145>: nop0x08048aa0 <+146>: leave0x08048aa1 <+147>: retEnd of assembler dump.
看到没有,这个地方啥也没发生。
但是,假如我们这个地方不是一个存在的函数名而是一个随便的字符串,会怎么样?
gcc -o cdecl ch3_cdecl.c -gch3_cdecl.c: In function ‘deal_with_declarator’:ch3_cdecl.c:123:4: error: ‘dasgettoken’ undeclared (first use in this function)dasgettoken;^ch3_cdecl.c:123:4: note: each undeclared identifier is reported only once for each function it appears in
此时,编译器会识别出这个名字,它是没定义的identifier。
总结一下,如果想调用一个函数,而忘记了它的参数列表,就只有一个函数名,编译器是可以通过的。这是危险的。应该对编译器进行配置以提醒这种情况。