Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用make&C实现多文件编译_C_Compiler Errors_Makefile - Fatal编程技术网

用make&C实现多文件编译

用make&C实现多文件编译,c,compiler-errors,makefile,C,Compiler Errors,Makefile,对于一个项目,我和一个合作伙伴一起编写了一个决策树实现。因为我们两个都是C语言的新手,而且必须快速工作,所以我们基本上把所有的功能都放在一个文件中,结果超过了1600行。这是一个快速而肮脏的项目,但现在下一个任务让我们负责扩展和重新实现代码。在目前的情况下,这是不会发生的 现在,我正在根据功能职责分解原始源代码。问题是,许多函数是相互交织的,我的make文件出现了重大错误。更具体地说,其他源文件报告在单独文件中声明的函数的隐式声明 我真的没有使用多个文件生成文件的经验。当前语法是从去年Syste

对于一个项目,我和一个合作伙伴一起编写了一个决策树实现。因为我们两个都是C语言的新手,而且必须快速工作,所以我们基本上把所有的功能都放在一个文件中,结果超过了1600行。这是一个快速而肮脏的项目,但现在下一个任务让我们负责扩展和重新实现代码。在目前的情况下,这是不会发生的

现在,我正在根据功能职责分解原始源代码。问题是,许多函数是相互交织的,我的make文件出现了重大错误。更具体地说,其他源文件报告在单独文件中声明的函数的隐式声明

我真的没有使用多个文件生成文件的经验。当前语法是从去年Systems Programming类中的一个简单shell实现中借用的,尽管当前项目的复杂性要高出一个数量级

cc= gcc
CFLAGS= -g -Wall -lm

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o proj2 main.o split.o tree.o id3.o output.o
我还尝试了以前的版本,其中每个对象文件都是单独编译的,如

main.o: main.c split.c tree.c id3.c output.c
  $(CC) $(CFLAGS) -o main.c split.c tree.c id3.c output.c
重复这个过程,为每个源创建一个.o文件,然后编译成一个可执行文件

然而,这不起作用,我收到了大约500行编译器投诉和警告,主要是关于隐式函数声明

因此,基本上我有两个相关的问题:

是否可以在不同的源文件之间交织函数调用? 如果是的话,我怎样才能在这里做到这一点?
您需要为每个源代码创建头文件,以便在其中包含声明。然后在源代码顶部包含相应的头文件。

您需要为每个源代码创建头文件,以便在其中包含声明。然后在源代码的顶部包含相应的头文件。

首先介绍一下您的makefile

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o proj2 main.o split.o tree.o id3.o output.o
如果代码是正确的,这应该可以工作,但是如果您使用的是GNUMake,那么您可以整理它:

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o $@ $^
现在您只需要维护对象列表的一个副本

另一个版本是错误的:

main.o: main.c split.c tree.c id3.c output.c
  $(CC) $(CFLAGS) -o main.c split.c tree.c id3.c output.c
首先,您试图将所有源文件编译成一个目标文件,这有点违背了目标文件的目的。第二,将一个对象文件命名为main.o,而该名称实际上应该属于由main.cc生成的对象文件。第三,该命令告诉编译器编译所有其他源文件split.c、tree.c、。。。进入一个名为main.c的对象文件-这并不违法,但你肯定会绊倒自己

也可以尝试使用C++,而不是C,但这是另一天。

现在是分手的时候了。我假设您知道如何将大函数分解为小函数,因此问题在于将函数分离为不同的源文件,然后正确编译和链接它们。假设main调用函数foo:

正如您所知,foo必须放在第一位,否则当main试图调用未声明的函数时,编译器会犹豫。但我们可以事先申报foo:

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}

void foo()
{
  // do foo things
}
当编译器调用foo时,它已经知道这样一个函数存在,并相信我们以后会定义它。现在有个诀窍:如果我们指示编译器编译,而不是链接,即生成一个像main.o这样的对象文件,而不是像proj2这样的可执行文件,它将更加信任我们:

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}
这将很好地编译成main.o。当我们将对象链接到一个可执行文件中时,编译器信任我们在其他对象文件中提供void foo的定义。定义将在另一个文件中,如下所示:

/* foo.c */

void foo()
{
  // do foo things
}
我们可以手工制作:

gcc -g -Wall -lm -c foo.c -o foo.o
gcc -g -Wall -lm -c main.c -o main.o
gcc -g -Wall -lm foo.o main.o -o proj2
但这很快就会变得单调乏味,因此我们将编写一个makefile:

cc= gcc
CFLAGS= -g -Wall -lm

proj2: main.o foo.o 
  $(CC) $(CFLAGS) -o $@ $^

%.o: %.c
  $(CC) $(CFLAGS) -c -o $@ $<

到目前为止还不错。如果这一点很清楚,那么我们可以继续讨论头文件…

首先介绍一下您的makefile

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o proj2 main.o split.o tree.o id3.o output.o
如果代码是正确的,这应该可以工作,但是如果您使用的是GNUMake,那么您可以整理它:

proj2: main.o split.o tree.o id3.o output.o 
  $(CC) $(CFLAGS) -o $@ $^
现在您只需要维护对象列表的一个副本

另一个版本是错误的:

main.o: main.c split.c tree.c id3.c output.c
  $(CC) $(CFLAGS) -o main.c split.c tree.c id3.c output.c
首先,您试图将所有源文件编译成一个目标文件,这有点违背了目标文件的目的。第二,将一个对象文件命名为main.o,而该名称实际上应该属于由main.cc生成的对象文件。第三,该命令告诉编译器编译所有其他源文件split.c、tree.c、。。。进入一个名为main.c的对象文件-这并不违法,但你肯定会绊倒自己

也可以尝试使用C++,而不是C,但这是另一天。

现在是分手的时候了。我假设您知道如何将大函数分解为小函数,因此问题在于将函数分离为不同的源文件,然后正确编译和链接它们。假设main调用函数foo:

正如您所知,foo必须放在第一位,否则当main试图调用未声明的函数时,编译器会犹豫 . 但我们可以事先申报foo:

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}

void foo()
{
  // do foo things
}
当编译器调用foo时,它已经知道这样一个函数存在,并相信我们以后会定义它。现在有个诀窍:如果我们指示编译器编译,而不是链接,即生成一个像main.o这样的对象文件,而不是像proj2这样的可执行文件,它将更加信任我们:

/* main.c */

void foo();

int main()
{
  // do main things
  foo();
  return(0);
}
这将很好地编译成main.o。当我们将对象链接到一个可执行文件中时,编译器信任我们在其他对象文件中提供void foo的定义。定义将在另一个文件中,如下所示:

/* foo.c */

void foo()
{
  // do foo things
}
我们可以手工制作:

gcc -g -Wall -lm -c foo.c -o foo.o
gcc -g -Wall -lm -c main.c -o main.o
gcc -g -Wall -lm foo.o main.o -o proj2
但这很快就会变得单调乏味,因此我们将编写一个makefile:

cc= gcc
CFLAGS= -g -Wall -lm

proj2: main.o foo.o 
  $(CC) $(CFLAGS) -o $@ $^

%.o: %.c
  $(CC) $(CFLAGS) -c -o $@ $<
到目前为止还不错。如果这一点很清楚,那么我们可以转到头文件…

这是-o main.c打字错误吗?它将用编译器的输出覆盖main.c文件。这是-o main.c打字错误吗?它将用编译器的输出覆盖main.c文件。