Wieso ist 'Ä' -60 ?


  • Mod

    Das wäre doch auch irgendwie doof, wenn das Programmverhalten (oder schlimmer noch, ob es sich überhaupt übersetzten lässt) eines standardkonformen Programms von der Zeichencodierung auf der Maschine auf der ich das Programm übersetzt habe, abhinge.



  • krümelkacker schrieb:

    camper schrieb:

    krümelkacker schrieb:

    Es ist die Rede von 3 Zeichensätzen:

    • basic source character set (Leerzeichen, horizontal-tab, vertical-tab, form-feed, new-line und 91 graphische Zeichen a-z, A-Z, 0-9 und _{}[]#()<>%:;.?*+-/^&|~!=,\"')
    • physical source file character set (enthält das basic source character set)
    • execution character set (enthält das basic source character set)

    Ich sehe im Standard nirgendwo ein source character set das nicht das basic source character set ist.

    'Ä' ist kein Teil davon.

    §2.1/1

    Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set (introducing new-line characters for end-of-line indicators) if necessary. Trigraph sequences (2.3) are replaced by corresponding single-character internal representations. Any source file character not in the basic source character set (2.2) is replaced by the universal-character-name that designates that character. (An implementation may use any internal encoding, so long as an actual extended character encountered in the source file, and the same extended character expressed in the source file as a universal-character-name (i.e. using the \uXXXX notation), are handled equivalently.)

    Nach dieser Phase gibt es keine Zeichen mehr, die nicht im "basic character set" sind; denn ein universal-character-name besteht aus Zeichen, die aus dem basic source character set kommen (Beispiel: \u00C4, also Backslash, u und HexQuad). Von daher braucht der Rest des Standards da auch nicht mehr drauf eingehen. Der Zeichensatz der Eingabedatei könnte ein Ä enthalten; denn, was das für ein erweiterter Zeichensatz ist, wurde nicht näher beschrieben und die letzte unterstrichene Stelle im zitierten Text macht klar, dass auch Zeichen vorkommen können, die nicht im basic source character set enthalten sind. Die werden dann eben nur durch ein universal-character-name ersetzt.

    Ok. source file character set also.
    Das ist allerdings trotzdem keine Übermenge des basic character sets.
    1. Nicht alle Maschinen vefügen überhaupt physisch über alle Zeichen des baisc character sets. Andernfalls gäbe es auch keinen Grund, Di- und Trigraphen zu haben.
    2. Zweitens ist das Mapping implementation-defined - es wird keine 1:1 Äquivalenz gefordert. Z.B. wird nicht bestimmt, dass Unicode Äquivalenz zum gleichen Zeichen führen muss.

    Meiner Ansicht nach ist die Frage, ob ein Programm wohlgeformt ist (ein undefinierter Begriff, denn man einfach als Negation von ill-formed einführen kann), nicht sinnvoll zu beantworten, bevor die Übersetzung in das basic character set stattgefunden hat.



  • Nur nochmal so als Nachtrag: Laut MSVC-Doku, verwendet der Compiler ASCII als execution character set und erwartet auch ASCII als Quellcode-Dateien. Dementsprechend kann man im ASCII-Quellcode auch kein 'Ä' unterbringen. Selbst ein '\u00CF' würde nicht funktionieren, da der Compiler es zum execution character set konvertieren müsste -- was nicht geht, da ASCII kein Ä enthält. Ich habe bis jetzt auch keine Optionen diesbezüglich gefunden. Eine mögliche Erklärung für die Originalfrage wäre also:

    • Der Compiler erwartet ASCII, unterstützt Ä offiziell also gar nicht, und prüft die Zeichen einfach nicht.
    • Der Quellcode liegt in ISO-8859-1 vor, dort hat das Ä den Code 0xC4
    • Der Compiler geht die ganze Zeit von ASCII aus. Und da auch das execution character set ASCII ist, muss nichts konvertiert werden und die 0xC4 bleibt erhalten.
    • char ist vorzeichenbehaftet und verwendet das 2er Komplement für negative Zahlen.

    Der GCC-Doku kann man entnehmen, dass über die -finput-charset= Option festgelegt werden kann, wie Quellcodedateien kodiert wurden. Damit kann er auch Zeichen wie Ä verarbeiten. Teil der Übersetzung ist jedoch die Konvertierung in das execution character set. Das scheint unter WinXP beim g++ (tdm-1) 4.5.1 UTF-8 per Default zu sein, was auch erklärt, warum ich eine Multibyte-Warnung bekomme:

    // Diesen Quellcode habe ich in UTF-8 gespeichert.
    char foo() { return '\u00Cf'; }
    char bar() { return 'Ä'; }
    
    >g++ -c -Wall -finput-charset=UTF-8 test.cpp
    test.cpp:2:21: warning: multi-character character constant
    test.cpp:3:21: warning: multi-character character constant
    test.cpp: In function 'char foo()':
    test.cpp:2:21: warning: overflow in implicit constant conversion
    test.cpp: In function 'char bar()':
    test.cpp:3:21: warning: overflow in implicit constant conversion
    

    (Dieselbe Ausgabe sieht man, wenn man -fexec-charset=UTF-8 verwendet). Klar, ein UTF-8-kodiertes Ä ist ja kein einzelnes Byte mehr. Mit ISO-8859-1 als exec-charset:

    >g++ -c -Wall -finput-charset=UTF-8 -fexec-charset=ISO-8859-1 test.cpp
    >
    

    klappt das Ganze. Obwohl in der Quellcodedatei das Ä immernoch aus 2 Bytes besteht, konvertiert der Compiler dies zum "execution character set". Das ist in diesem Fall "single-byte" und die Warnungen verschwinden.

    Da bei mir der Standard-CMD.exe-Zeichensatz Codepage 850 ist, habe ich mal probiert, ob ich als Zeichensatz einfach 850 eingeben kann und siehe da:

    >g++ -c -Wall -finput-charset=UTF-8 -fexec-charset=850 test.cpp
    >
    

    Der Compiler schluckt es ohne Warnung. Ein Blick in das Ergebnis bringt aber etwas Ernüchterung:

    >objdump -Cd test.o
    
    test.o:     file format pe-i386
    
    Disassembly of section .text:
    00000000 <foo()>:
       0:   55                      push   %ebp
       1:   89 e5                   mov    %esp,%ebp
       3:   b0 d8                   mov    [b]$0xd8[/b],%al
       5:   c9                      leave
       6:   c3                      ret
    00000007 <bar()>:
       7:   55                      push   %ebp
       8:   89 e5                   mov    %esp,%ebp
       a:   b0 8e                   mov    [b]$0x8e[/b],%al
       c:   c9                      leave
       d:   c3                      ret
    

    Lediglich bar() liefert mir hier das erwartete Ergebnis; denn das Ä hat in der Codepage 850 den Code 0x8E. Was das 0xD8 hier zu suchen hat, weiß ich nicht. Ich gehe mal davon aus, dass es ein Bug ist; denn für Ä klappt es ja und der Standard verlangt, dass in dem Fall beide Versionen hätten äquivalent übersetzt werden müssen.

    Obwohl der 2011er Standard immer noch nicht verlangt, dass ein Compiler UTF-8- und/oder UTF-16-kodierte Quelltexte lesen können muss, kann es sich meiner Meinung nach kein Hersteller erlauben, ein solches Feature nicht anzubieten; denn sonst würden die "Unicode-Literale" einfach keinen Spaß machen.

    const char utf8_string_1[] = u8"H\u00E4nsel und Gretel"; // (1)
    const char utf8_string_2[] = u8"Hänsel und Gretel";      // (2)
    

    (1): 100% portabal in C++2011
    (2): Der Compiler muss das ä lesen und verstehen können. Diese Fähigkeit ist optional.

    Habe ich beim MSVC etwas übersehen bzgl Optionen?


Anmelden zum Antworten