从 C 中同一目录中的另一个文件调用函数
我正在学习 C,但我在 Java 等高级编程语言方面拥有丰富的经验。
我正在阅读有关头文件的内容,因此我正在研究它们,但是我注意到我可以从另一个文件调用函数而无需 #include 它(它位于同一目录中),这怎么可能? 是这样配置的 make 文件、链接器还是什么?
我们有两个文件
main.c
add.c
main.c 从 add add.c 调用函数 add(int x,int y)
,但我在 #include add.c 之前错误地编译了它,它起作用了!更令人困惑的是,当我 #include add.c 时,它在函数 add 上给出了多重定义错误
I'm learning C, but i have a long experience with higher level programming languages like Java.
I was reading about header files so i was playing around with them, however I noticed that I could call a function from another file without #including it (it's in the same directory), how is that possible ?!
Is it the make file, linker that is configured that way or what ?
We have two files
main.c
add.c
main.c calls the function add(int x,int y)
from add add.c, but I mistakenly compiled before #including add.c and it worked ! What makes it more confusing is that when i #include add.c, it gives a multiple-definition error on function add
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
这里发生了一些不同的事情。首先,我将介绍多个文件的基本编译是如何工作的。
如果您有多个文件,重要的是函数的声明和定义之间的区别。定义可能是您在定义函数时所习惯的:您编写函数的内容,例如
声明,另一方面,让您向编译器声明您知道函数存在,但您不告诉编译器编译器是什么。例如,您可以编写
编译器会期望函数“square”在其他地方定义。
现在,如果您想要互操作两个不同的文件(例如,假设函数“square”在 add.c 中定义,并且您想在 main.c 中调用 square(10)),则需要同时定义和声明。首先,在 add.c 中定义 square。然后,您在 main.c 的开头声明它。这让编译器在编译 main.c 时知道有一个在其他地方定义的函数“square”。现在,您需要将 main.c 和 add.c 编译为目标文件。您可以通过调用来完成此操作
,这将生成文件 main.o 和 add.o。它们包含已编译的函数,但不完全可执行。这里要理解的重要一点是 main.o 在某种意义上是“不完整的”。编译main.o时,你告诉它函数“square”存在,但函数“square”没有在main.o内部定义。因此,main.o 对函数“square”有一种“悬空引用”。除非将其与另一个包含“square”定义的 .o(或 .so 或 .a)文件组合,否则它不会编译成完整的程序。如果您只是尝试链接 main.o 到程序中,即
您会收到错误,因为编译器将尝试解析对函数“square”的悬空引用,但找不到它的任何定义。但是,如果您在链接时包含 add.o(链接是在将 .o 文件转换为可执行文件或 .so 文件时解析所有这些对未定义函数的引用的过程),则不会有任何问题。这
就是如何在 C 文件中功能使用函数,但是风格,我刚刚向您展示的“不是正确的方式”。我这样做的唯一原因是因为我认为它会更好地帮助您了解正在发生的事情,而不是依赖“#include magic”。现在,您之前可能已经注意到,如果您必须在 main.c 顶部重新声明要使用的每个函数,事情会变得有点混乱,这就是为什么 C 程序经常使用名为“头”的帮助程序文件,该文件具有 .h 扩展名。标头的想法是它只包含函数的声明,不它们的定义。这样,为了使用 add.c 中定义的函数编译程序,您不需要手动声明正在使用的每个函数,也不需要在代码中 #include 整个 add.c 文件。相反,您可以#include add.h,它只包含add.c 的所有函数的声明。
现在,回顾一下#include:#include 只是将一个文件的内容直接复制到另一个文件中。例如,该代码
完全等同于
假设 wtf.txt 包含文本“hello world”。
因此,如果我们将 add.c 的所有声明放在 add.h 中(即
,然后在 main.c 的顶部,我们编写
这在功能上与我们刚刚在顶部手动声明函数“square”相同) 因此,使用标头
的一般想法是,您可以通过 #include 来自动声明所需的所有函数。
但是,标头还有一个更常见的用途。使用 50 个不同文件中的函数。 main.c 的顶部看起来像:
相反,人们经常将所有这些 #include 移动到 main.h 头文件中,并且只是从 main.c 中 #include main.h 在这种情况下,头文件服务于两个。 目的。它声明 main.c 中的函数,供其他文件包含时使用,并且当从 main.c 包含时,它包含 main.c 的所有依赖项。如果您#include,方式还允许链。 add.h,您不仅可以获得 add.c 中定义的函数,还可以隐式获得 add.c 使用的任何函数以及它们使用的任何函数,等等。
另外,更巧妙的是,#include 来自它自己的 .c 文件的头文件会隐式检查您所犯的错误。例如,如果您意外地
在 add.h 中定义了 square,那么您通常可能不会意识到,直到您链接 main.o 正在寻找 Square 的 一个 定义,而 add.o 正在提供 >另一个不兼容的。这将导致您在链接时出现错误,因此直到构建过程的后期您才会意识到错误。但是,如果您从 add.c 中 #include add.h 到编译器,则
在处理 #include 语句后,您的文件将类似于
编译器在编译 add.c 时会注意到的内容,并告诉您相关内容。实际上,以这种方式包含您自己的标头可以防止您向其他文件错误地宣传您所提供的功能类型。
为什么无需声明即可使用函数
正如您所注意到的,在某些情况下,您实际上可以使用函数而无需每次声明它或 #include 声明它的任何文件。这是愚蠢的,每个人都同意这是愚蠢的。然而,C 编程语言(和 C 编译器)的一个遗留功能是,如果您使用一个函数而不先声明它,它只是假设它是一个返回类型“int”的函数。因此,实际上,使用函数就是隐式将该函数声明为如果尚未声明则返回“int”的函数。如果你仔细想想,这是非常奇怪的行为,如果你这样做,编译器应该警告你。
标头防护
另一种常见做法是使用“标头防护”。为了解释头防护,让我们看一个可能的问题。假设我们有两个文件:herp.c 和 derp.c,它们都想要使用彼此包含的函数。遵循上述准则,您可能会得到一个带有该行的 herp.h
和一个带有该行的 derp.h
现在,如果您考虑一下,#include "derp.h" 将转换为 derp.h 的内容,其中依次包含#include "herp.h"行,它将被转换为herp.h的内容,并且that包含...等等,所以编译器将永远继续下去扩大包含范围。类似地,如果 main.h #includes herp.h 和 derp.h,并且 herp.h 和 derp.h 都包含 add.h,我们会看到在 main.h 中,我们最终得到两个 add.h 的副本,一份是 #include herp.h 的结果,一份是包含 derp.h 的结果。那么,解决办法呢? “标头防护”,即防止任何标头被 #included 两次的一段代码。例如,对于 add.h,执行此操作的正常方法是:
这段代码本质上是告诉预处理器(编译器中处理所有“#XXX”语句的部分)检查“ADD_H”是否已经存在定义的。如果不是(ifndef),那么它首先定义“ADD_H”(在这种情况下,ADD_H 不必定义为任何东西,它只是一个已定义或未定义的布尔值),然后定义标头的其余内容。但是,如果 ADD_H 已定义,则 #include 该文件将不会执行任何操作,因为 #ifndef 块之外没有任何内容。所以我们的想法是,只有第一次将其包含在任何给定文件中时,它才会实际向该文件添加任何文本。之后,#include 不会向您的文件添加任何其他文本。 ADD_H 只是您选择的任意符号,用于跟踪 add.h 是否已包含。对于每个标头,您使用不同的符号来跟踪它是否已包含。例如,herp.h 可能会使用 HEP_H 而不是 ADD_H。使用“标头保护”将解决我上面列出的任何问题,其中包含文件的重复副本或 #includes 的无限循环。
There's a few different things going on here. First I'll go over how basic compilation of multiple files works.
If you have multiple files, the important thing is the difference between the declaration and definition of a function. The definition is probably what you are used to when defining functions: You write up the contents of the function, like
The declaration, on the other hand, lets you declare to the compiler that you know a function exists, but you don't tell the compiler what it is. For example, you could write
And the compiler would expect that the function "square" is defined elsewhere.
Now, if you have two different files that you want to interoperate (for example, let's say that the function "square" is defined in add.c, and you want to call square(10) in main.c), you need to do both a definition and a declaration. First, you define square in add.c. Then, you declare it at the beginning of main.c. This let's the compiler know when it is compiling main.c that there is a function "square" which is defined elsewhere. Now, you need to compile both main.c and add.c into object files. You can do this by calling
This will produce the files main.o and add.o. They contain the compiled functions, but are not quite executable. The important thing to understand here is that main.o is "incomplete" in a sense. When compiling main.o, you told it that the function "square" exists, but the function "square" is not defined inside main.o. Thus main.o has a sort of "dangling reference" to the function "square". It won't compile into a full program unless you combine it with another .o (or a .so or .a) file which contains a definition of "square". If you just try to link main.o into a program, i.e.
You will get an error, because the compiler will try to resolve the dangling reference to the function "square", but wont find any definition for it. However, if you include add.o when linking (linking is the process of resolving all these references to undefined functions while converting .o files to executables or .so files), then there won't be any problem. i.e.
So that's how to functionally use functions across C files, but stylistically, what I just showed you is "not the right way". The only reason I did is because I think it will better help you understand what's going on, rather than relying on "#include magic". Now, you might have noticed before that things get a little messy if you have to redeclare every function you want to use at the top of main.c This is why often C programs use helper files called "headers" which have a .h extension. The idea of a header is that it contains just the declarations of the functions, without their definitions. This way, in order to compile a program using functions defined in add.c, you need not manually declare every function you are using, nor need you #include the entire add.c file in your code. Instead, you can #include add.h, which simply contains the declarations of all the functions of add.c.
Now, a refresher on #include: #include simply copies the contents of one file directly into another. So, for example, the code
is exactly equivalent to
assuming that wtf.txt contains the text "hello world".
So, if we put all the declarations of add.c in add.h (i.e.
and then at the top of main.c, we write
This is functionally the same as if we had just manually declared the function "square" at the top of main.c.
So the general idea of using headers is that you can have a special file that automatically declares all the functions you need by just #including it.
However, headers also have one more common use. Let's suppose that main.c uses functions from 50 different files. The top of main.c would look like:
Instead, people often move all those #includes to the main.h header file, and just #include main.h from main.c. In this case, the header file serves two purposes. It declares the functions in main.c for use when included by other files, and it includes all of the dependencies of main.c when included from main.c. Using it this way also allows chains of dependencies. If you #include add.h, not only do you get the functions defined in add.c, but you also implicitly get any functions which add.c uses, and any functions they use, and so on.
Also, more subtly, #including a header file from it's own .c file implicitly checks for errors you make. If for example, you accidentally defined square as
in add.h, you normally might not realize until you were linking that main.o is looking for one definition of square, and add.o is providing another, incompatible one. This will cause you to get errors when linking, so you won't realize the mistake until later in the build process. However, if you #include add.h from add.c, to the compiler, your file looks like
which after processing the #include statement will look like
Which the compiler will notice when compiling add.c, and tell you about. Effectively, including your own header in this way prevents you from falsely advertising to other files the type of the functions you are providing.
Why you can use a function without ever declaring it
As you have noticed, in some cases you can actually use a function without every declaring it or #including any file which declares it. This is stupid, and everyone agrees that this is stupid. However, it is a legacy feature of the C programming language (and C compilers) that if you use a function without declaring it first, it just assumes that it is a function returning type "int". So in effect, using a function is implicitly declaring that function as a function which returns "int" if it is not already declared. It's very strange behavior if you think about it, and the compiler should warn you if you it doing that behavior.
Header Guards
One other common practice is the use of "Header Guards". To explain header guards, let's look at a possible problem. Let's say that we have two files: herp.c, and derp.c, and they both want to use functions contained in each other. Following the above guidelines, you might have a herp.h with the line
and a derp.h with the line
Now, if you think about it, #include "derp.h" will be converted to the contents of derp.h, which in turn contains the line #include "herp.h", which will be converted to the contents of herp.h, and that contains... and so on, so the compiler will go on forever just expanding the includes. Similarly, if main.h #includes both herp.h and derp.h, and both herp.h and derp.h include add.h, we see that in main.h, we end up with two copies of add.h, one as a result of #including herp.h, and one as a result of including derp.h. So, the solution? A "Header guard", i.e. a piece of code which prevents any header from being #included twice. For add.h, for example, the normal way to do this is:
This piece of code is essentially telling the preprocessor (the part of the compiler which handles all of the "#XXX" statements) to check if "ADD_H" is already defined. If it isn't (ifndef) then it first defines "ADD_H" (in this context, ADD_H doesn't have to be defined as anything, it is just a boolean which is either defined or not), and then defines the rest of the contents of the header. However, if ADD_H is already defined, then #including this file will do nothing, because there is nothing outside of the #ifndef block. So the idea is that only the first time it is included in any given file will it actually add any text to that file. After that, #including it will not add any additional text to your file. ADD_H is just an arbitrary symbol you choose to keep track of whether add.h has been included yet. For every header, you use a different symbol to keep track of whether it has been included yet or not. For example, herp.h would probably use HERP_H instead of ADD_H. Using a "header guard" will fix any of the problems I listed above, where you have duplicate copies of a file included, or an infinite loop of #includes.
问题是您不应该
#include
.c 文件。要在另一个文件中使用函数,您需要声明它。通常,每个 .c 文件(main.c 除外)都有一个关联的头文件 (.h),该文件正确声明了 .c 文件中定义的所有函数。您可以声明任意多次(只要所有声明都相同),但只能有一个定义。
当您
#include "add.c"
时会发生什么,add.c 的文本包含在 main.c 中,给出 main.ca 定义(并且,作为add
的副作用(声明)。然后,当您单独编译 add.c 时,会创建add
的另一个定义。因此,该函数有两个定义,编译器会因为不知道使用哪一个而感到抓狂。如果将其更改为
#include "add.h"
,其中 add.h 看起来像这样:那么 main.c 就有一个
add
声明,并且可以使用该函数,但是add
的定义非常明确地只存在于 add.c 文件中,因此它只存在一次,因此它将正确编译。The problem is that you shouldn't be
#include
ing a .c file.To use a function in another file, you need to declare it. Usually, every .c file (except main.c) has an associated header (.h) file that properly declares all the functions defined in the .c file. You can declare as many times as you want (so long as all the declarations are identical), but there can only be one definition.
What happens when you
#include "add.c"
is that the text of add.c is included in main.c, giving main.c a definition (and, as a side-effect, a declaration) ofadd
. Then, when you compile add.c on it's own, that creates another definition ofadd
. Thus, there are two definitons of the function, and the compiler freaks out because it doesn't know which one to use.If you change it to
#include "add.h"
, where add.h looks something like this:then main.c has a declaration of
add
and can use the function, but the definition ofadd
is quite firmly only in the add.c file, and so it only exists once, and so it will compile properly.这是一个从不同 C 程序调用函数的简单示例,
让我将主程序命名为 main.c,将保存该函数的程序命名为 function.c for the function.c 我正在创建名为 function.h
main 的 头文件.c
函数
.c 函数.h
使用下面给出的命令进行编译
g++ main.c function.c -o main
这里有详细的解释。在主程序中,我调用了对 2 个数字求和的函数。
主程序中的值 1 和 2 通过标头 function.h 提供给 function.c 中的函数,该标头保存了 function.c 的访问点或桥接器
有关更多详细信息,您可以查看下面给出的链接
http://www.cplusplus.com/forum/beginner/34691/
https://social.msdn.microsoft.com/Forums/en-US/4ea70f43-a0d5-43f8- 8e24-78e90f208110/从另一个文件调用文件中的函数?forum=winembplatdev
添加print 语句来检查结果或使用 echo $?执行文件 main 后
Here is a simple example of calling a function from different c program
let me name the main program as main.c and the program that holds the function as function.c for the function.c I am creating the header file called function.h
main.c
function.c
function.h
To compile use the command given below
g++ main.c function.c -o main
Here the detailed explanation. In the main program I have called the function to sum 2 numbers.
The values 1 and 2 in the main program was feed to the function in the function.c through the header function.h which holds the access point or the bridge to the function.c
For more details you can check the links given below
http://www.cplusplus.com/forum/beginner/34691/
https://social.msdn.microsoft.com/Forums/en-US/4ea70f43-a0d5-43f8-8e24-78e90f208110/calling-a-function-in-a-file-from-another-file?forum=winembplatdev
Add a print statement to check the result or use echo $? after execution of the file main
您之所以能够调用它,是因为在 C 中进行调用不需要声明。但是返回类型未知,因此默认为
int
。这在一定程度上是可能的,因为 C 中的默认调用约定以及类型默认提升到至少int
精度。如果包含定义要调用的函数的标头,编译器就能够检查对该函数的调用是否具有正确的参数数量和类型。
如果您包含函数定义,除非您使用
static
指定其存储,否则它们将被导出。由于您还要编译和链接add.c
,因此您无法添加此文件,因为您的目标文件中的任何一个或两个都不会导出add
。如果您想简单地包含所有函数,最好将它们的定义放入标头中,并在它们上面添加存储说明符。
You're able to call it because a declaration isn't necessary to make a call in C. The return type however is unknown, and so will default to
int
. This is possible in part due to the default calling convention in C, and the default promotion of types to at leastint
precision.If you include a header that defines the function you are calling, the compiler is able to check that calls to the function have the correct number and type of arguments.
If you include function definitions, they will be exported unless you specify their storage with
static
. Since you're also compiling and linkingadd.c
, you can't add this since then either neither or both of your object files will then be exportingadd
.If you want to simply include all your functions, best to put their definitions into headers, and sprinkle storage specifiers on them.