使用放在另一个目录中的另一个C文件中的函数?

时间:2016-07-04 10:37:38

标签: c compilation linker

假设我有一个包含两个子目录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?

4 个答案:

答案 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比较上次编译模块的时间与上次修改时的模块比较,以及它知道需要编译什么以及什么不需要编译。