C函数调用而忘记参数列表的后果 [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_declarator 
Dump of assembler code for function deal_with_declarator:
109 void deal_with_declarator(void) {
   0x08048a0e <+0>: push   %ebp
   0x08048a0f <+1>: mov    %esp,%ebp
   0x08048a11 <+3>: sub    $0x8,%esp

110     //printf("\nthis.type %c\n",this.type);
111     switch(this.type) 
   0x08048a14 <+6>: movzbl 0x804a060,%eax
   0x08048a1b <+13>:    movsbl %al,%eax
   0x08048a1e <+16>:    cmp    $0x28,%eax
   0x08048a21 <+19>:    je     0x8048a2f 
   0x08048a23 <+21>:    cmp    $0x5b,%eax
   0x08048a26 <+24>:    jne    0x8048a34 

112     {
113         case '[' : 
114             deal_with_arrays(); break;
   0x08048a28 <+26>:    call   0x80488fb 
   0x08048a2d <+31>:    jmp    0x8048a34 

115         case '(' : 
116             deal_with_function_args(); break;
   0x08048a2f <+33>:    call   0x804898c 

117     }
118     deal_with_pointers();
   0x08048a34 <+38>:    call   0x80489bc 

119 
120     while( top >= 0 ){
   0x08048a39 <+43>:    jmp    0x8048a9b 
   0x08048a9b <+141>:   mov    0x804a038,%eax
   0x08048aa0 <+146>:   test   %eax,%eax
   0x08048aa2 <+148>:   jns    0x8048a3b 

121         if( stack[top].type == '(') {
   0x08048a3b <+45>:    mov    0x804a038,%edx
   0x08048a41 <+51>:    mov    %edx,%eax
   0x08048a43 <+53>:    shl    $0x6,%eax
   0x08048a46 <+56>:    add    %edx,%eax
   0x08048a48 <+58>:    add    $0x804a0c0,%eax
   0x08048a4d <+63>:    movzbl (%eax),%eax
   0x08048a50 <+66>:    cmp    $0x28,%al
   0x08048a52 <+68>:    jne    0x8048a6d 

122             pop;
---Type  to continue, or q  to quit---
   0x08048a54 <+70>:    mov    0x804a038,%eax
   0x08048a59 <+75>:    sub    $0x1,%eax
   0x08048a5c <+78>:    mov    %eax,0x804a038

123             gettoken();
   0x08048a61 <+83>:    call   0x8048774   #函数调用

124             deal_with_declarator();
   0x08048a66 <+88>:    call   0x8048a0e 
   0x08048a6b <+93>:    jmp    0x8048a9b 

125         }else {
126             printf("%s ", pop.string);
   0x08048a6d <+95>:    mov    0x804a038,%edx
   0x08048a73 <+101>:   lea    -0x1(%edx),%eax
   0x08048a76 <+104>:   mov    %eax,0x804a038
   0x08048a7b <+109>:   mov    %edx,%eax
   0x08048a7d <+111>:   shl    $0x6,%eax
   0x08048a80 <+114>:   add    %edx,%eax
   0x08048a82 <+116>:   add    $0x804a0c0,%eax
   0x08048a87 <+121>:   add    $0x1,%eax
   0x08048a8a <+124>:   sub    $0x8,%esp
   0x08048a8d <+127>:   push   %eax
   0x08048a8e <+128>:   push   $0x8048be4
   0x08048a93 <+133>:   call   0x8048410 
   0x08048a98 <+138>:   add    $0x10,%esp

127         }
128     }
129 }
   0x08048aa4 <+150>:   nop
   0x08048aa5 <+151>:   leave  
   0x08048aa6 <+152>:   ret    

End of assembler dump.

其中第123行是调用函数gettoken

我们再看看错误代码的汇编结果:

(gdb) disassemble /m deal_with_declarator 
Dump of assembler code for function deal_with_declarator:
109 void deal_with_declarator(void) {
   0x08048a0e <+0>: push   %ebp
   0x08048a0f <+1>: mov    %esp,%ebp
   0x08048a11 <+3>: sub    $0x8,%esp

110     //printf("\nthis.type %c\n",this.type);
111     switch(this.type) 
   0x08048a14 <+6>: movzbl 0x804a060,%eax
   0x08048a1b <+13>:    movsbl %al,%eax
   0x08048a1e <+16>:    cmp    $0x28,%eax
   0x08048a21 <+19>:    je     0x8048a2f 
   0x08048a23 <+21>:    cmp    $0x5b,%eax
   0x08048a26 <+24>:    jne    0x8048a34 

112     {
113         case '[' : 
114             deal_with_arrays(); break;
   0x08048a28 <+26>:    call   0x80488fb 
   0x08048a2d <+31>:    jmp    0x8048a34 

115         case '(' : 
116             deal_with_function_args(); break;
   0x08048a2f <+33>:    call   0x804898c 

117     }
118     deal_with_pointers();
   0x08048a34 <+38>:    call   0x80489bc 

119 
120     while( top >= 0 ){
   0x08048a39 <+43>:    jmp    0x8048a96 
   0x08048a96 <+136>:   mov    0x804a038,%eax
   0x08048a9b <+141>:   test   %eax,%eax
   0x08048a9d <+143>:   jns    0x8048a3b 

121         if( stack[top].type == '(') {
   0x08048a3b <+45>:    mov    0x804a038,%edx
   0x08048a41 <+51>:    mov    %edx,%eax
   0x08048a43 <+53>:    shl    $0x6,%eax
   0x08048a46 <+56>:    add    %edx,%eax
   0x08048a48 <+58>:    add    $0x804a0c0,%eax
   0x08048a4d <+63>:    movzbl (%eax),%eax
   0x08048a50 <+66>:    cmp    $0x28,%al
   0x08048a52 <+68>:    jne    0x8048a68 

122             pop;
---Type  to continue, or q  to quit---
   0x08048a54 <+70>:    mov    0x804a038,%eax
   0x08048a59 <+75>:    sub    $0x1,%eax
   0x08048a5c <+78>:    mov    %eax,0x804a038

123             gettoken;   #看到没,啥也没做
124             deal_with_declarator();
   0x08048a61 <+83>:    call   0x8048a0e 
   0x08048a66 <+88>:    jmp    0x8048a96 

125         }else {
126             printf("%s ", pop.string);
   0x08048a68 <+90>:    mov    0x804a038,%edx
   0x08048a6e <+96>:    lea    -0x1(%edx),%eax
   0x08048a71 <+99>:    mov    %eax,0x804a038
   0x08048a76 <+104>:   mov    %edx,%eax
   0x08048a78 <+106>:   shl    $0x6,%eax
   0x08048a7b <+109>:   add    %edx,%eax
   0x08048a7d <+111>:   add    $0x804a0c0,%eax
   0x08048a82 <+116>:   add    $0x1,%eax
   0x08048a85 <+119>:   sub    $0x8,%esp
   0x08048a88 <+122>:   push   %eax
   0x08048a89 <+123>:   push   $0x8048be4
   0x08048a8e <+128>:   call   0x8048410 
   0x08048a93 <+133>:   add    $0x10,%esp

127         }
128     }
129 }
   0x08048a9f <+145>:   nop
   0x08048aa0 <+146>:   leave  
   0x08048aa1 <+147>:   ret    

End of assembler dump.

看到没有,这个地方啥也没发生。

但是,假如我们这个地方不是一个存在的函数名而是一个随便的字符串,会怎么样?

gcc -o cdecl ch3_cdecl.c -g
ch3_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

总结一下,如果想调用一个函数,而忘记了它的参数列表,就只有一个函数名,编译器是可以通过的。这是危险的。应该对编译器进行配置以提醒这种情况。

你可能感兴趣的:(C函数调用而忘记参数列表的后果 [C专家编程])