假设我有一个包含两个子目录B和C的父目录A.
子目录C有一个helper.c和helper.h,如下所示:
//helper.c
void print(){
printf("Hello, World!\n");
}
//helper.h
void print();
现在,在子目录B中,我有一个main.c,它只调用print函数:
//main.c
#include<stdio.h>
#include"../C/helper.h"
void main(){
print();
}
我尝试了以下命令来编译main.c:
Command 1: gcc main.c //Gives undefined reference to 'print' error
Command 2: gcc main.c ../C/helper.c //Compiles successfully
现在我删除了#include&#34; ../ C / helper.h&#34;从主.c再次尝试命令2。它仍然有效。
所以我有以下问题:
i)是否包含helper.h文件会有什么不同 helper.c?
ii)为什么命令1失败?
iii)有没有办法编译我的C程序而无需指定 每次都是helper.c?
答案 0 :(得分:1)
首先,您需要了解#include
只是将#include
参数中的任何文本添加到语句所在文件中的位置,例如:
//file1.h
void foo();
//main.c
#include "file1.txt"
int main(int argc, char **argv)
{
foo();
return 0;
}
将导致预编译生成此统一文件以进行编译:
//main.c.tmp
void foo();
int main(int argc, char **argv)
{
foo();
return 0;
}
所以回答 第一和第二个问题 :
当您包含仅包含没有定义的声明(即函数签名)的头文件(或任何文件)(即函数实现)时,如上例所示,链接器将无法找到定义和你会得到未定义的参考文献&#39;错误。
当您包含包含定义的c代码文件(或任何文件)时,这些定义将合并到您的代码中,链接器将包含它们,这就是它工作的原因。
以及 第三个问题 将c文件直接包含在其他c文件中是不好的做法,常见的方法是保留单独的c文件,其中包含暴露其提供的功能的头文件,包括头文件和链接编译的c文件,例如在你的情况下: / p>
gcc main.c helper.c -o out
允许你在main.c中包含helper.c并且仍然有效,因为你指示编译器编译两个文件而不仅仅是main.c
所以当链接发生时,将找到编译中的定义,你将会没有得到undefined behavior
错误
简而言之,这就是。我抽象了许多正在传递的概念。 this是一篇很好的文章,详细描述了编译过程,this是对整个过程的一个很好的概述。
答案 1 :(得分:1)
执行时会发生什么:
CustomHwException
执行gcc main.c时 编译器编译main.c并创建目标文件。该文件将包含函数print()的未解析链接。因为main.c文件中没有函数print()的实现。 编译后gcc尝试制作完整的可执行文件。为此,gcc将所有目标文件组合在一起,并尝试解析所有未解析的链接。当你记得函数print()有未解决的链接时,gcc无法找到实现并引发错误。
执行时
Command 1: gcc main.c //Gives undefined reference to 'print' error
gcc编译这两个文件。第二个文件../C/helper.c包含函数print()的实现,因此链接器可以在函数main()中找到它并解析对它的引用。
i)是否包含helper.h文件或helper.c有什么区别?
在你的情况下,helper.h包含函数print()的前向声明。这为编译器提供了如何调用函数print()的信息。
ii)为什么命令1失败?
见上文。
iii)有没有办法编译我的C程序而不必每次都指定helper.c?
使用make实用程序。在单独的目标文件helper.o中编译helper.c并在链接命令中使用它。
Command 2: gcc main.c ../C/helper.c //Compiles successfully
有关详细信息,请参阅make utility manual。 命令应该由TAB缩进。
答案 2 :(得分:0)
我会尽力回答:
i)是否包含helper.h文件或helper.c有什么区别?
当您包含文件时,您不希望公开您的实现,因此最好包含h
文件,其中只包含“签名” - 您实现的API。
ii)为什么命令1失败?
编译时,必须将所有资源添加到可执行文件中,否则他将无法编译。
iii)有没有办法编译我的C程序而无需指定 helper.c everytime?
Makefile
编译您的程序。也许this教程可以帮助你。答案 3 :(得分:0)
i)是否包含helper.h文件会有什么不同 或helper.c?
包含helper.c意味着每次都会编译helper.c,就好像它是main.c的一部分一样。
包含helper.h让编译器知道函数print()采用和返回的参数类型,这样如果你不正确地调用print(),编译器就会给出错误或警告
ii)为什么命令1失败?
没有告诉编译器在哪里可以找到打印功能的实际代码。如上所述,包括.h文件只能帮助编译器进行类型检查。
iii)有没有办法编译我的C程序而无需指定 helper.c everytime?
您可以将其编译一次到目标文件中,也可以选择将该obj添加到静态或动态加载的库中。您仍然需要帮助编译器找到obj或库。例如,
gcc -c helper.c
gcc main.c helper.o
避免编译不需要编译的模块的正确方法是使用Makefile。 Makefile比较上次编译模块的时间与上次修改时的模块比较,以及它知道需要编译什么以及什么不需要编译。