gcc ANSI-C Dateiendung .cpp



  • Hallo Community,

    Für meinen Informatikkurs an der Uni habe ich das folgende C-Programm zum Travelling Salesman Problem mit der Nearest Neigbor Methode programmiert.
    In der Uni benutzen wir Visual Studio und dessen C-Compiler. Dafür müssen die Programmdateien als .cpp gespeichert werden.
    Ich arbeite zu Hause mit Ubuntu 14.04 und gcc. Wenn ich meinen Code mit

    gcc code.c -lm
    

    kompiliere funktioniert alles wie gewünscht.
    Kompiliere ich jedoch

    gcc code.cpp -lm
    

    erhalte ich die folgende Fehlermeldung:

    code.cpp: In function ‘void anhaengen(double, double, char*, double)’:
    code.cpp:84:47: error: invalid conversion from ‘void*’ to ‘bezirk*’ [-fpermissive]
         if ((anfang = malloc(sizeof(struct bezirk))) == NULL) {
                                                   ^
    code.cpp:111:52: error: invalid conversion from ‘void*’ to ‘bezirk*’ [-fpermissive]
         if((zeiger->next = malloc(sizeof(struct bezirk))) == NULL){
    

    Ich darf zur Lösung der Aufgabe nur ANSI-C benutzen.
    Meine Frage: Ist der folgende Code ANSI-C oder nicht?

    /* DIE DATEI postcodes.txt BITTE ALS PARAMETER BEIM START DES PROGRAMMES ÜBERGEBEN */
    
    #include<math.h>
    #include<stdlib.h>
    #include<time.h>
    #include<string.h>
    #include<stdio.h>
    
    #define MAX 20
    
    // Deklariere eine Struktur 'bezirk', die die notwendigen Attribute beinhaltet
    struct bezirk{
      double y;
      double x;
      char name[MAX];
      double current_distance;
      // mit diesem Pointer ist es möglich Strukturen aneinander zu hängen
      struct bezirk *next;
    };
    
    /* 
       Initialisiere 3 Pointer:   
       *anfang: Zeigt auf den Anfang der Strukturenkette.
       *ende:   Zeigt auf das Ende der Kette
       *start:  Zeigt auf den Bezirk innerhalb der Kette, an dem sich der Travelling Salesman gerade befindet. 
    */ 
    struct bezirk *anfang = NULL;
    struct bezirk *ende = NULL;
    struct bezirk *start = NULL;
    
    // In diesem Array werden zwanzig zufällig ausgewählte Bezirke, sowie der Startbezirk gespeichert.
    struct bezirk route[21];
    
    /* Funktion für den Absolutbetrag einer Zahl */
    //=============================================
    double betrag (double x){
    
      if (x < 0) return -x;
      else return x;
    }
    
    /* Funktion, die die Distanz zwischen zwei Koordinatenpaaren zurückgibt */
    //========================================================================
    double distanz (double ax, double bx, double ay, double by){
    
      double const pi = 3.1415926535897;
      double delta_x, delta_y;
    
      // Abstand Breitengrade: 1° entspricht 113,3 km
      delta_y = betrag(ay-by)*113.3;
    
      // Längengrade: 1° entspricht 113,3 km * cos(Breitengrad)
      /* Nehme für Breitengrad zweier Punkte den Mittelwert: 
         (ay+by)/2 um den Abstand zwischen zwei Längengraden zu berechnen */
      delta_x = betrag(ax-bx)*113.3*cos(((ay+by)/2)*(pi/180));
    
      // Pythagoras:
      return sqrt(pow(delta_x,2)+pow(delta_y,2));
    }
    
    /* Funktion, die eine weitere Struktur vom Typ bezirk an die anderen anhängt */
    //=============================================================================
    void anhaengen(double x, double y, char *name, double current_distance) {
    
      // Dieser Pointer zeigt immer auf eine Struktur bezirk. Über ihn erfolgt der Zugriff auf die Werte.
      struct bezirk *zeiger;
    
      // Falls der Pointer anfang NULL ist, so befindet sich noch kein Element in der Liste.
      if (anfang == NULL) {
    
        // Räume Speicherplatz für die Größe eines neuen bezirks frei. Ggf Fehlermeldung.
        if ((anfang = malloc(sizeof(struct bezirk))) == NULL) {
          fprintf(stderr, "Kein Speicherplatz vorhanden");
          return;
        }
    
        // Setzte nun die Werte für das erste Element.
        anfang->y = y;
        anfang->x = x;
        strcpy(anfang->name, name);
    
        // Setze den next pointer von anfang auf NULL, damit dieser das neue Ende angibt.
        anfang->next = NULL;	
      }
    
      // Ansonsten (falls schon Elemente in der Liste vorhanden sind)
      else {
    
        // Setze den Zeiger auf den Anfang der Liste
        zeiger=anfang;
    
        // Solange der nächste nicht NULL ist, setze den Zeiger auf das nächste Element
        // um so ans Ende der Liste zu gelangen.
        while(zeiger->next != NULL) {
          zeiger = zeiger->next;
        }
    
        //Räume speicherplatz frei und zeige ggf. Fehlermeldung
        if((zeiger->next = malloc(sizeof(struct bezirk))) == NULL){
          fprintf(stderr, "Kein Speicher vorhanden");
          return;
        }
    
        //Setze jetzt den Zeiger auf das gerade freigegebene Element
        zeiger = zeiger->next;
    
        // Setze Werte
        zeiger->x = x;
        zeiger->y = y;
        strcpy(zeiger->name, name);
        zeiger->current_distance = current_distance;
    
        // Setze das nächste Element auf NULL um wieder das neue ENDE zu kennzeichnen.
        zeiger->next = NULL;
      }
    
      // Setze den Start-Zeiger auch auf den Anfang, damit dieser zum Start der Berechnung bereits
      // zugeordnet ist.
      start=anfang;
    }
    
    /* Funktion, die ein Element aus der Kette der bezirk Strukturen löschen kann */
    //==============================================================================
    void loeschen(char *wen){
    
      // Initialisiere zuerst zwei Pointer
      struct bezirk *zeiger, *zeiger1;
    
      // Falls Anfang nicht leer ist
      if (anfang!=NULL){
    
        // Prüfe ob Anfang das gesuchte Element ist        
        if(strcmp(anfang->name, wen)==0){
    
          // Dann setze den Zeiger eins weiter nach vorne
          zeiger = anfang->next;
    
          // Gebe den Speicherplatz auf den Anfang zeigt frei
          free(anfang);
    
          // Setze zeiger als neuen Anfang
          anfang = zeiger;
        }
    
        //Ansonsten durchlaufe alle Elemente bis Ende, solange bis das gesuchte Element gefunden ist
        else{
    
          // Setze Zeiger auf Anfang
          zeiger = anfang;
    
          // Laufe solange, bis das nächste Element NULL ist, also das Ende der Liste erreicht ist
          while(zeiger->next!=NULL){
    
    	// Sezte den zweiten zeiger (zeiger1) auf das nächste Element
    	zeiger1 = zeiger->next;
    
    	// Falls zeiger1 auf das gesuchte Element zeigt
    	if(strcmp(zeiger1->name, wen)==0){
    
    	  // Setze den next-Zeiger des zeigers auf das übernächste Element
    	  // Damit wird das zu löschende Element enfach aus der Zeigerkette herausgenommen
    	  zeiger->next = zeiger1->next;
    
    	  // Lösche das übersprungene Element
    	  free(zeiger1);
    
    	  // Ende des Vorganges.
    	  break;
    	}
    
    	// Gehe zum nächsten Element
    	zeiger = zeiger->next;
          } 
        } 
      }
    
      // Ansonsten (wenn der Anfang NULL ist)
      else{
        printf("Kein Element zum Loeschen vorhanden!");
      }
    
      // Setze den Start Pointer auf Anfang.
      start=anfang;
    }
    
    /* Funktion, die alle Elemente, die sich im moment in der Strukturkette befinden ausgibt. */
    //==========================================================================================
    void ausgeben(void){
    
      // Initialisiere Zeiger und setze ihn auf Anfang.
      struct bezirk *zeiger = anfang;
      printf("\nListe zufälliger Stationen: (21 Stk, da der erste der Startbezirk ist)\n\n");
    
      // Solange der Zeiger sich nicht am Ende der Liste befindet
      while(zeiger!=NULL){
    
        // Gebe die Werte des Elements aus, auf das der Zeiger zeigt.
        printf("Bezirk: %s, \tx-Koordinate: %.3lf, \ty-Koordinate: %.3lf \n", 
    	   zeiger->name, zeiger->x, zeiger->y);
    
        // Gehe nun zum nächsten Element
        zeiger = zeiger->next;    
      }
    }
    
    /* Funktion, die für alle Elemente der Kette die Distanzen zum aktuellen Startpunkt berechnet. */
    //===============================================================================================
    void berechne_distanzen(void){
    
      /* Setze den Zeiger wieder auf Anfang */
      struct bezirk *zeiger = anfang;
    
      /* Solange er das Ende der Kette nicht erreicht hat */
      while(zeiger!=0){
    
        // Falls der Zeiger auf den Start zeigt, setzte current_distance auf 0.
        if(zeiger == start){
          zeiger->current_distance = 0;
    
          // gehe zum nächsten Element und breche diesen Durchlauf der Schleife ab.
          zeiger = zeiger->next;
          continue;
        }
    
        // Ansnsten berechne seine Distanz zu Start
        else{
          zeiger->current_distance = distanz(zeiger->x, start->x, zeiger->y, start->y);
        }
    
        // Gehe dann weiter zum nächsten Element
        zeiger = zeiger->next;    
      }
    }    
    
    /* Funktion, die den Start-Zeiger in Abhängigkeit der kleinsten Distanz neu setzt */
    //----------------------------------------------------------------------------------------------
    void neuer_start(void){
    
      // Setze den Start-Zeiger auf Anfang und den Zeiger auf das zweite Element
      struct bezirk *zeiger = anfang->next;
      start = anfang;
    
      // Überprüfe solange bis das nächste Element das Ende der Kette ist
      while(zeiger->next!=0){
    
        // ob die aktuelle Distanz des Zeigers kleiner ist als der momentane Start
        if(zeiger->current_distance < start->current_distance){
    
          // Wenn ja, setzte den Zeiger als neues Startelement
          start=zeiger;
        }
    
        // Ansonsten gehe zum nächsten Element
        zeiger = zeiger->next;
      }
    }
    
    /* Funktion, welche prüft ob ein Element schon im Array enthalten ist. Wird benötigt um zu vermeiden, 
       dass eine Zufallszahl zweimal gezogen wird.*/
    //===================================================================================================
    int doppelt(int z, int zufall[20]){
      int i;
      for(i=0; i<20; ++i){
    
        // Falls die Zahl schon im Array vorhanden ist, gebe true zurück.
        if(zufall[i] == z) return 1;
      }
    
      // Falls die Funktion nicht mit return true geendet hat, so ist z nicht im Array enthalten > return false
        return 0;
    }
    
    /* main*/
    //===============================================================================================
    int main(int argc, char **argv){
    
      // Statisches Array von Strukturen des Typs bezirk, welches alle Elemente der Datei aufnimmt
      struct bezirk alle[1726];
    
      //Bestimme den Pointer zum FILE-Objekt und übergebe Parameter
      FILE *dateizeiger;
      dateizeiger = fopen(argv[1], "r");
    
      //Fehlermeldung falls Datei nicht geöffnet werden kann.
      if (dateizeiger==0){
        printf("Fehler beim Öffnen der Datei. \npostcodes.txt bitte als Parameter beim Programmaufruf in der Konsole angeben!\n");
        return 1;
      }
    
      printf("\n********************************************************");
      printf("\n* Travelling Saleman Problem: Nearest Neighbor Methode *");
      printf("\n********************************************************");
      printf("\n\nBitte Startbezirk eingeben (Zeilennummer in der angegebenen Datei!): \n");
      printf("Vorgabe ist 724 (M1_2EA)\n> ");
    
      // Benutzereingabe der Zeile des Startbezirks
      int zeile;
      scanf("%i", &zeile);
    
      // Bei falscher Eingabe beende.
      if ((zeile<=0) || (zeile > 1726)){
        printf("\n Fehler: Zeile außerhalb des gültigen Bereichs (1-1726). \nBeenden.");
        return 1;
      }
    
      // Lese alle Zeilen ein und füge sie dem Array hinzu
      int q = 0;
      for(q=0; q<1726; ++q){
        fscanf(dateizeiger, "%s %lf %lf", alle[q].name, &alle[q].y, &alle[q].x);
        alle[q].current_distance = 0;
      }
    
      // Schließe die Datei wieder
      fclose(dateizeiger);
    
      // Hänge den Startbezirk als erstes Element an die dyn. Struktur an
      int startbezirk = zeile -1;
      anhaengen(alle[startbezirk].x, alle[startbezirk].y, alle[startbezirk].name, 0);
    
      // Initialisiere den Zufallsgenerator mit Hilfe der Systemzeit neu, damit dieser nicht immer die selben Werte liefert.
      time_t t;
      time(&t);
      srand((unsigned int)t);
    
      // Erstelle nun ein Array zufall mit 20 Zahlen, die zufällig aus 1726 gezogen werden.
      int zufall[20];
    
      /* Setzte dazu alle Werte von zufall auf 0. (Damit bei der ersten Prüfung auf doppelte Werte nicht zufällig 
         ein Pointer auf einen richtigen aber unbrauchbaren Wert im Speicher zeigt. */
      int r;
      for (r=0; r<20; ++r){
        zufall[r] = 0;
      }
    
      // Fülle das Array dann mit Zufallszahlen
      printf("\nZufällig ausgewählte Zeilen der Datei:\n\n");
      int i = 0;
      while(i<20){
        // Erzeuge eine Zufallszahl zwischen 0 und 1726
        int random = rand() % 1726;
        // Falls die Zahl nicht 0 ist und auch nicht doppelt vorkommt
        if (doppelt(random, zufall)==0 && random != 0){
          printf("Zufallszahl: %i\n", random);
          //Weise sie dem Array zufall zu
          zufall[i] = random;
          ++i;
        }
      }
    
      // Wähle mit Hilfe der Indizes aus zufall[20] nun die entsprechenden Berzirke aus und verkette sie.
      int p, z;
      for (p=0; p<20; ++p){
        z = zufall[p];
    
        // Falls die Zufallszahl gleich der vom Benutzer eingegebenen Zeile (-1 wg. Index) ist,
        // Springe zum nächsten Schleifendurchlauf
        if(z==(zeile-1)) continue;
    
        // Ansonsten hänge die Werte an die Strukturenkette an.
        anhaengen(alle[z].x, alle[z].y, alle[z].name, 0);
      }
    
      // Gebe die zufällig gewählten Bezirke aus
      ausgeben();
    
      // Berechne die Route
      printf("\nMit Hilfe der Nearest Neighbor Methode ergibt sich die folgende Route:\n\n");
      int k;
      for(k=0; k<20;++k){
        if(k==19){
    
          // Schleife funktioniert ab 19 nicht mehr, da nur noch zwei Elemente in der Liste sind.
          // Gebe also zuerst den nächsten Startwert aus
          printf("19: \t%s\n", start->name);
    
          // Lösche ihn dann
          loeschen(start->name);
    
          // Anfangs-Zeiger zeigt nun auf das letze verbliebene Element. Gebe es aus.
          printf("20: \t%s\n", anfang->name);
        }
    
        /* sonst gebe zuerst den aktuellen Start aus, dann berechne die Distanzen neu. 
           Lösche das Element aus der Kette, das schon Start gewesen ist, also nicht mehr angefahren werden muss vom Handelsreisenden 
           und weise zum Schluss einen neuen Start zu.
        */
        else{
          printf("%d: \t%s\n", k, start->name);
          berechne_distanzen();
          loeschen(start->name);
          neuer_start();
        }
      }
      return 0;
    }
    

    Vielen Dank schonmal und Grüße
    Sebastian Hölzl
    ^



  • Hallo,

    s. gcc: 3.1 Option Summary sowie 3.2 Options Controlling the Kind of Output

    Also die Optionen "-x" und "-ansi":

    gcc -x c -ansi code.cpp -lm
    


  • Sebastian_Hoelzl schrieb:

    In der Uni benutzen wir Visual Studio und dessen C-Compiler. Dafür müssen die Programmdateien als .cpp gespeichert werden.

    VS verkraftet auch .c

    Sebastian_Hoelzl schrieb:

    Ich darf zur Lösung der Aufgabe nur ANSI-C benutzen.

    Streng genommen ist ANSI-C die Normung von 1989. Die kennt z.B. nicht den Zeilenkommentar mit //
    Danch ist es ISO-C.



  • Sowohl gcc wie auch der VStudio Compiler cl.exe "erkennen" C-Quellcodes an der Endung *.c und C++ Quellcodes an *.cpp, falls keine anderen expliziten Schalter für die Spracherkennung gesetzt sind.
    Leider setzt die VStudio IDE bei neuen Projekten defaultmäßig (ohne Nachfrage wie z.B. bei CodeBlocks) C++ als Standard, es sei denn man fügt explizit *.c Dateien zum Projekt hinzu.

    Für korrektes ANSI C muss man dann noch "Spracherweiterungen deaktivieren" in den Projekteinstellungen.

    Für gcc äquivalent:

    gcc -ansi *.c
    


  • ein Blick auf die Fehlermeldung sagt doch alles. malloc liefert void* zurück, da malloc ja nicht weiß, was man da für speicher reserviert hat.

    also
    if ((anfang = (struct bezirk*)malloc(sizeof(struct bezirk))) == NULL) {
    und
    if((zeiger->next = (strukt bezirk*) malloc(sizeof(struct bezirk))) == NULL){

    und schon sollte es funktionieren. dass der compiler von visual studio das durchgehen lässt............ 👎



  • HansKlaus schrieb:

    malloc liefert void* zurück

    Und void* ist in C ohne cast in jeden anderen Zeiger konvertierbar.



  • aha, ich habs trotzdem so gelernt. 😕
    aber ein expliziter typcast ist doch nicht unkonform, oder irre ich da?

    wäre aber auf jeden fall einfacher gewesen, dem compiler einfach seinen wunsch zu erfüllen.



  • Einfacher ja, aber nicht zielführend. Wenn man C kompilieren will, sollte man auch einen C-Compiler nehmen. Und nicht so lange mit dem Holzhammer auf den Code einprügeln, bis der C++-Compiler ihn frisst.



  • Der eigentliche Fehler ist hier die Dateiendung cpp statt c 💡



  • wenn man den compiler mit .cpp füttert, tut er ja auch so, als würde er c++ vor sich haben.
    irgendwie ist jetzt der zeitpunkt für ne grundsatzdiskussion darüber, ob c nicht eigentlich auch c++ bzw. c++ nur c mit erweiterungen ist, gekommen. 😃

    rename code.cpp code.c wäre aber langweilig gewesen



  • HansKlaus schrieb:

    wenn man den compiler mit .cpp füttert, tut er ja auch so, als würde er c++ vor sich haben.
    irgendwie ist jetzt der zeitpunkt für ne grundsatzdiskussion darüber, ob c nicht eigentlich auch c++ bzw. c++ nur c mit erweiterungen ist, gekommen. 😃

    rename code.cpp code.c wäre aber langweilig gewesen

    Nein.


  • Mod

    HansKlaus schrieb:

    irgendwie ist jetzt der zeitpunkt für ne grundsatzdiskussion darüber, ob c nicht eigentlich auch c++ bzw. c++ nur c mit erweiterungen ist, gekommen. 😃

    Da muss man nicht diskutieren, die Antwort ist, dass C keine Untermenge von C++ ist. Basta! Eines der wichtigsten Gegenbeispiele wurde hier im Thread genannt. Der C++-Standard hat eine lange Liste mit weiteren Inkompatibilitäten.


Anmelden zum Antworten