Frage zu extern "C"



  • Hallo, was habe ich von dem Befehl extern "C" ist der nur dazu da um zu Kennzeichnen das es sich um eine C Funktion handelt?

    Ich habe hier ein einfach Beispiel geschrieben:

    Das printf hätte ich ja auch in my_cpp_function() Funktion schreiben können.

    #include <iostream>
    using namespace std;
    
    extern "C" void my_c_function();
    void my_cpp_function();
    
    int main(int argc, char* argv[])
    {
    
    my_cpp_function();
    
    system("PAUSE");
    }
    //---------------------------------------------------------------------------
    
    void my_cpp_function()
    {
            my_c_function();
    }
    
    extern "C"  void my_c_function()
    {
            printf("hello\n");
    }
    


  • Das sorgt dafür, das der Funktionsname nicht gemanglet wird.


  • Mod

    Das extern "C" bedeutet ganz was anderes. Das hätte dir Google in Sekunden verraten, jetzt musst du hier stattdessen eine Minute warten. 🙄

    Es geht dabei darum, Module, die in verschiedenen Sprachen geschrieben (und übersetzt!) wurden, gemeinsam zu benutzen.



  • Normalerweise taucht das so auf (Es kann übrigens auch mit geschweiften Klammern verwendet werden um Blöcke zusammenzufassen):

    #ifdef cplusplus
        extern "C"
        {
    #endif
    /// C-Code
    #ifdef cplusplus
        }
    #endif
    


  • Es gibt da ein Konzept, das sich im Englischen "language linkage" schimpft. Wenn Du einem C++ Compiler eine Funktionsdeklaration zeigst, dann hat sie für ihn standardmäßig eine C++ Bindung. Die C++ Bindung erlaubt u.a. Funktionstemplates, Überladungen und die Zuordnung zu Namensräumen. Sowas gibt's ja in C nicht. Die "language linkage" könnte aber auch so Dinge beeinflussen wie die Parameterübergabe auf Maschinenebene stattfindet.

    Um jetzt Kompilate mehrerer Sprachen kombinieren zu können, muss man sich bei gemeinsam benutzten Funktionen auf eine bestimmte Bindung einigen. Tut man das nicht, wird höchstwahrscheinlich der Linker irgendwelche Symbole nicht finden und im schlimmsten Fall erhältst Du ein fertiges Programm, was einfach nicht funktioniert.

    Möchtest Du eine Funktion, die ein C-Compiler kompiliert hat und in einer Objektdatei vorliegt, von C++ aus nutzen, muss der C++ Compiler die Deklaration der Funktion mit der richtigen Bindung sehen:

    dings.c

    int foo(void) {
      return 3;
    }
    

    bums.cpp

    extern "C" {
    int foo(void);
    }
    
    int main() {
      return foo();
    }
    

    Damit Du Header einer kompilierten C-Bibliothek auch direkt in C++ verwenden kannst, sehen die Header in der Regel so aus:

    #ifndef IRGENDEIE_C_BIBLIOTHEK_HEADER
    #define IRGENDEIE_C_BIBLIOTHEK_HEADER
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    deklarationen
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    

    Ein C Compiler wird das extern "C" hier ignorieren. Er kennt aber auch nichts anderes als eine C-Bindung -> ok

    Ein C++ Compiler wird das extern "C" hier erkennen und dementsprechend den eingeschlossenen Deklalationen eine C-Bindung zuordnen -> ok

    Klar soweit?



  • krümelkacker schrieb:

    bums.cpp

    extern "C" {
    int foo(void);
    }
    

    In dem Fall können die geschweiften Klammern auch weggelassen werden, dann bezieht sich das extern "C" nur auf genau die Funktion die dort deklariert wird.

    Man kann das alles etwas allgemeiner formulieren:

    1. Funktionsdeklarationen, die sowohl im C++-Code als auch im C-Code bekannt sein müssen, müssen im C++-Code als extern "C" deklariert werden (Im C-Code ohne)
    2. Die Definition muss in C-Code nicht weiter markiert werden, in C++-Code muss sie auch wieder als extern "C" markiert werden.

    in vielen Projekten mit gemischter C/C++-Übersetzung findet man für die Funktionen, die in Sourcen beider Sprachen bekannt sein müssen, Makros, etwa wie folgt:

    //cdecl.h
    #ifdef __cplusplus
    
    #define BEGIN_EXTERN_C extern "C" {
    #define END_EXTERN_C }
    #define C_DECL extern "C"
    
    #else
    
    #define BEGIN_EXTERN_C
    #define END_EXTERN_C 
    #define C_DECL
    
    #endif
    

    Die Dinger werden dann sowohl für Header (funktionsdeklarationen) als auch für die Sourcen (definitionen) verwendet. Letzteres sorgt dafür, dass man nicht groß was ändern muss, wenn man eine Source, die mal mit einem C-Compiler übersetzt wurde, als C++ übersetzt.



  • Warum nimmt man #endif mit in die geschweifte Klammer?
    Und dann nochmal #ifdef cplusplus }

    #ifdef cplusplus 
         extern "C"
         {
     #endif
    /// C-Code
     #ifdef cplusplus
         }
     #endif
    

    Und kann man das so schreiben bzw. sollte man es so machen?

    #include <iostream>
    using namespace std;
    
    #define cplusplus // Um extern und die void cpp_1() Funktion benutzen zu können
                      // sowie extern "C"
    #ifdef cplusplus
         #include <conio>
         extern "C"  {
    #endif
    
     // C Funktionen
    void foo(void);
    void c_ClearScreen();
    
    #ifdef cplusplus
    
     // C++ Funktionen
     void cpp_1();
     }
    #endif
    
    int main(int argc, char* argv[])
    {
    
    foo();
    cpp_1();
    
    cout<<"Taste druecken zum Bildschirm loeschen: ";
    getch();
    
    c_ClearScreen();
    
    system("PAUSE");
            return 0;
    }
    //---------------------------------------------------------------------------
    
    void cpp_1()
    {
            cout<<"cpp ok"<<endl;
    }
    
    void foo(void)
    {
            printf("ok\n");
    }
    
    void c_ClearScreen()
    {
            clrscr();
    }
    

    Weil das #include <conio> könnte ich ja auch genau so gut unter das #include <iostream> schreiben und den ganzen #define und #ifdef kram weglassen.
    Und einfach oben die Funktionen definieren.

    Oder wäre hier auch das extern "C" sinnvoll um zu verdeutlichen das C Funktionen verwendet werden?



  • externC schrieb:

    Warum nimmt man #endif mit in die geschweifte Klammer?

    Tut man nicht. #ifdef & Co werden vom Präprozessor ausgewertet, das geschieht schon bevor der Compiler sich dann überhaupt mit den geschweiften Klammern beschäftigt. Soll heißen: #endif ist nicht innerhalb des (nur für den Compiler interessanten) Blocks aus geschweiften Klammern, sondern die öffnende und schließende geschweifte Klammer sind jeweils innerhalb eines (bereits vom Präprozessor aufgelösten) #ifdef/#endif Blocks

    Und kann man das so schreiben bzw. sollte man es so machen?

    Nein. Sollte man nicht. #define cplusplus bzw. #define __cplusplus werden vom Compiler gemacht, das solltest du selber garnicht machen.

    Außerdem solltest du Funktionen, die sowohl in C als auch in C++ verwendet werden, nicht in einen Header schreiben, der auch reine C++-Funktionen enthält, sondern den Header entsprechend aufteilen. Genauso darfst du in einen Header, den der C-Compiler zu sehen bekommt, keine C++-Header wie <iostream> etc. einbinden.



  • pumuckl schrieb:

    Und kann man das so schreiben bzw. sollte man es so machen?

    Nein. Sollte man nicht. #define cplusplus bzw. #define __cplusplus werden vom Compiler gemacht, das solltest du selber garnicht machen.

    wtf?
    Was heißt denn hier "machen"?
    Das vordefinierte Macro heißt __cplusplus. Ohne Doppel-Unterstrich taucht das nicht im C++ Standard auf.



  • Ich hab das ganze jetzt mal von C nach C++ ausprobiert. Meine Funktionen werden ohne Probleme ausgeführt.

    Aber ich bekomme immer diese Warnungen:

    [C++ Warnung] Unit1.c(7): W8065 Aufruf der Funktion 'function' ohne Prototyp
    [C++ Warnung] Unit1.c(9): W8065 Aufruf der Funktion 'function_2' ohne Prototyp
    [C++ Warnung] Unit1.c(11): W8065 Aufruf der Funktion 'system' ohne Prototyp
    

    Wenn ich in my_functions.cpp nicht schreibe:

    void function();
    void function_2();

    Wieso muss ich in my_functions.cpp auch nochmal die Prototypen definieren?
    Das habe ich doch eigentlich schon in my_header.h gemacht.

    main.c

    #include <stdio.h>
    #include "my_header.h"
    
    int main(int argc, char* argv[])
    {
    
    function();
    printf("\n");
    function_2();
    
    system("PAUSE");
    }
    

    my_header.h

    #ifndef my_header
    #define my_header
    
    #ifdef __cplusplus
    
    extern "C" // Ermöglicht die Verwendung von C++ Funktionen in
               // C Programmen
    {
            void function(); 
            void function_2();
    }
    #endif
    #endif
    

    my_functions.cpp

    #include "my_header.h"
    #include <iostream>
    using namespace std;
    
    void function();
    void function_2();
    
    void function()
    {
     cout<<"ok";
    }
    
    void function_2()
    {
     cout<<"funktion 2";
    }
    


  • -.- Wenn ich mein Projekt abspeichere kommt wieder diese Prototyp Warnung, da hab ich mich wohl zu früh gefreut ... 😡

    Obwohl ich in my_function.cpp die Funktionen definiert habe.



  • #ifndef my_header
    #define my_header
    
    #ifdef __cplusplus
    
    extern "C" // Ermöglicht die Verwendung von C++ Funktionen in
               // C Programmen
    {
            void function();
            void function_2();
    }
    #endif
    #endif
    

    Denk mal darüber nach, was hier beim Compiler ankommt, wenn __cplusplus nicht definiert ist. Richtig: garnichts.

    Und dann vergleiche nochmal mit den vorangegangenen Beiträgen, insbesondere verfolge die Diskussion "Warum nimmt man #endif mit in die geschweifte Klammer?" 😉



  • externC schrieb:

    my_header.h

    #ifndef my_header
    #define my_header
    
    #ifdef __cplusplus
    
    extern "C" // Ermöglicht die Verwendung von C++ Funktionen in
               // C Programmen
    {
            void function(); 
            void function_2();
    }
    #endif
    #endif
    

    Das ist ja auch falsch so. Der C Compiler sieht davon GAR nix, weil der Präprocessor alles rausschmeißt; denn __cplusplus ist ja beim C Compiler nicht definiert. Dementsprechend fehlen Dir die Funktionsdeklarationen ("Prototypen" in C-Sprech) und der C-Compiler warnt Dich, dass Du Funktionen später aufrufst, die er noch gar nicht kennt.

    Richtig:

    ...
    
    #ifdef __cplusplus
    extern "C" {
    [b]#endif[/b]
    
    ...
    
    [b]#ifdef __cplusplus[/b]
    }
    #endif
    
    ...
    


  • Wieder die Prototyp Fehlermeldung. 😞

    Ich versteh nicht was da jetzt schon wieder falsch sein soll. (Naja falsch vielleicht nicht denn es Kompiliert ja und läuft. (Ein Lehrer sagte mal "Das sind nur Warnungen das ist nicht so schlimm", aber ob das nicht so schlimm ist da zweifel ich irgendwie dran, den die Warnung wird ja nicht zum Spass ausgegeben.)

    main.c

    #include <stdio.h>
    #include "my_header.h"
    
    int main()
    {
    
    function();
    function_2();
    
    system("PAUSE");
    
    return 0;
    
    }
    

    my_header.h

    #ifndef my_header
    #define my_header
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void function();
    void function_2();
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    my_functions.cpp

    #include "my_header.h"
    #include <iostream>
    using namespace std;
    
    void function()
    {
     cout<<"ok";
    }
    
    void function_2()
    {
     cout<<"function_2";
    }
    


  • pumuckl schrieb:

    1. Die Definition muss in C-Code nicht weiter markiert werden, in C++-Code muss sie auch wieder als extern "C" markiert werden.

    Edit: Wobei ... bei mir läuft das Programm fehlerfrei und ohne Warnungen beim Erstellen:

    cl -c main.c
    cl -c /EHsc my_functions.cpp
    link main.obj my_functions.obj

    erstellt main.exe ohne Warnungen und Fehler ...



  • externC schrieb:

    Wieder die Prototyp Fehlermeldung. 😞

    Ich versteh nicht was da jetzt schon wieder falsch sein soll. (Naja falsch vielleicht nicht denn es Kompiliert ja und läuft. (Ein Lehrer sagte mal "Das sind nur Warnungen das ist nicht so schlimm", aber ob das nicht so schlimm ist da zweifel ich irgendwie dran, den die Warnung wird ja nicht zum Spass ausgegeben.)

    Das ist richtig. Zum Spaß sind die nicht da. Diese Warnung richtig interpretiert heißt: "Ich kann nicht überprüfen, ob es die Funktion gibt und/oder ob Du sie mit den richtigen Parametern aufrufst". Du bist also auf Dich allein gestellt und wenn Du etwas falsch machst, dann gibt Dir das Programm Grütze aus oder schmiert ab.

    externC schrieb:

    my_header.h

    #ifndef my_header
    #define my_header
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    void function();
    void function_2();
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    Du solltest für eigene Macros immer Namen verwenden, die mit einem Großbuchstaben anfangen und keine kleinen Buchstaben enthalten. Auch übermäßiger Gebrauch von Unterstrichen (am Anfang oder irgendwo zweimal hintereinander) sollte man für eigene Namen nicht verwenden, da die reserviert sind.

    Der Haken hier ist, dass eine leere Parameter-Klammer in C90 etwas anderes bedeutet als in C++:

    In C90 wird hier dem Compiler lediglich gesagt, dass es da zwei Funktionen gibt, mit den Namen function und function_2. Welche Parameter die bekommen ist aber offen. Möchtest Du dem Compiler mitteilen, dass die Funktionen keine Parameter bekommen, musst Du ein void in die Klammern schreiben:

    void function(void);
    void function_2(void);
    

    In C++ ist das void überflüssig. Man hielt es beim Design von C++ für eine ziemlich dämliche Idee, die Angabe über Parameter offen zu lassen. Dementsprechend verhält sich eine leere Klammer in C++ genauso wie eine in C mit void drin. In C++ ist das void aber auch nicht falsch oder schädlich. Aus C-Kompatibilitätsgründen ist es in C++ immer noch erlaubt. In reinem C++ Code solltest du void an dieser Stelle nicht verwenden, da das einfach unüblich ist.

    Versuch's also mal mit void in der Klammer.



  • Danke an alle für die Hilfe, jetzt funktioniert es, hier nochmal alle Programme vielleicht kann wer anderes damit auch noch was Anfangen. Ich hab bei wikipedia auch noch 2 interessante Links gefunden:

    http://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B
    http://en.wikipedia.org/wiki/Name_mangling

    Die C++ Version:

    main.cpp

    #include <iostream>
    #include "my_header.h"
    
    using namespace std;
    
    int main(int argc, char* argv[])
    {
    
     check_if_file_exists ();
    
    system("PAUSE");
            return 0;
    }
    

    my_header.h

    #ifndef my_header 
     #define my_header 
    
     /*/
     __cplusplus wird automatisch vom Compiler definiert, wenn ein C++ Compiler verwendet wird
     (Ist im C++ Standart so festgelegt)
     /*/
    
     #ifdef __cplusplus // Wenn kein C++ Compiler verwendet wird, dann wird void check_if_file_exists(); ganz normal aufgerufen
    
     extern "C" { // Wenn ein C++ Compiler verwendet wird dann wird die Funktion check_if_file_exists();
                // mit den C Symbolnamen Compiliert  http://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B
    			// Weiteres Stichwort: "name mangling"  http://en.wikipedia.org/wiki/Name_mangling
     #endif 
    
     void check_if_file_exists();
    
     #ifdef __cplusplus // Falls ein C++ Compiler verwendet wurde dann wird hier die Klammer vom exter "C" Befehl geschlossen.
     } 
     #endif 
     #endif
    

    my_functions.c

    #include "my_header.h"
    #include <iostream>
    #include <fstream>
    
    void check_if_file_exists()
    {
    
            std::ifstream file ("test.txt");
    
            if ( file == NULL )
            {
                    std::cout<<"Die Datei test.txt existiert nicht\n"<<std::endl;
            }
    
            else
            {
                    std::cout<<"test.txt existiert"<<std::endl;
            }
    
    }
    

    Und jetzt die C Version

    main.c

    #include <stdio.h>
    #include "my_header.h"
    
    int main()
    {
    
    check_if_file_exists();
    
    system("PAUSE");
    return 0;
    
    }
    

    my_header.h

    #ifndef my_header 
    #define my_header
    
    #ifdef __cplusplus // Wenn kein C++ Compiler verwendet wird, dann wird void check_if_file_exists(); ganz normal aufgerufen
    
    extern "C" {   // Wenn ein C++ Compiler verwendet wird dann wird die Funktion check_if_file_exists();
                // mit den C Symbolnamen Compiliert  http://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B
                // Weiteres Stichwort: "name mangling"  http://en.wikipedia.org/wiki/Name_mangling
    #endif
    
     void check_if_file_exists(void); // Wenn aus C eine C++ Funktion aufgerufen werden soll,
                                      // und die C++ Funktion keine Parameter erwartet dann muss
                                      // der Übergabeparameter void sein, das void muss man nur hier
                                      // in my_header.h schreiben
    
     #ifdef __cplusplus // Falls ein C++ Compiler verwendet wurde dann wird hier die Klammer vom exter "C" Befehl geschlossen.
     } 
     #endif 
     #endif
    

    my_functions.cpp

    #include "my_header.h"
    #include <iostream>
    #include <fstream>
    
    void check_if_file_exists()
    {
    
            std::ifstream file ("test.txt");
    
            if ( file == NULL )
            {
                    std::cout<<"Die Datei test.txt existiert nicht\n"<<std::endl;
            }
    
            else
            {
                    std::cout<<"test.txt existiert"<<std::endl;
            }
    
    }
    


  • #include <stdio.h>
    #include "my_header.h"
    
    int main()
    {
    
    check_if_file_exists();
    
    system("PAUSE");
    return 0;
    
    }
    

    Schöner:

    #include <cstdio>//Benutze die C++-Version dieses Headers
    #include "my_header.h"
    
    int main()
    {
        check_if_file_exists();
        for(;;);//return-Statement eig. unnötig, es wird Standardmäßig 0 zurückgegeben (Achtung, nur bei main()!)
    }//system("pause"); ist unportabler Blödsinn. Nimm lieber eine Endlosschleife
    

    Edit: Und brav auf Formattierung achten!



  • Wieso gibt es denn jetzt 2mal eine my_header.h-Datei (Einmal im Abschnitt "C++-Version" und einmal im Abschnitt "C-Version")? Kann das nicht jedes mal dieselbe sein? Die Headerdatei definiert eine C-Schnittstelle. Auf welcher Seite welche Sprache verwendet wird, ist dann ja eigentlich egal, sofern man dort den __cplusplus-Trick eingebaut hat.

    Dein IncludeGuard besteht wieder aus Kleinbuchstaben.



  • Hacker schrieb:

    //system("pause"); ist unportabler Blödsinn. Nimm lieber eine Endlosschleife
    

    Super Alternative 🙄


Anmelden zum Antworten