代码之家  ›  专栏  ›  技术社区  ›  PAH

如何解决Makefile错误的多重定义

  •  0
  • PAH  · 技术社区  · 2 年前

    当我用make编译我的C程序时,我得到了一个错误。我已经开发这个项目几天了,在我需要添加一个外部文件(semaphore_v2.h)之前,一切都编译得很好。我不知道是不是导入错误,但当我试图用它编译项目时,它没有编译。下面的一些信息。

    PD:如果重新定义的警告“__USE_XOPEN”是我不知道如何修复它的原因。另一个错误是对“fmin”的未定义引用,可以忽略,我知道如何解决它。

    我已经检查了.h文件的警卫。我还检查过我没有多次插入,也没有包括.c文件。我猜这个错误一定是因为我不知道如何解决的警告,或者我在更新makefile时出错了,但我没有看到它。

    出错 (我已经包含了发生错误的详细模式):

    > gcc -c bowman.c -gdb -Wall -Wextra
    > gcc -c utils.c -gdb -Wall -Wextra
    > gcc bowman.o utils.o -o bowman -ggdb -Wall -Wextra
    > gcc -c poole.c -ggdb -Wall -Wextra -lpthread
    > In file included from request_queue.h:7,
    > from poole.c:8:
    > semaphore_v2.h:19: warning: "__USE_XOPEN" redefined
    > 19 | #define __USE_XOPEN
    > 
    > In file included from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
    > from /usr/include/stdio.h:27,
    > from poole.c:3:
    > /usr/include/features.h:355: note: this is the location of the previous definition
    > 355 | # define __USE_XOPEN    1
    > 
    > poole.c: In function ‘worker’:
    > poole.c:26:20: warning: unused parameter ‘args’ [-Wunused-parameter]
    > 26 | void *worker(void *args){
    >       |              ~~~~~~^~~~
    > poole.c: In function ‘process_new_bowman’:
    > poole.c:159:30: warning: unused parameter ‘data’ [-Wunused-parameter]
    > 159 | int process_new_bowman(char *data, int fd){
    >       |                        ~~~~~~^~~~
    > gcc -c request_queue.c -ggdb -Wall -Wextra -lpthread
    > gcc -v poole.o utils.o request_queue.o -o poole -ggdb -Wall -Wextra -lpthread
    > Using built-in specs.
    > COLLECT_GCC=gcc
    > COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper
    > OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
    > OFFLOAD_TARGET_DEFAULT=1
    > Target: x86_64-linux-gnu
    > Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.4.0-1ubuntu1~22.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
    > Thread model: posix
    > Supported LTO compression algorithms: zlib zstd
    > gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04) 
    > COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/
    > LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/11/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/11/../../../:/lib/:/usr/lib/
    > COLLECT_GCC_OPTIONS='-v' '-o' 'poole' '-ggdb' '-Wall' '-Wextra' '-mtune=generic' '-march=x86-64' '-dumpdir' 'poole.'
    >  /usr/lib/gcc/x86_64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccYawT3g.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o poole /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/11/../../.. poole.o utils.o request_queue.o -lpthread -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o
    > /usr/bin/ld: request_queue.o: in function `SEM_constructor_with_name':
    > /home/guille/git-repos/HAL9000System/semaphore_v2.h:78: multiple definition of `SEM_constructor_with_name'; poole.o:/home/guille/git-repos/HAL9000System/semaphore_v2.h:78: first defined here
    > /usr/bin/ld: request_queue.o: in function `SEM_constructor':
    > /home/guille/git-repos/HAL9000System/semaphore_v2.h:100: multiple definition of `SEM_constructor'; poole.o:/home/guille/git-repos/HAL9000System/semaphore_v2.h:100: first defined here
    > /usr/bin/ld: request_queue.o: in function `SEM_init':
    > /home/guille/git-repos/HAL9000System/semaphore_v2.h:115: multiple definition of `SEM_init'; poole.o:/home/guille/git-repos/HAL9000System/semaphore_v2.h:115: first defined here
    > /usr/bin/ld: request_queue.o: in function `SEM_destructor':
    > /home/guille/git-repos/HAL9000System/semaphore_v2.h:127: multiple definition of `SEM_destructor'; poole.o:/home/guille/git-repos/HAL9000System/semaphore_v2.h:127: first defined here
    > /usr/bin/ld: request_queue.o: in function `SEM_wait':
    > /home/guille/git-repos/HAL9000System/semaphore_v2.h:141: multiple definition of `SEM_wait'; poole.o:/home/guille/git-repos/HAL9000System/semaphore_v2.h:141: first defined here
    > /usr/bin/ld: request_queue.o: in function `SEM_signal':
    > /home/guille/git-repos/HAL9000System/semaphore_v2.h:159: multiple definition of `SEM_signal'; poole.o:/home/guille/git-repos/HAL9000System/semaphore_v2.h:159: first defined here
    > /usr/bin/ld: poole.o: in function `worker':
    > /home/guille/git-repos/HAL9000System/poole.c:43: undefined reference to `fmin'
    > collect2: error: ld returned 1 exit status
    > make: *** [makefile:16: poole] Error 1
    

    这个 制作文件 如下所示:

    > all: bowman Poole discovery 
    > 
    > bowman: bowman.o utils.o
    >   gcc bowman.o utils.o -o bowman -ggdb -Wall -Wextra
    > 
    > bowman.o: bowman.c utils.h
    >   gcc -c bowman.c -ggdb -Wall -Wextra
    > 
    > discovery: discovery.o utils.o
    >   gcc discovery.o utils.o -o discovery -ggdb -Wall -Wextra
    > 
    > discovery.o: discovery.c utils.h
    >   gcc -c discovery.c -ggdb -Wall -Wextra
    > 
    > poole: poole.o utils.o request_queue.o
    >   gcc -v poole.o utils.o request_queue.o -o poole -ggdb -Wall -Wextra -lpthread
    > 
    > poole.o: poole.c utils.h request_queue.h
    >   gcc -c poole.c -ggdb -Wall -Wextra -lpthread
    > 
    > request_queue.o: request_queue.c request_queue.h
    >   gcc -c request_queue.c -ggdb -Wall -Wextra -lpthread
    > 
    > utils.o: utils.c utils.h
    >   gcc -c utils.c -ggdb -Wall -Wextra
    > 
    > .PHONY: clean
    > clean:
    >   rm *.o
    >   rm bowman
    >   rm poole
    >   rm discovery
    

    以下是项目中每个文件中包含的标题:

    utils.h :

    #ifndef _UTILS_H
    #define _UTILS_H
    
    #define _GNU_SOURCE
    #include <string.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <signal.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    
    #include <errno.h>
    #include <limits.h>
    ...
    #endif
    

    Poole.c :

    #define _GNU_SOURCE
    
    #include <stdio.h>
    #include <math.h>
    #include <sys/poll.h>
    
    #include "utils.h"
    #include "request_queue.h"
    ...
    

    utils.c :

    #include "utils.h"
    ...
    

    request_queue.h :

    #ifndef _REQUEST_QUEUE
    #define _REQUEST_QUEUE
    
    #include <pthread.h>
    #include <stdbool.h>
    
    #include "semaphore_v2.h"
    ...
    #endif
    

    request_queue.c :

    #include "request_queue.h"
    ...
    

    semaphore_v2.h :

    #ifndef _MOD_SEMAPHORE_H_
    #define _MOD_SEMAPHORE_H_
    
    /////////////////////////////////////////////////////////////////////////////
    /**
     *  @file   semaphore.h
     *  @author Jorge Solanas
     *  @date   October 5, 2011
     *  @brief  Library to facilitate the use of semaphores
     *
     *  (c) Copyright La Salle BCN, 2011.
     *  All rights reserved. Copying or other reproduction of this program except
     *  for archival purposes is prohibited without written consent of
     *  La Salle BCN.
     */
    //////////////////////////////////////////////////////////////////////////////
    
    //#define __USE_SVID
    #define __USE_XOPEN
    
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <assert.h>
    #include <stdlib.h>
    
    /**
     * Union which must be explicitly declared by the application.
     * It is the fourth argument of the semctl function, and it is
     * optional, depending upon the operation requested.
     */
    /*union semun
    {
        int val;
        struct semid_ds * buf;
        unsigned short  * array;
    };*/
    
    /**
     * Struct used to indicate to a semaphore which operation is
     * going to be applied (number of the semaphore, operation and
     * flags).
     */
    typedef struct
    {
        unsigned short int sem_num;
        short int sem_op;
        short int sem_flg;
    } sembuf;
    
    /**
     * Struct with all the info about a semaphore. In this case,
     * only the id is specified.
     */
    typedef struct
    {
        int shmid;
    } semaphore;
    
    /**
     * This file provides a simple but useful abstraction for
     * controlling acces by multiple processes to a common resource.
     */
    
    //////////////////////////////////////////////////////////////////////////////
    //                       V2 SEMAPHORE                                      //
    /////////////////////////////////////////////////////////////////////////////
    
    /**
     * Method which creates a semaphore which a specific identifier/name.
     * Very useful for the session S8 ;)
     * @param sem The var where semaphore will be created
     * @param name Semaphore name. If there isn't semaphore with this name, it is created.
                   If it exists, you get the memory region
     * @return int The result of the operation executed
     */
    
    int SEM_constructor_with_name(semaphore * sem, key_t key) {
    
        // IPC_CREAT: if this is specified, and a semaphore with the given key does not exist, it is created, otherwise the call returns with -1, setting the appropriate errno value.
        sem->shmid = semget(key, 1, IPC_CREAT | 0644);
        if (sem->shmid < 0) return sem->shmid;
        return 0;
    }
    
    
    
    
    
    //////////////////////////////////////////////////////////////////////////////
    //                       V1 SEMAPHORE                                      //
    /////////////////////////////////////////////////////////////////////////////
    
    /**
     * Method which creates a semaphore
     * @param sem The var where semaphore will be created
     * @return int The result of the operation executed
     */
    int SEM_constructor (semaphore * sem)
    {
        assert (sem != NULL);
        sem->shmid = semget (IPC_PRIVATE, 1, IPC_CREAT | 0600);
        if (sem->shmid < 0) return sem->shmid;
        return 0;
    }
    
    /**
     * Method which initializes a semaphore
     * @param sem The semaphore to initialize
     * @param v The value to which the semaphores will be
     *          initialized
     * @return int The result of the operation executed
     */
    int SEM_init (const semaphore * sem, const int v)
    {
        unsigned short _v[1] = {v};
        assert (sem != NULL);
        return semctl (sem->shmid, 0, SETALL, _v);
    }
    
    /**
     * Method to destroy a semaphore
     * @param sem The semaphore to destroy
     * @return int The result of the operation executed
     */
    int SEM_destructor (const semaphore * sem)
    {
        assert (sem != NULL);
        return semctl (sem->shmid, 0, IPC_RMID, NULL);
    }
    
    /**
     * Method to apply a wait operation, in order to warn that a
     * process is going to use a common resource. The semaphore
     * counter will be decreased. If counter has the value zero, the
     * process will be added to the semaphore's queue
     * @param sem The semaphore where wait operation will be applied
     * @return int The result of the operation executed
     */
    int SEM_wait (const semaphore * sem)
    {
        struct sembuf o = {0, -1, SEM_UNDO};
    
        assert (sem != NULL);
        return semop(sem->shmid, &o, 1);
    }
    
    /**
     * Method to apply a signal operation, in order to warn that a
     * process has released a common resource. The semaphore counter
     * will be increased. If counter had the value zero and there
     * are processes in the queue, the top one has the access to the
     * common resource granted.
     * @param sem The semaphore where signal operation will be
     *            applied
     * @return int The result of the operation executed
     */
    int SEM_signal (const semaphore * sem)
    {
        struct sembuf o = {0, 1, SEM_UNDO};
        assert (sem != NULL);
        return semop(sem->shmid, &o, 1);
    }
    
    #endif /* _MOD_SEMAPHORE_H_ */
    
    1 回复  |  直到 2 年前
        1
  •  2
  •   Jonathan Leffler    2 年前

    文件 sempaphore_v2.h 包含未标记的函数的定义 static inline (或者至少, static )。这意味着该文件只能包含在任何给定程序的一个源文件中。否则,就会出现多个定义错误,就像您看到的那样。

    你说你不想修改 semaphore_v2.h 因为它是给你使用的(而且它的日期似乎是2011年,所以我很同情)。如第一段所述,这意味着您必须编写自己的代码,以便只有一个源文件(我们称之为 semlib.c )包括 信号量v2.h 。它定义了一系列函数,这些函数使用的代码来自 信号量v2.h 。它有一个关联的标头( semlib.h )它定义了您需要使用的任何类型,并声明了您计划使用的函数,这样您的代码的其余部分就不需要包含 信号量v2.h (以及 semlib。h ,尤其不包括 信号量v2.h )。然后可以编译 semlib。c 并将其与程序的其余部分链接,该程序将被修改为使用由声明的设施 semlib。h 。

    在 comment , PAH 说:

    谢谢,这就是问题所在,尽管我不明白。为什么在这里发生了这种情况,但在我的其他头文件中没有发生,我应该把它们也定义为静态吗?

    据推测,您只在其他标头中声明函数(例如。 request_queue.h )并定义源文件中的函数(例如。 request_queue.c )。这是处理头中声明和源文件中定义的正常方式。文件 信号量v2.h 违反了这个经验法则,而你所看到的问题就是它违反了这个法则的结果。

    如果要在头中包含函数定义,则必须确保不会出现多个定义错误。如我回答的第二段所建议的,该标题仅包含在一个源文件中,或者您必须标记函数 静止的 。 标记它们 静止的 意味着创建的每个包含标头的对象文件都将拥有标头中定义的函数的自己的副本。您需要在包含标头的每个源文件中使用标头中的所有函数,以避免出现有关未使用函数定义的警告。或者您可以标记函数 内联函数 ;这仍然意味着每个对象文件都将包含函数的副本,但仅包含源文件使用的函数。

    这些几乎是你唯一的选择。两个选项都使用 静止的 (有或没有 inline )涉及修改标头。由于您不想这样做,因此必须创建包装器模块( semlib。c , semlib。h )以绕过 信号量v2.h 头球

    在很多方面,最好的解决方案是重写 信号量v2.h 作为两个文件 semaphore_v2.c ,其中包含功能定义,以及经过修订的 信号量v2.h ,它只声明函数而不定义它们。这甚至可能是更好的选择,即使这违背了你不改变外部(有缺陷的)标题的愿望。如图所示,标头肯定是有缺陷的,正是因为它会导致您遇到的问题。