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

时间:2016-02-19 16:59:34

标签: c compilation main

所以我有一些我正在研究的大学项目的文件;每个文件都有几个函数和一个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的可执行文件?

3 个答案:

答案 0 :(得分:2)

只有在编译文件以生成&#34;测试可执行文件&#34;时才可以对您定义的符号执行#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

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

虽然将#ifdef main功能放在你的foo功能上,但IMO并不是正确的方法,尤其是因为......

  

[..]我的实际文件非常大[..]

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

一个小例子:假设你有一个你为大学做的项目foo.c,其中一部分是实施一个链表。合理的做法是创建(至少)两个源文件list.cfoo.c,并将项目特定部分(即使它只是一些测试)放入{{ 1}}以及关于将链接列表实现到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中,甚至是将C文件编译为目标文件(%.o: %.c)的隐含规则。 / p>

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

b)使用所需的源文件组装命令行并执行单个计算:

gcc -o foo foo.c list.c

您也可以使用make来实现此功能,例如使用变量FOO_DEPENDENCIES := list.c

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