C头文件和源文件混淆问题

3
我写了一个小模块,希望能够重复使用。我有一个头文件bitstream.h,其中包含结构和函数声明,以及实现代码的bitstream.c。现在我想在其他程序中使用它,但是不想每次手动编译bitstream.c,就像你使用stdio.h时不必每次都编译一样,但我做不到。我的文件如下:

bitstream.c

#include "bitstream.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

bit_stream_t *fbit_stream(const char *path) {
    FILE *f;

    /* open file for reading */
    f = fopen(path, "rb");

    ...

bitstream.h

#ifndef BITSTREAM_H
#define BITSTREAM_H

#define EOS 0x0a

typedef struct {
    int size;
    int i_byte;
    unsigned char i_bit;
    unsigned char current_bit;
    unsigned char *bytes;
} bit_stream_t;

extern bit_stream_t *fbit_stream(const char *path);
extern unsigned char bit_stream_next(bit_stream_t *bs);
extern void bit_stream_close(bit_stream_t *bs);
extern void print_bit_stream(bit_stream_t *bs);

#endif

我将这两个文件放入 /usr/local/include 中(我在 Linux 机器上),现在我想在 main.c 中使用它(在其他地方,例如 /home/foo/main.c):

main.c

#include <bitstream.h>
#include <stdio.h>

int main(int argc, const char *argv[]) {
    if (argc != 2) {
        printf("Need 1 argument!\n");
        return 1;
    }

    bit_stream_t *my_bs;
    my_bs = fbit_stream(argv[1]);

    while (my_bs -> current_bit != EOS) {
        printf("%d", my_bs -> current_bit);
        bit_stream_next(my_bs);
    }

    bit_stream_close(my_bs);
    printf("\n");
    return 0;
}

当我尝试使用 gcc -Wall -o main.o main.c 时,我会得到以下结果:

/tmp/ccgdq66W.o: In function `main':
main.c:(.text+0x35): Undefined reference to `fbit_stream'
main.c:(.text+0x63): Undefined reference to `bit_stream_next'
main.c:(.text+0x7b): Undefined reference to `bit_stream_close'
collect2: error: ld returned 1 exit status

我做错了什么? 预先感谢任何帮助!

smuecke


1
错误来自于你的链接器,而不是编译器。这是因为你没有编译 bitstream.c,所以链接器找不到定义。 - teppic
stdio不仅仅是一个头文件。请查看Lie Ryan的回答。我希望这能帮到你。 - Missu
2个回答

3

将 bitstream.c 编译成 bitstream.o

gcc -Wall -c bitstream.c

将main.c和bitstream.o编译成main(如果在winX上则为main.exe)

gcc -Wall -o main main.c bitstream.o

3

你不需要每次都“编译”stdio.h的原因是它的实现(以及更多内容)已经被编译并存储在GLibC中。也就是说,glibc是一个库(动态库),GCC在每次编译程序时都会将其链接到该库中。您可以通过运行以下命令来检查:

ldd hello

假设hello是一个简单的Hello World!程序,它使用printf函数。你将得到类似下面的结果:
linux-vdso.so.1 =>  (0x00007fff5f98d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7e985b2000)
/lib64/ld-linux-x86-64.so.2 (0x00007f7e98977000)

看一下第二行:libc.so.6,那是你的GNU C库,也是你不必每次使用stdio.h时都“编译”它的原因。


通用情况

您想要做的是实现自己的库。为此,您可以采取以下几种方法。

  1. /usr/include下创建一个名为MyLib的目录,并将所有头文件放在其中。这样,您就可以使用#include <MyLib/myHeader.h>包含您的文件。

  2. 方法1:将您的实现myImp.c编译成对象文件,并将该对象放入文件夹/my/libs/path/myImp.o中,每次您想要使用它时,都要用以下命令进行编译:

    gcc -o prog prog.c /my/libs/path/myImp.o

    注意:prog.c必须#include <MyLibs/myHeader.h>

  3. 方法2:您可以使用实用程序ar创建存档,并将所有库放入其中。查看其手册。每次您想要使用库中的某些内容时,都要将其与.a库链接。

  4. 方法3:您可以创建动态库,这将减小二进制文件的大小(链接.o和ar库是静态链接,这意味着更大的二进制文件大小)。您可以使用-fPIC(位置无关代码)gcc标志和其他一些小技巧来实现。我会链接一些资源。


您的具体情况

  1. 使用gcc -c bitstream.c编译bitstream.c
  2. 将该文件放入/home/you/myLibs/bitstream.o
  3. bitstream.h复制到/usr/include/MyLib/bitstream.h(您需要root
  4. 使用#include <MyLib/bitstream.h>包含它
  5. 使用以下命令编译您的程序:gcc -o main main.c ~/myLibs/bitstream.o
尽管这远非完美,但可以满足您的需求。我建议您阅读有关如何组织自己的库以及其他更多信息,请参阅Rusty RusselCCAN库,它是构建C库的不错起点。
强烈推荐您查看CCAN!

资源


好的,据我所理解,当使用自己的库时,无论如何都必须添加/foo/bitstream.o,因为像stdiomath这样的东西在gcc中始终是“活着的”,没有其他方法可以避免。 - smuecke
1
@smuecke 不,它们在GCC中并不是“活的”。GCC是一个编译器套件。像stdio.h这样的头文件定义了库函数的原型。这些库使用GCC进行编译,并存储在系统文件中。例如,stdio.hglibc中。 - Enzo Ferber
1
@smuecke 不,没有绕过编译的目标文件/foo/bitstream.o/foo/bitstream.a(归档)或/lib/bitsream.so(共享)的方法。你需要这些文件才能告诉GCC将它们与你的程序链接起来。 - Enzo Ferber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接