C - GCC "multiple definition of "错误



所以我有这三个文件

Main.c

#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include "support.h"
int main( void ) {
    int* num1 = malloc(100);
    printf("num1: %p", &num1);
}

支持.c

#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "support.h"
void *malloc(size_t size) {
    struct block_meta *block;
    if (size <= 0) {
        return NULL;
    }
    if (!global_base) { // First call.
    block = request_space(NULL, size);
        if (!block) {
            return NULL;
        }
        global_base = block;
    } else {
        struct block_meta *last = global_base;
        block = find_free_block(&last, size);
        if (!block) { // Failed to find free block.
            block = request_space(last, size);
            if (!block) {
                return NULL;
            }
        } else { // Found free block
            block->free = 0;
            block->magic = 0x77777777;
        }
    }
    return(block+1);
}

void free(void *ptr) {
    if (!ptr) {
        return;
    }
    struct block_meta* block_ptr = get_block_ptr(ptr);
    assert(block_ptr->free == 0);
    assert(block_ptr->magic == 0x77777777 || block_ptr->magic == 0x12345678);
    block_ptr->free = 1;
    block_ptr->magic = 0x55555555;
}
void *realloc(void *ptr, size_t size) {
    if (!ptr) {
        // NULL ptr. realloc should act like malloc.
        return malloc(size);
    }
    struct block_meta* block_ptr = get_block_ptr(ptr);
    if (block_ptr->size >= size) {
        // We have enough space. Could free some once we implement split.
        return ptr;
    }
    // Need to really realloc. Malloc new space and free old space.
    // Then copy old data to new space.
    void *new_ptr;
    new_ptr = malloc(size);
    if (!new_ptr) {
        return NULL; // TODO: set errno on failure.
    }
    memcpy(new_ptr, ptr, block_ptr->size);
    free(ptr);
    return new_ptr;
}
void *calloc(size_t nelem, size_t elsize) {
    size_t size = nelem * elsize; // TODO: check for overflow.
    void *ptr = malloc(size);
    memset(ptr, 0, size);
    return ptr;
}

支持.h

#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void *malloc(size_t size);
void free(void *ptr);
void *realloc(void *ptr, size_t size);

struct block_meta {
    size_t size;
    struct block_meta *next;
    int free;
    int magic; // For debugging only. TODO: remove this in non-debug mode.
};
#define META_SIZE sizeof(struct block_meta)
void *global_base = NULL;
struct block_meta *find_free_block(struct block_meta **last, size_t size) {
    struct block_meta *current = global_base;
    while (current && !(current->free && current->size >= size)) {
        *last = current;
        current = current->next;
    }
    return current;
}
struct block_meta *request_space(struct block_meta* last, size_t size) {
    struct block_meta *block;
    block = sbrk(0);
    void *request = sbrk(size + META_SIZE);
    assert((void*)block == request); // Not thread safe.
    if (request == (void*) -1) {
        return NULL; // sbrk failed.
    }
    if (last) { // NULL on first request.
        last->next = block;
    }
    block->size = size;
    block->next = NULL;
    block->free = 0;
    block->magic = 0x12345678;
    return block;
}
struct block_meta *get_block_ptr(void *ptr) {
    return (struct block_meta*)ptr - 1;
}

然而,当我尝试使用进行编译时

gcc -o asgn2 main.c support.c

我得到错误

/tmp/ccscmcbS.o:(.bss+0x0): multiple definition of `global_base'
/tmp/ccyjhjQC.o:(.bss+0x0): first defined here
/tmp/ccscmcbS.o: In function `find_free_block':
support.c:(.text+0x0): multiple definition of `find_free_block'
/tmp/ccyjhjQC.o:main.c:(.text+0x0): first defined here
/tmp/ccscmcbS.o: In function `request_space':
support.c:(.text+0x55): multiple definition of `request_space'
/tmp/ccyjhjQC.o:main.c:(.text+0x55): first defined here
/tmp/ccscmcbS.o: In function `get_block_ptr':
support.c:(.text+0xfe): multiple definition of `get_block_ptr'
/tmp/ccyjhjQC.o:main.c:(.text+0xfe): first defined here
collect2: error: ld returned 1 exit status

我不相信我不止一次声明了这些方法,而且它的格式与通常给出的格式大不相同。不太清楚这意味着什么。

问题是在头文件中定义了的函数和全局变量(而不是声明的)。因此,在编译这些函数时,它们被拉入main.c和support.c中。然后在链接阶段,链接器会看到多个定义。

即使您包含了保护程序,在这种情况下也无济于事,因为它只保护单个编译单元中的多个定义,而不是跨多个单元。

从头文件中取出这些函数的定义,用声明替换它们,并将它们放在support.c或单独的.c文件中。

您可以为gcc使用-fcommon选项。

确保标头只包含一次,因此在标头源代码中添加以下内容:

#ifndef _HAVE_SUPPORT_H
#define _HAVE_SUPPORT_H
// ...
// YOUR HEADER SOURCE CODE
// ...

#endif //_HAVE_SUPPORT_H

正如我所说,这确保了头只包含一次,因为它定义了_HAVE_SUPPORT_H。如果现在另一个源试图包含它,它将不会执行任何操作,因为已经定义了_HAVE_SUPPRORT_H

如果头中只有函数声明,并且"real"函数将在另一个*.c文件中,这也会有所帮助。

编辑:对于您的问题,第二部分是最重要的,因为@kaylum注意到

对我来说,解决方案很简单,降级到以前的GCC版本。以下是安装在两个不同Ubuntu版本上的gcc的比较。

  • ubuntu 20.04的GCC:https://packages.ubuntu.com/focal/gcc(gcc 9)
  • ubuntu 22.04的GCC:https://packages.ubuntu.com/jammy/gcc(gcc 11)

因为在我的案例中,代码是大约20年前的遗留代码,所以继续使用旧的编译器对我来说是有意义的。

还有一个使函数静态的可能解决方案。

最新更新