multiple definition von header-Funktion



  • Hi!

    Ich hab gerade mein erstes Projekt mit einer eigenen Header-Datei geschrieben - meiner Meinung nach ist alles richtig... der compiler sieht das anders.

    Ich habe also ein Projekt, in dem ich genau eine header-Datei habe, in der ich mehrere Funktionen zum Rechnen mit Dates nicht nur deklariere, sondern auch definiere.

    Diese Header-Datei binde ich jetzt in mehreren .c-Dateien ein, weil ich in jeder dieser .c-Dateien die Funktionen der header-Datei nutzen will.

    Der Compiler sagt nun aber:

    multiple definition of `_bla'

    wobei bla eine der Funktionen in der header-Datei ist. Das sagt er zu jeder der Funktionen in der header-Datei.

    Ich versteh das nicht, schließlich habe ich die Funktionen in der header-Datei mit

    #ifndef DateFunc_H_INCLUDED
    #define DateFunc_H_INCLUDED  
    .....    
    #endif
    

    eingeschlossen.

    Jemand eine Idee?



  • Ich habe also ein Projekt, in dem ich genau eine header-Datei habe, in der ich mehrere Funktionen zum Rechnen mit Dates nicht nur deklariere, sondern auch definiere.

    Wozu soll das gut sein? Macht nur Probleme. Und das kommt so:

    Der Compiler übersetzt jedes c-File für sich. Der Include Guard funktioniert sicher, aber es besteht wahrscheinlich ohnehin keine Gefahr, dass das h-File in einem c-File mehrmals eingefügt wird (nicht vergessen: #include ist nur Textersatz). Also kann der Compiler alle c-File ohne Probleme übersetzen.

    Dann fängt der Linker mit der Arbeit an und will aus den Objekten eine Exe machen. Das geht aber nicht gut, weil es in jedem Objekt, das das h-File verwendet hat, diese Funktion gibt, und er also verwirrt ist und sich zu Recht beschwert. Und das alles nur, weil du die Funktion nicht in ein c-File geschrieben hast. 🙂



  • fricky schrieb:

    den msvc-linker kann man z.b. zwingen, dass er immer das erste symbol nimmt, wenn mehrere gleichen namens da sind. aber sowas ist meistens pfusch. besser du sorgst dafür, dass solche mehrdeutigleiten nicht auftreten

    Es würde also auch anders gehen. Das wäre aber erstklassiger Pfusch, und alles andere als portabel.
    🙂



  • Hm,

    aber wie mache ich dass denn dann, wenn ich nur eine einzige Datei haben will, in der wichtige Funktionen enthalten sind, die ich in mehreren .c-Files benutzen will? Ich will doch nicht in jedes c-File die Funktion reinschreiben.

    Und wozu sind dann header überhaupt gut? Laut Wikipedia Artikel sind die doch gerade dazu gut, solche Redundanzen zu vermeiden, indem man oft benutzte Funktionen da reinschreibt.

    Versteh die Welt nicht mehr...



  • Und wozu sind dann header überhaupt gut? Laut Wikipedia Artikel sind die doch gerade dazu gut, solche Redundanzen zu vermeiden, indem man oft benutzte Funktionen da reinschreibt.

    Missverständnis (gut möglich, dass das an Wikipedia liegt). Header sind gut, um Redundanzen zu vermeiden, aber damit meint man die Prototypen (Deklarationen), und nicht die Definitionen. Wenn man sauber programmiert, hat man ein paar (möglichst wenige) Funktionen als Schnittstelle zwischen den "Modulen" (oder "Übersetzungseinheiten"). Man kann in C auch Funktionen aus anderen Modulen aufrufen, aber man muss sie vorher deklarieren, damit der Compiler die Einzelheiten kennt. Nachher kümmert sich der Linker darum, dass die Funktionen aus den anderen Modulen auch erreichbar sind.

    Und damit man diese Prototypen nicht in jedes c-File schreiben muss, nimmt man den Umweg über h-Files, um die Redundanz zu vermeiden. Die Definitionen soll aber immer in ein c-File (gute Faustregel, alles andere ist meistens Pfusch).

    Ich will doch nicht in jedes c-File die Funktion reinschreiben.

    Natürlich nicht. Mach einfach ein neues c-File, das diese "gemeinsamen" Funktionen anbietet:

    /* gemeinsam.c */
    
    static int lokale_hilfsfunktion(int i) { return 42 }
    /* lokale_hilfsfunktion() soll in den anderen Modulen nicht sichtbar sein,
       deshalb muss man 'static' dazusagen */
    
    int gemeinsame_funktion(int i) { return 17 }
    /* kein static, das ist die Schnittstelle zur Aussenwelt */
    

    Und dann einen Header:

    /* gemeinsam.h */
    
    extern int gemeinsame_funktion(int);
    /* Das ist der Prototyp. Mit 'extern' sagt man, dass das Ding woanders definiert
       wird, aber hier aufgerufen werden soll. Daher muss man es deklarieren.
       Diesen Header dann überall #includen, wo man die "gemeinsamen" Funktionen
       aufrufen will, und alles wird gut.
       Übrigens kann man das 'extern' auch weglassen, weil das für Prototypen
       implizit gemacht wird. */
    

    Das ist die übliche Vorgehensweise. Es ergibt sich dann, dass man für die meisten c-Files auch h-Files hat. So gesehen kann man die Dinger als Paare betrachten. Alles klar?



  • super erklärt!
    Jetzt hab ichs verstanden. Vielen Dank!

    Kleine Zusatzfrage:

    Wenn ich in einem c-File eine Funktion habe, von der ich weiß, dass ich sie nur in dem File brauchen werde, sollte ich dann trotzdem eine header-Datei mit der Deklaration der Funktion in das c-File einbinden? Sozusagen, damit jemand, der nur schnell wissen will, was für Funktionen in dem C-File sind, sich einfach mal schnell die header-Datei anschauen kann?

    Achso, und woher weiß der linker eigentlich, in welchem c-File er nach dem Funktionskörper suchen muss?



  • Wenn ich in einem c-File eine Funktion habe, von der ich weiß, dass ich sie nur in dem File brauchen werde, sollte ich dann trotzdem eine header-Datei mit der Deklaration der Funktion in das c-File einbinden? Sozusagen, damit jemand, der nur schnell wissen will, was für Funktionen in dem C-File sind, sich einfach mal schnell die header-Datei anschauen kann?

    Da bin ich mir nicht so sicher. Man kann die Meinung vertreten, dass in den Header nur die Schnittstelle zur Aussenwelt gehört, und deshalb dort die lokalen Definitionen nichts verloren haben. Man kann andererseits die Meinung vertreten, dass man xyz.h auch in xyz.c verwenden will, und deshalb sollen alle Prototypen zusammen in dem Header stehen. Da beginnen wohl schon die Stilfragen. Aber es ist nicht so schlimm, weil man ja die lokalen Funktionen immer static macht, und jemand, der nur die Schnittstelle lesen will, wird die Deklarationen der static-Funktionen dann einfach ignorieren.

    Ich selbst hab immer alle Prototypen in den Headern, und teile deshalb die Header in static-Teile und nicht-static-Teile.

    Achso, und woher weiß der linker eigentlich, in welchem c-File er nach dem Funktionskörper suchen muss?

    Ich hab keine Ahnung, bis jetzt hat der das immer geschafft. 🙂
    Sagen wir mal: der macht das automagically. Kleine Feinheit: der Linker arbeitet nicht mehr mit c-Files, sondern mit "object files" (die meinte ich mit "Objekte"). Die haben als Endung .o oder .obj oder sowas. Da stehen noch die Namen drin (oder ID's oder sowas), und der Linker frisst sich da durch.



  • Da stehen noch die Namen drin (oder ID's oder sowas), und der Linker frisst sich da durch.

    Nönö, da müssen schon die Namen drinstehen, sonst könnte der keine ordentlichen Fehlermeldungen von sich geben. In der Exe sind die natürlich nicht mehr.
    🙂


Anmelden zum Antworten