mikrocontroller.net

Forum: PC-Programmierung gcc Unterschiede zu g++ beischeinbar reinem C


Autor: Icke Muster (Firma: my-solution) (hendi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also ich wollte mal was fragen, wie immer...
Ich hab von Andreas Dannenberg folgenden Code kopiert:
/******************************************************************
 *****                                                        *****
 *****  Name: webconverter.cpp                                *****
 *****  Ver.: 1.0                                             *****
 *****  Date: 07/03/2001                                      *****
 *****  Auth: Andreas Dannenberg                              *****
 *****        HTWK Leipzig                                    *****
 *****        university of applied sciences                  *****
 *****        Germany                                         *****
 *****        adannenb@et.htwk-leipzig.de                     *****
 *****  Func: converts HTML-code to a C-constant              *****
 *****                                                        *****
 ******************************************************************/

//---------------------------------------------------------------------------
#include <stdio.h>


#pragma hdrstop
//---------------------------------------------------------------------------
#pragma argsused
int main(int argc, char* argv[])
{
  FILE *in, *out;
  char InChar;

  if (argc < 2)
  {
    fprintf(stdout, "Usage: WebConverter <infile> <outfile>\r\n");
    return 1;
  }

  if ((in = fopen(argv[1], "rb")) == NULL)
  {
    fprintf(stderr, "Cannot open input file.\n");
    return 1;
  }

  if ((out = fopen(argv[2], "wb")) == NULL)
  {
    fprintf(stderr, "Cannot open output file.\n");
    return 1;
  }

  fprintf(out, "const unsigned char WebSide[] = {\r\n\"");

  while (!feof(in))
  {
    InChar = fgetc(in);
    switch (InChar)
    {
      case 0x22 : fputc('\\', out);
                  fputc('"', out);
                  break;
      case 0x0D : fputc('\\', out);
                  fputc('r', out);
                  fputc('\\', out);
                  fputc('n', out);
                  fputc('"', out);
                  fprintf(out, "\r\n");
                  fputc('"', out);
                  break;
      case 0x0A : break;
      case 0xFF : break;
      default   : fputc(InChar, out);
    }
  }

  fputc('\\', out);
  fputc('r', out);
  fputc('\\', out);
  fputc('n', out);

  fputc('"', out);
  fprintf(out, "};\r\n");

  fclose(in);
  fclose(out);

  return 0;
}
//---------------------------------------------------------------------------

das Ding nennt sich Webconverter und soll eigentlich nur z.B. ne 
Webseite in ein Char Array "konvertieren". Macht also am Ende 
Anführungszeichen und Zeilenumbrüche, bla... Nun hab ich das mit dem gcc 
übersetzt und da kam am Ende(bei Ausführung) immer sowas raus:
>>>>>"ÿ\r\n"};<<<<<
Das stand dann am Ende des erzeugten Arrays. Kann man das irgendwie 
erklären? Es hat ne Weile gedauert, bis ich raus hatte, dass es in C++ 
übersetzt werden sollte, dann gibt es dies komischen Probleme nicht, die 
besagte Zeile sieht dann so aus: >>>>>"\r\n"};<<<<<
Hat irgendjemand von euch eine Ahnung wo plötzlich das ÿ(0xFF) her 
kommt? Hat cpp nen anderen Zeichensatz? Also es ist jetzt nicht sofort 
wichtig das zu wissen, aber es interessiert mich schon sehr, weil ich 
bisher dachte, das gcc und g++ bei reinem C Code gleich handeln(vll. 
stimmt aber auch meine Unterscheidung nicht).
Zu guter Letzt, auch wenns echt richtige PillePalle ist noch meine 
Änderung:
if(argv[3] != NULL){
  fprintf(out, "const unsigned char ");
  fprintf(out, argv[3]);
  fprintf(out, "[] = {\r\n\"");
  }
  else{
  fprintf(out, "const unsigned char WebSide[] = {\r\n\"");
  }
is halt dafür gedacht die Arrays umzubenennen, wie gesagt Quatsch, aber 
vll hilfts ja nem NOOB, zumindest gibts den kompletten Code des Files 
IMHO bei Google nur einmal....
Danke für eure Antworten!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist eher Zufall, dass das mit dem C++ Compiler geklappt
hat.

Der Fehler ist in beiden Fällen, die fehlerhafte eof() Behandlung.

Merk dir folgende 2 Sätze:
"C und C++ versuchen nicht in die Zukunft zu schauen. Ob eine
Datei zu ende ist, kann erst festgestellt werden, nachdem ein
Leseversuch gestartet wurde und dieser Versuch fehlgeschlagen ist".

Die korrekte Art, in beiden Sprachen, eine Date zu lesen, besteht
immer darin, die Leseschleife auf dem Returnwert der Lesefunktion
aufzubauen. Dazu haben ausnahmslos alle Lesefunktionen sowohl
in C als auch in C++ einen Returnwert der sich dafür eignet.

feof() ist nicht dafür gedacht, die Leseschleife zu steuern,
sondern wird im Anschluss an die Leseschleife benutzt um fest-
zustellen, warum die Leseschleife abgebrochen wurde. Die Lese-
schleife bricht, aufgrund des Returnwertes der Leseoperation,
irgendwann ab. War dies, weil eof aufgetreten ist, dann ist alles
gut: Die Datei konnte fehlerfrei und vollständig gelesen werden.

Das Schema ist also immer

   while( Leseoperation ) {
     verabeite das Gelesene
   }

   if( !feof( ... ) ) {
     melde einen fehler und gegebenenfalls
     ein return
   }

   eventuell noch mit dem gelesenen etwas machen.


Dein Job ist es jetzt, rauszufinden welcher spezielle
Returnwert von fgetc kommt, um anzuzeigen, dass die
Leseoperation nicht geklappt hat. Hinweis: Es hat damit
zu tun, dass der Returnwert von fegtc wieder erwarten kein
char oder unsigned char sondern ein int ist.
Und ja, die 0xFF haben damit zu tun.


PS: Den obigen Fehler findet man sehr häufig. Sogar in Lehrbüchern.
Was die feof() Schleife auch nicht richtiger macht. Die klassische
Frage, die in Newsgroups dann normalerweise auftaucht, lautet
dazu: Warum wird mein letzter Datensatz doppelt verarbeitet?

Autor: Icke Muster (Firma: my-solution) (hendi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke erst mal für deine Antwort.
Der Returnwert von fgetc() ist -1, weil das Dateiende erreicht ist. Die 
Fkt. feof() ist ja aber scheinbar nur dafür da, zu unterscheiden, warum 
die Datei nicht gelesen werden konnte, also ob wirklich das Ende 
erreicht wurde oder ein anderer Fehler auftrat. Ich hab mir deinen Post 
hier: Beitrag "Datei einlesen mit C" noch mal durchgelesen. 
Hab mal n bissel rum gespielt. Also mit feof() könnte man es eigentlich 
so machen:
while(1){
InChar = fgetc(*FILE);
if(feof(*FILE))
    break;

//....Verarbeitung....
}
Dann bleibt die Geschichte aber hängen, wenn ein anderer Fehler 
auftritt. Also macht:
while((InChar = fgetc(*FILE)) != EOF){
//      ....Verarbeitung....
}
//Test ob Abbruch wegen Dateiende
if(!feof)
    printf("Datei konnte nicht richtig eingelesen werden...");
wohl mehr Sinn. Zumindest wenn ich das richtig verstanden hab. Also war 
mein Fehler, dass im Prinzip die letzte -1 auch noch geschrieben wurde, 
oder?
Es ist übrigens wirklich so, dass ich bei mehreren Beispielen im Netz 
den Test mit while(!feof()){...} gefunden hab. Naja, auf jeden Fall 
klappt es jetzt auch mit gcc, warum die Geschichte mit g++ funktioniert 
hat bleibt damit immer noch offen, aber das muss man wohl akzeptieren...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Icke Muster wrote:
> Der Returnwert von fgetc() ist -1, weil das Dateiende erreicht ist.

Exakt. Das Makro EOF, welches diese -1 kapselt hast du ja anscheinend
auch gefunden.

> Die
> Fkt. feof() ist ja aber scheinbar nur dafür da, zu unterscheiden, warum
> die Datei nicht gelesen werden konnte, also ob wirklich das Ende
> erreicht wurde oder ein anderer Fehler auftrat.

Ganz genau.
Die Geschichte von C ist voller Missverständnisse :-)

> Ich hab mir deinen Post
> hier: Beitrag "Datei einlesen mit C" noch mal durchgelesen.
> Hab mal n bissel rum gespielt. Also mit feof() könnte man es eigentlich
> so machen:
>
> while(1){
> InChar = fgetc(*FILE);
> if(feof(*FILE))
>     break;
> 
> //....Verarbeitung....
> }
> 

Könnte man.
Ist aber ziemlich umständlich. Wo man so ein Schema aber benutzen
kann ist, wenn das Lesen selbst aus mehreren einzelnen Leseoperationen
besteht.

> Dann bleibt die Geschichte aber hängen, wenn ein anderer Fehler
> auftritt. Also macht:
>
> while((InChar = fgetc(*FILE)) != EOF){
> //      ....Verarbeitung....
> }
> //Test ob Abbruch wegen Dateiende
> if(!feof)
>     printf("Datei konnte nicht richtig eingelesen werden...");
> 
> wohl mehr Sinn. Zumindest wenn ich das richtig verstanden hab.

Du hast.
Das ist die Standardform, mit der man die Einleserei macht.
In diesem Fall: Die Klammerung in der Bedingung der while
Schleife nicht vergessen! Das gibt sonst überraschende Ergebnisse.

> Also war
> mein Fehler, dass im Prinzip die letzte -1 auch noch geschrieben wurde,
> oder?

Genau.
Meist findest du den Fehler in folgender Form:

   while( !feof( in ) ) {
     fgets( buffer, sizeof( buffer ), in );
     printf( "%s" );
   }

und die Frage, die dann auftaucht lautet: Warum wird die
letzte Zeile in meiner Datei doppelt ausgegeben (bearbeitet)?

Sollte jetzt klar sein, warum das so ist.
Dieser Fehler ist natürlich besonders peinlich, wenn in der
Datei zb. Abbuchungen von einem Konto stehen. Der (Bank-)Kunde
wird sich schön bedanken, wenn ihm eine Abbuchung doppelt vom
Konto abgezogen wirde, nur weil zufällig seine Transaktion die
letzte in der Datei war.

> Es ist übrigens wirklich so, dass ich bei mehreren Beispielen im Netz
> den Test mit while(!feof()){...} gefunden hab.

Traurig, aber wahr. Dasselbe Problem findet sich auch in vielen
Büchern.

Die interessantere Frage lautet doch: Warum ist das so?
In anderen Sprachen ist das nämlich nicht so. Da wird eof
mit dem letzten gelesenen Zeichen gesetzt. Nicht so in C.

Der Grund liegt in der Vereinheitlichung aller Eingaben zum
Standard-Eingabe Mechanismus 'stream'. Egal ob eine Eingabe
von einem Keyboard kommt, von einem File, von einer Modemverbindung
etc. immer landet, aus C-Sicht, alles in einem Stream. D.h. aber
auch, man braucht vereinheitlichte Mechanismen. Wie willst du
aber feststellen, ob diese Eingabe vom Benutzer tatsächlich
jetzt die letzte wahr? Nur weil der Input Buffer zur Zeit leer
ist, heist das ja nicht, dass der Benutzer keine Eingaben mehr
machen will. Oder eine Modemstrecke? Nur weil zur Zeit kein
Zeichen auf der UART liegt, muss ja die Übertragung noch lange
nicht zu Ende sein. Während dein Programm dieses eine Zeichen
verarbeitet, könnte ja immer noch ein neues Zeichen auf der
UART eintrudeln. Dasselbe mit Files. Auf Unix Systemen ist es
durchaus möglich, dass 2 Programme ein und dieselbe Datei
gleichzeitig geöffnet haben. Während der eine liest, schreibt
das andere Programm munter in die Datei.
Also: Selbst wenn das letzte Zeichen vom Strom gelesen wurde,
heist das nicht notwendigerweise, dass damit eof() erreicht
ist. Während dieses letzte Zeichen bearbeitet wird, könnte
ein neues letztes Zeichen eintrudeln. Erst wenn beim
nächsten Leseversuch dann tatsächlich keines vorhanden ist,
kann man davon ausgehen, dass damit auch wirklich eof
erreicht ist. Das kann zb im Falle eines Modems auch bedeuten,
dass ein Timeout abgewartet werden muss oder im Falle eines
Terminals der Benutzer eof extra signalisieren muss (CTRL-Z oder
CTRL-D).
Aus Sicht des Programmes macht das aber alles keinen Unterschied.
Die Frage lautet ganz einfach: Konnte gelesen werden oder konnte
nicht.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.