multiple definition of function (c, gcc, make)



  • Erstmal guten morgen meinerseits,
    habe ein Problem beim linken eines Programmes: Die Funktion print_player2 wird mehrmals definiert.

    Wo habe ich hier einen Fehler gemacht? Die Header sind ja durch #ifndef gegen mehrmaliges inkludieren geschützt, also muss der Fehler im Makefile liegen.
    Evtl. ist in beiden Dateien - main.o und player.o - die Funktion definiert? Wie löse ich das Problem?

    Ich benutze MinGW.

    Danke für die Hilfe 🙂

    $ make
    gcc -Wall -pedantic -ansi -o main.o -c main.c
    gcc -Wall -pedantic -ansi -o player.o -c player.c
    gcc -Wall -pedantic -asni -o launch.exe main.o player.o
    player.o:player.c:(.text+0x29): multiple definition of 'print_player2'
    main.o:main.c:(.text+0x29): first definition here
    collect2.exe: error: ld returned 1 exit status
    make: *** [launch.exe] Error 1
    
    /* main.c */
    #include "dependencies.h"
    
    int main(void) {
    	struct player p;
    	p.name = "Max";
    	print_player(&p);
    	print_player2(&p);
    	return EXIT_SUCCESS;
    }
    
    /* dependencies.h */
    #ifndef ENOX_DEPENDENCIES_H
    #define ENOX_DEPENDENCIES_H
    
    #include <stdio.h>
    #include <stdlib.h>
    #include "player.h"
    
    #endif
    
    /* player.c */
    #include "player.h"
    
    void print_player(struct player *p) {
    	printf("name: %s\n", p->name);
    }
    
    /* player.h */
    #ifndef ENOX_PLAYER_H
    #define ENOX_PLAYER_H
    #include <stdio.h>
    
    struct player {
    	const char *name;
    };
    
    void print_player(struct player*);
    
    void print_player2(struct player *p) {
    	printf("name: %s match\n", p->name);
    }
    
    #endif
    

    Das Makefile:

    LINK_TARGET 	= launch.exe
    OBJS 			= main.o player.o
    REBUILDABLES 	= $(OBJS) $(LINK_TARGET)
    
    CC				= gcc
    CFLAGS			= -Wall -pedantic -ansi
    
    .PHONY : all
    all : $(LINK_TARGET)
    
    .PHONY : clean
    clean :
    	-rm $(REBUILDABLES)
    
    $(LINK_TARGET) : $(OBJS)
    	$(CC) $(CFLAGS) -o $@ $^
    
    %.o : %.c
    	$(CC) $(CFLAGS) -o $@ -c $<
    


  • Der Linker beschwert sich, da print_player2 in zwei verschiedenen sog. „translation units“ definiert wurde. Sowohl player.c als auch main.c inkludieren player.h .
    „Include guards“ helfen nur bei mehrmaligen Inkludieren eines Headers in einer TU.
    Entweder du definierst diese Funktion in player.c oder verwendest static . Dadurch hat sie „internal linkage“, d.h. die Funktionsdefinition ist nur in der jeweiligen TU sichtbar.



  • Funktionsdefinitionen in Headerdateien sind ganz ganz selten nötig. Noch seltener.

    EinGast schrieb:

    ... oder verwendest static . Dadurch hat sie „internal linkage“, d.h. die Funktionsdefinition ist nur in der jeweiligen TU sichtbar.

    Dann ist die Funktion allerdings mehrmals im ausführbaren Programm vorhanden.



  • EinGast schrieb:

    Der Linker beschwert sich, da print_player2 in zwei verschiedenen sog. „translation units“ definiert wurde. Sowohl player.c als auch main.c inkludieren player.h .
    „Include guards“ helfen nur bei mehrmaligen Inkludieren eines Headers in einer TU.
    Entweder du definierst diese Funktion in player.c oder verwendest static . Dadurch hat sie „internal linkage“, d.h. die Funktionsdefinition ist nur in der jeweiligen TU sichtbar.

    Danke für die schnelle Antwort! Frage mich warum ich die Funktion im Header hatte o.O



  • EinGast schrieb:

    Entweder du definierst diese Funktion in player.c oder verwendest static . Dadurch hat sie „internal linkage“, d.h. die Funktionsdefinition ist nur in der jeweiligen TU sichtbar.

    Wie DirkB schon beschrieben hat taucht die Funktion dann aber mehrfach im Speicher auf, was man nicht wirklich haben moechte.
    Du hast aber eine Variante vergessen. Anstelle von static kann man auch inline schreiben. Dann wird der Code der Funktion vom Compiler an die aufrufende Stelle kopert (also der Aufruf wird mit dem Funktionscode ersetzt). Das ist besonders dann Wertvoll, wenn man mit viel Abstraktion arbeitet, am Ende nur eine kleine Funktion hat (1-5 Zeilen) und sich nicht einen teuren Sprung leisten moechte.



  • inline habe ich nicht (oder absichtlich) aufgrund der CFLAGS vergessen. inline ist ein wenig komplexer als du das so erklärt hast.
    Dass static die nicht empfehlenswerte Variante ist, hätte ich allerdings extra erwähnen sollen, da das nicht wirklich offensichtlich ist. Nun ja, das habt ihr ja nun erledigt.



  • EinGast schrieb:

    inline habe ich nicht (oder absichtlich) aufgrund der CFLAGS vergessen. inline ist ein wenig komplexer als du das so erklärt hast.

    Koennst du bitte einmal auf die Details eingehen? Ich verwende regelmaessig inline und habe bisher leider noch nichts von diesem Problem gehoert und/oder gelesen. Auf die Schnelle konnte ich leider auch keinen Hinweis auf wirkliche Probleme finden. Dazu habe ich sowohl bei [1] und [2] den Verweis darauf gefunden, dass man sie in gewissen Situationen nutzen soll (anstelle von Makros). Generell nutze ich sie auch in diesen Rahmen regelmaessig.

    Fuer eine genauere Erklaerung waere ich dir dankbar.

    [1] http://www.cplusplus.com/forum/articles/20600/
    [2] http://embeddedgurus.com/barr-code/2011/03/do-inline-function-bodies-belong-in-c-header-files/



  • inline gibt es nicht in ISO C90 (und das ist anscheinend für enox sehr wichtig).

    inline hat unterschiedliche Semantiken in ISO C99, dem GNU-Dialekt von ISO C90 und C++: http://stackoverflow.com/a/216546 oder http://www.greenend.org.uk/rjk/tech/inline.html. Dein Vorschlag inline statt static zu schreiben ist also nicht portabel und führt zu einem Linker-Fehler in Standard-C.

    inline ist außerdem nur ein Hinweis. Einige weniger populäre Compiler (u.a TCC) ignorieren diesen oder können diesen sogar immer befolgen, auch wenn es negative Auswirkungen auf die Größe der Funktion/des Kompilats hat. Compiler-Optionen können auch Einfluss darauf ausüben, z.B. würde in diesem Fall GCC den Hinweis nicht beachten.

    (Am Rande: Einem gut optimierenden Compiler ist es eh egal, ob da jetzt static oder static inline steht. So wie ein gut optimierender Linker auch weiß, wie er mit unbenutzten oder doppelten Funktionen umzugehen hat.)


Anmelden zum Antworten