C对象在文件中出现的顺序是否重要?
例如,在函数中,如果我创建了两个函数,而上面的函数引用另一个函数,它会起作用吗? (是的,我已经尝试过了。) 对静态函数,INLINE函数等有效吗? 结构有效吗?如果我引用在.c文件中进一步定义的结构会发生什么?
这是针对特定编译器的吗?在这种情况下,编译器如何工作?它首先扫描整个文件的所有声明/定义,然后尝试取消引用函数/符号吗?
答案 0 :(得分:3)
首先,如果“如果我创建了两个函数而上面的那个函数引用了另一个函数,它会起作用吗?”你的意思是这样的:
int foo()
{
return bar();
}
int bar()
{
return 0;
}
然后编译器可以对bar()
进行有根据的猜测,但如果bar()
尚未声明,它至少会发出警告。对于无法调用的符号(如变量或类型),如果在声明它们之前使用它们,那将是一个彻头彻尾的错误。
在C中,无论何时使用标识符(无论标识符的类型如何:它可能是函数,变量,类型等),都应事先声明。您可以添加到任何标识符的各种修饰符(如您所说的static
,inline
以及所有其他标识符)对此没有任何影响。
请勿混淆声明和定义。声明只是告诉编译器存在名称;一个定义实际上告诉编译器它是什么。
例如,这是一个定义:
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) { }
如果您有两个功能a
和b
,他们会按照以下顺序互相调用并定义,{{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