编译没有主要功能的C代码

Compile C code without it's main function

所以我有几个文件用于我正在处理的大学项目;每个文件都有几个函数和一个用于测试目的的 main 函数。然而,一些文件需要使用其他文件的函数,这使得链接器对多个 main 声明的抱怨如预期的那样。我想知道是否有一种方法可以将这些文件编译为目标文件而无需一路编译 main 函数,基本上剥离掉 main 只留下可用的函数。

一个例子(不是我的一个很大的实际文件),我假设每个文件都存在一个头文件,其中分别包含 foobar 的声明:

//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 的可执行文件?

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

#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

使用其他文件(包括带有 "real" 应用程序 main 的文件)制作可执行文件,而不会抱怨多重定义的符号。

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

[..] my actual files which are quite large [..]

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

一个小例子:假设您有一个项目 foo 正在为您的大学做,其中一部分是实施 linked 列表。合理的做法是创建(至少)两个源文件,foo.clist.c,并将项目特定部分(即使只是一些测试)放入 foo.c 和关于将 linked 列表实施到 list.c 的所有代码。然后将 linked 列表的接口(即可能是一些 struct ... 声明,以及创建和修改列表的函数)放入头文件 list.h 中,您可以从任何文件中包含该文件需要 linked 列表,例如未来的项目 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) 分别编译每个源文件,link 将生成的目标文件一起编译:

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

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

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

b) Assemble 带有所需源文件的命令行并进行一次计算:

gcc -o foo foo.c list.c

你也可以用 make 做到这一点,例如使用变量 FOO_DEPENDENCIES := list.c

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

如果您使用 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 意味着链接器将使用 defaultMain 作为 main 如果 main 没有在别处提供。