C函数声明的顺序

时间:2014-08-12 16:48:45

标签: c function prototype declaration

这是一个类似我正在处理的问题的示例代码

    #include <stdio.h>

    int cube_then_square(int x){
        x = cube(x);
        return x*x;
    }

    int cube(int y){
        return y*y*y;
    }

    int main(int argc, char *argv[]){
        printf("5 cubed then squared is: %d\n", cube_then_square(5));
        return 0;
    }

所以编译器给我一个未声明的多维数据集的问题。那么有人可以解释一下将这些函数放入内存的顺序等等......以及如何将原型放在顶部以及主要之后的实现有什么不同。感谢。

5 个答案:

答案 0 :(得分:3)

存在隐式声明函数的警告,因为大多数现代C程序都不使用隐式函数。

旧C89允许隐式声明一切。当你调用一个函数时,它被隐式声明为int func ()。 这适用于这种情况,因为您使用以下行隐式声明函数int cube()

x = cube(x);

然后你定义函数int cube(int)int cube()int cube(int)具有兼容的类型,因此这是一个很好的调用。

你真正遇到的麻烦是从一个隐式声明的函数调用一个不兼容的函数(这就是警告存在的原因)。 int cube(float)是一个不兼容的函数类型,它肯定存在,如果你用一个隐式声明的函数调用它,你可能会期待一些非常奇怪的效果(读取未定义)。正如mafso提到的那样,严格的C99不再允许隐式声明的函数,这就是为什么许多编译器都包含警告的原因。

记住隐式声明的函数是BAD PRACTICE但你应该知道这样的场景存在。

这里有一个小程序来演示隐式声明函数的弱点。它利用了你在c程序中所期望的一些转换规则,这些规则突然消失了隐式声明的函数。

#include <stdio.h>

cube1(int x){return x*x*x;}
main(){float y = 9.; printf("%d\n%d\n", cube1(y), cube2(y));}
cube2(int x){return x*x*x;}

输出:

729
1

这些功能在asm中是相同的

00000000004004dc <cube1>:
  4004dc:   55                      push   %rbp
  4004dd:   48 89 e5                mov    %rsp,%rbp
  4004e0:   89 7d fc                mov    %edi,-0x4(%rbp)
  4004e3:   8b 45 fc                mov    -0x4(%rbp),%eax
  4004e6:   0f af 45 fc             imul   -0x4(%rbp),%eax
  4004ea:   0f af 45 fc             imul   -0x4(%rbp),%eax
  4004ee:   5d                      pop    %rbp
  4004ef:   c3                      retq   
0000000000400540 <cube2>:
  400540:   55                      push   %rbp
  400541:   48 89 e5                mov    %rsp,%rbp
  400544:   89 7d fc                mov    %edi,-0x4(%rbp)
  400547:   8b 45 fc                mov    -0x4(%rbp),%eax
  40054a:   0f af 45 fc             imul   -0x4(%rbp),%eax
  40054e:   0f af 45 fc             imul   -0x4(%rbp),%eax
  400552:   5d                      pop    %rbp
  400553:   c3                      retq  

但是在调用点上,从不对隐式调用执行从float到integer的预期转换。

答案 1 :(得分:1)

编译器从上到下读取文件。当它到达功能时,它会检查它是否已经知道它。在这种情况下,它没有看到函数cube(int),因此它返回错误。

你可以做两件事: 1.在函数cube之前移动函数cube_then_square。 2.您在cube_then_square之前创建了一个前向声明:

int cube(int y);

答案 2 :(得分:1)

在文件顶部的包含下,添加int cube(int y);

您必须在使用之前声明一个函数。它不必定义,但必须在使用之前声明它,因为编译器按顺序检查行。

答案 3 :(得分:0)

要解决此问题,您可以在使用前声明cube

#include <stdio.h>

int cube(int y){
    return y*y*y;
}

int cube_then_square(int x){
    x = cube(x);
    return x*x;
}

int main(int argc, char *argv[]){
    printf("5 cubed then squared is: %d\n", cube_then_square(5));
    return 0;
}

您还可以在顶部添加功能标题int cube(int y);

#include <stdio.h>

int cube(int y);

int cube_then_square(int x){
    x = cube(x);
    return x*x;
}

int cube(int y){
    return y*y*y;
}

int main(int argc, char *argv[]){
    printf("5 cubed then squared is: %d\n", cube_then_square(5));
    return 0;
}

答案 4 :(得分:0)

这些功能如何“放入内存”完全无关紧要。实际上,这绝不会在语言层面暴露出来。没关系。

当您组织实施文件时,您可以选择以自上而下的方式或自下而上的方式进行。

在前一种情况下,首先定义上层函数。上层函数调用较低级别的函数,这些函数稍后在文件中定义。为了使其正常工作,低级函数必须在第一次调用之前另外原型化(例如在文件的最顶部)。 (形式上,不需要原型,但至少需要声明。)

在后一种情况下,首先定义低级函数,稍后定义高级函数。在这种方法中,定义本身已经作为原型,这意味着不需要额外的原型设计(除非你在函数之间有一个循环调用依赖)。

自上而下是一种更高级的维护方法,因为您必须使函数原型与其定义保持同步。

您的示例中的内容看起来像是这两种方法的混合。 main(最高级别的函数)在底部定义,但cube_then_squarecube之前定义。这没有什么不妥,但为了保持这种顺序,你必须在文件的开头提供cube的原型。

我个人更喜欢自下而上的方法。例如。在您的具体示例中,我将cube的整个定义移到文件的顶部。这将消除提供额外原型的需要。

无论如何,无论您如何操作,生成的代码都没有任何有意义的差异。目标文件中的函数排序可能会受到源文件中的排序的影响,但从语言的角度来看,这完全是无关紧要的。