【问题标题】:Compile C code without it's main function在没有 main 函数的情况下编译 C 代码
【发布时间】:2016-06-01 08:13:45
【问题描述】:

所以我有几个我正在从事的大学项目的文件;每个文件都有几个函数和一个main 函数用于测试目的。然而,一些文件需要使用其他文件的函数,这会导致链接器对多个 main 声明的投诉符合预期。我想知道是否有一种方法可以将这些文件编译为目标文件,而无需一路编译 main 函数,基本上是去掉 main 以只留下可用的函数。

一个例子(不是我的一个非常大的实际文件),对于每个我假设存在一个头文件,其中分别包含 foo 和 bar 的声明:

//File foo.c
#include <stdio.h>
#include <stdlib.h>
#include "foo.h"

int foo(int x, int y)
{
    return x + y;
}

//Assumes the user gives 2 integer inputs
int main(int argc, char **argv)
{
    int x, y;
    sscanf(argv[1], "%d", &x);
    sscanf(argv[2], "%d", &y);
    printf("foo(%d, %d) = %d\n", x, y, foo(x,y));
}

//File bar.c
#include <stdio.h>
#include <stdlib.h>
#include "foo.h"
#include "bar.h"

int bar(int x, int y, int z);
{
    return foo(x, y) * z;
}

//Assums the user gives 3 integer inputs
int main(int argc, char **argv)
{
    int x, y, z;
    sscanf(argv[1], "%d", &x);
    sscanf(argv[2], "%d", &y);
    sscanf(argv[3], "%d", &z);
    printf("bar(%d, %d, %d) = %d\n", x, y, z, bar(x, y, z));
}

是否可以将foo.c 编译为目标文件,以便我可以调用gcc bar.c foo.o 并获得bar.c 的可执行文件?

【问题讨论】:

  • 只编译,不链接...
  • 您可以将main 放入#ifdef TEST 之类的内容中。

标签: c compilation main


【解决方案1】:

您可以在您定义的符号上执行#ifdef,仅当您正在编译文件以生成“测试可执行文件”时,而不是当您与其他文件一起使用该文件时;我已经看到这被用于对小型库进行快速功能测试。

#include "foo.h"

int foo(int x, int y)
{
    return x + y;
}

#ifdef COMPILE_MAIN
//Assumes the user gives 2 integer inputs
int main(int argc, char **argv)
{
    int x, y;
    sscanf(argv[1], "%d", &x);
    sscanf(argv[2], "%d", &y);
    printf("foo(%d, %d) = %d\n", x, y, foo(x,y));
}
#endif

现在你可以做

gcc -DCOMPILE_MAIN foo.c -o foo.x

制作一个可执行的foo.x,你可以直接调用它来测试foo,你可以这样做

gcc foo.c bar.c main.c -o yourapp.x

使用其他文件(包括带有“真实”应用程序main 的文件)生成可执行文件,而不会抱怨多重定义的符号。

【讨论】:

  • 完美,我会试试这个,但这看起来正是我想要的。我正在整理一个 makefile 来完成所有这些工作,因此至少我不必一直输入这些内容。编辑:不过我明天必须试试这个,所以一旦我能够测试这个,我就会接受你的回答。
【解决方案2】:

如果您使用 gcc 或 clang 编译代码,您可以将测试 mains 声明为弱符号:

extern void main(int argc, char ** argv) __attribute__((weak));
void main(int argc, char ** argv) { ... }

弱main只会在没有强main时使用。

对于 Visual Studio(来自this answer):

// in foo.cpp
extern "C" void main(int argc, char ** argv;
extern "C" void defaultMainFoo(int argc, char ** argv)
// different name for each file
{ ... }
#pragma comment(linker, "/alternatename:_main=_defaultMainFoo");

/alternatename 表示如果main 未在其他地方提供,则链接器将使用defaultMain 代替main。

【讨论】:

    【解决方案3】:

    虽然将 #ifdef 放在 main 函数周围是可行的,但 IMO 这不是正确的方法,尤其是因为 ...

    [..] 我的实际文件很大 [..]

    合理的做法是将您的代码分成逻辑的、可重用的部分,并将这些(小)部分中的每一个放入单独的文件中。

    一个小例子:假设你有一个项目foo 正在为你的大学做,其中一部分是实现一个链表。合理的做法是创建(至少)两个源文件,foo.c 和 list.c,并将项目特定部分(即使它只是一些测试)放入 foo.c 以及有关实现的所有代码您的链接列表到list.c。然后将链接列表的接口(即可能是一些struct ... 声明,以及创建和修改列表的函数)放入头文件list.h,您可以从任何需要链接列表的文件中包含该文件,例如未来的项目bar:

    list.c

    struct list * create_list(void) {
       // awesome code
    }
    // and more awesome code
    

    list.h

    struct list {
       // an awesome list
    };
    struct list * create_list(void); // <- only a declaration
    // more awesome declarations
    

    foo.c

    #include "foo.h"
    int main() {
      // awesome use of the list
      return 0;
    }
    

    bar.c

    #include "foo.h"
    int main() {
      // also awesome
      return 0;
    }
    

    那么你可以:

    a)分别编译每个源文件,并将生成的目标文件链接在一起:

    gcc -c list.c
    gcc -c foo.c
    gcc -o foo foo.o list.o
    

    这种方式也可以用 make 完美表达,事实上至少在 GNU Make 中甚至有一条隐含的规则可以将 C 文件编译为目标文件 (%.o: %.c)。

    如果没有链接时间优化,您可能会失去使用这种方法的一些优化。如果您的项目需要不同的编译器标志,那么这不是一个好方法。

    b) 用所需的源文件组装一个命令行并进行一次计算:

    gcc -o foo foo.c list.c
    

    您也可以使用 make 来执行此操作,例如使用变量 FOO_DEPENDENCIES := list.c。

    请注意,使用此方法编译时间会长得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-16
      • 2016-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 1970-01-01
      相关资源
      最近更新 更多