【问题标题】:Calling methods from multiple C files without custom header files, Makefile Linking从多个 C 文件调用方法,无需自定义头文件,Makefile Linking
【发布时间】:2012-10-25 18:47:17
【问题描述】:

这在翻译中有点丢失,所以我会更准确: 我们有类 recursion.c、fib.c 和 countUp.c。从 recursion.c 我们必须递归调用 fib.c 或 countUp.c,由输入参数决定。我不能使用头文件,只能放置原型:

int fib(int n);

void countUp(int n);

我的 Makefile

TAR = tar
COMPILER_FLAGS = -g -Wall -std=c99 -c
LINKER_FLAGS = -g -o
OBJS = recurse.o
C_FILES = recurse.c fib.c countUp.c
ASM_FILES = recurse.asm
TARGET_FILE = recurse
TARGET_TAR = PA5.tar

$(TARGET_TAR): $(TARGET_FILE)
    $(TAR) -cvf $(TARGET_TAR) $(C_FILES) $(ASM_FILES) $(TARGET_FILE) Makefi$

recurse.o: recurse.c
    $(C_COMPILER) $(COMPILER_FLAGS) $(C_FILES)

$(TARGET_FILE): $(OBJS)
    $(LD_LINKER) $(LINKER_FLAGS) $(TARGET_FILE) $(OBJS)

其中 fib 和 countUp 类方法必须递归调用。 recursive.c 文件被认为是我们的 c 驱动程序。不要创建或实现除标准 c 头文件(stdio.h、string.h 等)之外的任何头文件。当我尝试运行它时,我得到:

make
gcc -g -o recurse recurse.o
recurse.o: In function `main':
(file root location)/recurse.c:43: undefined reference to `fib'
(file root location)/recurse.c:46: undefined reference to `countUp'
collect2: ld returned 1 exit status
make: *** [recurse] Error 1

任何线索。

原问题: 我有多个 C 文件,我将它们组合成一个可执行文件。例如说我有 math.c,参数被传递给它,然后如果输入参数调用 add 它执行 add.c 中的函数,如果参数调用 subtract 它将调用 subtract.c 中的函数等。文件然后编译成 .o 文件,然后创建一个可执行文件。我遇到的问题是无法使用标头 (.h) 文件。有什么办法可以闯入单独的班级还是我错过了什么?我真的不知道如何问这个问题,就 C 而言,行话很糟糕,抱歉:(

我想我真的不明白司机的想法。 (不是设备驱动,她一直告诉我们这是一个 c 可执行驱动)。

【问题讨论】:

  • 我遇到的问题是无法使用标头 (.h) 文件 - 为什么会出现这种情况?你当然可以#include 任何你喜欢的文件类型,但它可能会导致名称冲突等等,通常被认为是不好的做法。
  • 这是家庭作业吗?在这种情况下,您的老师可能试图让您展示您对“外部”关键字的掌握。
  • 抱歉忘记标记作业,不是在寻找确切的答案。我们必须在没有 .h 文件的情况下在我们的硬件任务中执行它,这是我迷路的地方。我们正在练习递归,我们必须从我们的 recursive.c 主文件中调用递归 .c 文件。还没有在课堂上与外部人员合作过
  • 作业标签正在被移除;你可以指定你不是在你的问题中寻找确切的答案。你能告诉我们你正在使用什么代码吗?你不能#include 似乎不对。用gcc -E 编译(它只做预处理,但没有别的),看看你的头文件是否被拉入。
  • 这太本地化了,在实际应用中没有任何用途,对以后的读者也没有帮助。

标签: c methods compilation prototype


【解决方案1】:

如果我理解正确,我认为您需要做的是在您在recursion.c 中定义的任何函数之上添加以下原型。原型将允许您从 recursion.c 内的任何函数中调用这些函数(事实上,包括头文件类似于复制粘贴文件中定义的所有原型,正如 @Justin 和 @EdS 已经指出的那样)

int fib(int n);
void countUp(int n);

int main() {
 ...
}

然后您需要确保您的项目文件包含文件 recursion.c、fib.c 和 countUp.c - 当您构建您的项目时,链接器将完成其工作和查找编译的目标文件中的入口点,并将继续组装单个可执行文件。

你用的是什么编译器?

【讨论】:

  • 这似乎更正确,但我想我的问题是当我调用原型中的方法时,它实际上是在执行相应类中的方法吗?海合会
  • 如果按类是指每个源文件(例如 fib.c 和 countUp.c),是的。 C 中的编译阶段分为两个阶段。首先,编译器会从上到下分别处理项目中的每个文件。在这个过程中,只要找到一个函数,它就会注册它的名字、返回类型和参数(这称为签名)等等,允许从另一个函数调用这个函数如果它是签名就不能调用一个函数编译器不知道。这在源代码中强加了严格的顺序,其中另一个函数调用的函数必须在它之前。
  • 这种排序是不切实际的,这就是函数原型发挥作用的地方。原型允许您输入函数签名,而无需实际指定函数体。因此,包含头文件的过程只是为编译器提供了一个可以调用函数的起点。这消除了严格的订购要求。但是,编译器并不限制您调用的函数必须驻留在相同的源代码中。此代码可能在另一个 .c 文件中,甚至在预编译库中。
  • 这是第二阶段,名为“链接”的地方。链接器将搜索源代码中使用的所有签名,并尝试将它们与函数的入口点进行匹配您的项目(同样,它们可以在其他源文件或项目中包含的库中)。如果所有签名都找到了对应的匹配项,则项目将链接并生成可执行文件,否则链接器将通知您它找不到特定的函数实现并无法生成可执行文件。
  • 希望我的解释没有混淆。随时问!
【解决方案2】:

包含头文件只是一个预处理器指令,用于在包含位置包含该文件的内容。要在没有头文件的情况下实现相同的效果,只需将您在头文件中放入的代码复制并粘贴到每个 c 文件的顶部即可。

当然,这不是很容易维护,就像你想改变你需要在许多文件中改变它的内容一样,这就是为什么头文件首先存在的原因。

【讨论】:

  • 抱歉应该澄清一下,不能使用任何类型的标头实现。我们可以做的就是将原型方法放在我们的主函数之上,然后调用我们的主函数。我想我得去和她谈谈,这件事看起来真的很时髦
  • @ZachCaudle 这就是我的意思——在这种情况下,您唯一需要放入头文件的就是原型
【解决方案3】:

由于这是家庭作业,并且考虑到您告诉我们 A) 您没有要使用的头文件,并且 B) 您没有被指示使用 extern 关键字,在我看来,您唯一的选择是包含 .c 文件本身:

#include "add.c"
#include "subtract.c"
/* etc... */

int main()
{
    // use functions defined in "add.c", "subtract.c", etc.
}

请注意,这是一种糟糕的形式,因为您包含的是实现而不是接口,并且可能会引入一堆您不想要或不需要的东西。如果这不能回答您的问题,那么问题中缺少某些东西,您老师的一些指示。

【讨论】:

  • 你不需要extern关键字来声明函数,只需要外部变量。
  • @larsmans:是的,我知道,我只是想在这里覆盖我的所有基础,因为很难弄清楚这项任务背后的原因。
猜你喜欢
  • 2020-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-14
  • 1970-01-01
  • 2021-05-17
  • 1970-01-01
  • 2015-01-04
相关资源
最近更新 更多