C对象的顺序是否重要?

时间:2011-06-12 02:13:49

标签: c

C对象在文件中出现的顺序是否重要?

例如,在函数中,如果我创建了两个函数,而上面的函数引用另一个函数,它会起作用吗? (是的,我已经尝试过了。) 对静态函数,INLINE函数等有效吗? 结构有效吗?如果我引用在.c文件中进一步定义的结构会发生什么?

这是针对特定编译器的吗?在这种情况下,编译器如何工作?它首先扫描整个文件的所有声明/定义,然后尝试取消引用函数/符号吗?

4 个答案:

答案 0 :(得分:3)

首先,如果“如果我创建了两个函数而上面的那个函数引用了另一个函数,它会起作用吗?”你的意思是这样的:

int foo()
{
    return bar();
}

int bar()
{
    return 0;
}

然后编译器可以对bar()进行有根据的猜测,但如果bar()尚未声明,它至少会发出警告。对于无法调用的符号(如变量或类型),如果在声明它们之前使用它们,那将是一个彻头彻尾的错误。

在C中,无论何时使用标识符(无论标识符的类型如何:它可能是函数,变量,类型等),都应事先声明。您可以添加到任何标识符的各种修饰符(如您所说的staticinline以及所有其他标识符)对此没有任何影响。

请勿混淆声明定义。声明只是告诉编译器存在名称;一个定义实际上告诉编译器它是什么。

例如,这是一个定义:

int bar() { return 4; }

注意它有一个正文,里面有(简单的)代码。

这是匹配声明:

int bar();

一旦看到声明或其定义,编译器就会很乐意接受函数的使用。出于组织原因和更好的灵活性,通常最好在C文件的顶部(或在包含的头文件中)编写所有函数的声明,然后编写定义。

所以,我的第一个例子应该是这样的:

int foo();
int bar();

int foo()
{
    return bar();
}

int bar()
{
    return 0;
}

使用C代码上方的声明,我可以以任何我喜欢的方式更改函数的顺序。

答案 1 :(得分:2)

通常必须在您使用它的地方定义上面的内容。您可以针对不同情况以不同方式避免这种情况。

对于函数,只需在上面提供一个原型,然后一切就好了。

int trol(int a, int b);

// use trol(int, int)

int trol(int a, int b) { }

如果您有两个功能ab,他们会按照以下顺序互相调用并定义,{{1}那么你必须在a的定义之上提供b的原型。 b的原型不是必需的,因为它是在a内使用它的地方定义的。然后编译器就没有问题了。

函数的另一个特例是,您可以在不声明函数的情况下使用函数,编译器将尝试从调用中推断出签名。这个答案很好地解释了我的想法:Must declare function prototype in C?

对于a,您可以在实际定义之前使用指针(但您无法访问任何字段),方法是提供前向声明:

b

(上述场景有助于递归数据结构,如链接列表和树;在完全定义之前,您可以使用指向struct的指针,因为任何类型的指针的大小都是常量。)

答案 2 :(得分:1)

重要的是,因为如果编译器不知道函数是什么 - 它将尝试'猜测'(创建具有匹配参数的默认int foo()原型),如果你的调用不正确 - 你'会有不匹配(构建错误,隐式转换,等等)。

在调用函数之前声明函数(通过原型即前向声明)是常见的做法(如果不是必需的话)。

对于具有可变参数列表的函数(例如printf),必须具有前向声明才能使它们正常工作。例如,此代码将无法编译:

int foo(int a)
{
    b(a);
    b("hello", "kitty");
}

void b(int a, ...)
{

    printf("%d", a);
}

但是 - 会:

#include <stdio.h>
int foo(int a)
{
    return b(a);
}

int b(int a)
{

    return printf("%d", a);
}

(关于隐含前向声明的警告)

因此,为了避免处理文件中对象的顺序 - 使用原型设计(前向声明)让编译器知道后面的内容。

答案 3 :(得分:1)

根据我的经验,在引用之前,必须使用引用的“对象”编写C中的所有内容。我不认为这是特定于任何编译器,但也许有一些我没有找到。基本上,一切都必须是:

Object Declaration
...
Object Reference