Gestern habe ich stundenlang nach einem Fehler gesucht, der sich als
sehr raffiniert erwies. Ein Programm von mir zur Darstellung und
Bearbeitung von Daten zeigte bei einer bestimmten Konfigurationsdatei
plötzlich nur noch Müll, obwohl es sonst immer wie erwartet
funktionierte. Eine ältere Version des Programms arbeitete auch damit
korrekt.
Um dem auf die Spur zu kommen, habe ich diese Datei immer wieder
verkürzt und nochmal getestet. Mit 10 Zeilen war es noch Müll, mit 5
Zeilen war wieder alles in Ordnung. Schließlich stellte sich heraus,
dass es zwei Zeilen waren, die das Chaos verursachten. Ohne diese beiden
Zeilen funktionierte auch der Rest von 160 Zeilen.
Verwirrend war, dass die beiden destruktiven Zeilen syntaktisch gleich
aufgebaut waren wie die korrekten. Also musste es am Inhalt liegen. Es
zeigte sich, dass die Zahlen 08 und 09 die Übeltäter waren, verkürzt auf
8 und 9 ging es dann wieder.
Also nochmal in die Source geguckt, wo die Zahlen interpretiert wurden.
Die Ursache liegt in der Libfunktion strtol. Mit 0 als dritten Parameter
liest er auch Hexzahlen, wenn sie mit 0x anfangen. Schön praktisch,
dachte ich wohl.
Dass aber eine Zahl, die mit 0 anfängt, nicht dezimal sondern oktal
gelesen wird, wird bei den Funktionsbeschreibungen selten erwähnt,
wodurch es zu solch heimtückischen Fehlern kommt.
Man kann also nur davor warnen, strtol() mit 0 als drittem Parameter zu
verwenden.
Jobst Q. schrieb:> Man kann also nur davor warnen, strtol() mit 0 als drittem Parameter zu> verwenden.
Oder man liest die Doku dieser Funktion, und stellt fest, dass sie sich
genau verhält wie beschrieben...
Das Verhalten ist so implementiert, weil in C eine Integer-Zahl mit
führender Null als Oktalzahl interpretiert wird. Du müsstest also bei
nicht weiter validiertem User-Input noch eine entsprechende
"Bereinigung" vornehmen.
Jobst Q. schrieb:> Man kann also nur davor warnen, strtol() mit 0 als drittem Parameter zu> verwenden.
Danke für den unterhaltsamen und lehrreichen Bericht.
Bitte die Leute ignorieren die alle Dokus auswendig kennen, niemals
etwas übersehen und alles besser wissen.
> Bitte die Leute ignorieren die alle Dokus auswendig kennen, niemals> etwas übersehen und alles besser wissen.
Hat keiner behauptet, im Gegenteil. Meinen Beitrag hätte ich auch als
"RTFM!" abkürzen können, im konstruktiv gemeinten Sinne.
Wenn ich so eine Funktion benutze - insbesondere eine Implementation für
eine kleine 8-bit µC-Architektur - dann schaue ich immer in die Doku.
Gerade weil ich nicht alles auswendig weiß, und weil es subtile
Unterschiede und Einschränkungen geben kann. Die kann ich entweder
selbst empirisch herausfinden, oder ich werfe vorab einen Blick in die
Doku. Letzteres lernt man durch Erfahrungen, die man mit ersterem
gemacht hat.
Solche Parameter wie "0" für die Basis sind offensichtlich ein
Sonderfall, die sich nicht aus dem Funktionsnamen ergeben! Es würde mich
als Programmierer interessieren, wie sich die Funktion verhält, wenn
unzulässige Zeichen in dem String vorhanden sind, etc. Diese Dinge
nachzuschauen, ist nun wirklich kein Zeichen von "alles besser wissen".
Jobst Q. schrieb:> Dass aber eine Zahl, die mit 0 anfängt, nicht dezimal sondern oktal> gelesen wird, wird bei den Funktionsbeschreibungen selten erwähnt,> wodurch es zu solch heimtückischen Fehlern kommt.
Ich habe auf die Schnelle (Auswertung der ersten 20 Google-Treffer von
"strtol") keine einzige Funktionsbeschreibung gefunden, die den
speziellen Wert 0 für das dritte Argument zwar beschreibt, aber die
Bedeutung der führenden '0' des Eingabestrings als Präfix für eine
Oktalzahl verschweigt.
Deswegen würde ich das "selten" durch "fast immer" ersetzen.
Thomas schrieb:> Bitte die Leute ignorieren die alle Dokus auswendig kennen, niemals> etwas übersehen und alles besser wissen.
Wer die Doku nicht oder nicht vollständig gelesen hat, wird gar nicht
erst auf die Idee kommen, als drittes Argument eine 0 zu übergeben.
Der Vollständigkeit halber noch die Stelle im C-Standard (C99 in diesem
Falle, aber bis auf sich ändernde Kapitelnummerierungen steht das auch
heute noch so drin):
"If the value of base is zero, the expected form of the subject sequence
is that of an integer constant as described in 6.4.4.1, optionally
preceded by a plus or minus sign, but not including an integer suffix."
6.4.4.1 dokumentiert die Syntax der Integer-Konstanten, wie sie vom
Compiler verarbeitet werden sollen.
Jochen schrieb:> Das Verhalten ist so implementiert, weil in C eine Integer-Zahl mit> führender Null als Oktalzahl interpretiert wird.
So ist das eben, eine Fehlkonstruktion zieht weitere nach sich. Eine
gültige Ziffer als Präfix zu verwenden ist schon ziemlich unintelligent.
> Du müsstest also bei> nicht weiter validiertem User-Input noch eine entsprechende> "Bereinigung" vornehmen.
Einfacher geht es, das Einlesen auf Dezimalzahlen zu beschränken. Für
den Fall, viele Formate einlesen zu können, habe ich jetzt eine
Rahmenfunktion geschrieben:
1
/*-----------------------------------*/
2
longintstrtolx(constchar*s,char**endptr){
3
4
intb=10;
5
charc;
6
7
if(endptr!=NULL)*endptr=s;
8
9
if(*s=='0'){
10
c=*(s+1)|32;
11
if(c=='x')b=16;
12
elseif(c=='o'){b=8;s+=2;}
13
elseif(c=='b'){b=2;s+=2;}
14
}
15
16
elseif(*s=='_'){
17
b=strtol(++s,&s,10);
18
if((*s!='_')||b<2||b>36)return0;
19
s++;
20
}
21
22
returnstrtol(s,endptr,b);
23
}
Damit kann man nicht nur Hex- (0x),Oktal- (0o)und Binärzahlen(0b)
einlesen, sondern Zahlen zu beliebiger Basis in der Form: _Basis_Zahl.
Yalu X. schrieb:> Wer die Doku nicht oder nicht vollständig gelesen hat, wird gar nicht> erst auf die Idee kommen, als drittes Argument eine 0 zu übergeben.
Eben!
Bei vielen Funktionen der Standard-Bibliotheken kann man sich die
Verwendung anhand des Funktionsnamens und der Parameterliste (Name
wegdenken!) schon intuitiv herleiten. Das ist übrigens ein sehr gut
geeignetes Kriterium für die Qualität einer API!
Gutes Beispiel: int strncmp(const char *, const char *, size_t)
Da brauche ich keine benannten Parameter, um zu erkennen, was was ist.
Schlechtes Beispiel: long int strtol(const char *, char **, int)
Wofür int steht, ist nicht direkt erkennbar, wenn man strtol noch nie
benutzt hat. Deshalb -> Doku...
Ich habe mich schon so daran gewöhnt, das Zahlen mit einer 0 davor immer
octal sind, dass es mich mehr verwirren würde, wenn das ein Programm mal
anders macht. Warum sollte man aber auch unnötige 0en vor dezimale
Zahlen schreiben wollen? Da muss man nur suchen, wo die Zahl endlich
anfängt. Besser einfach ein paar Abstände davor, wenn man es rechts
bündig eingerückt haben will.
DPA schrieb:> Warum sollte man aber auch unnötige 0en vor dezimale> Zahlen schreiben wollen?
Je nun, darauf hast du bei Daten, die von außen kommen, keinen Einfluß.
Oliver
DPA schrieb:> Warum sollte man aber auch unnötige 0en vor dezimale> Zahlen schreiben wollen?
Das ist in Zeit-Daten absolut üblich, und in Kernel-Logfiles sieht man
schonmal sowas wie das hier:
pcpu-alloc: [0] 00 01 02 03 04 05 06 07 [0] 08 09 10 11 12 13 14 15
pci 0000:08:00.6:
Jobst Q. schrieb:> Einfacher geht es, das Einlesen auf Dezimalzahlen zu beschränken. Für> den Fall, viele Formate einlesen zu können, habe ich jetzt eine> Rahmenfunktion geschrieben:> [...Bugs und implementation defined behavior...]> Damit kann man nicht nur Hex- (0x),Oktal- (0o)und Binärzahlen(0b)> einlesen, sondern Zahlen zu beliebiger Basis in der Form: _Basis_Zahl.
Über strtol beschweren aber dann so einen Mist schreiben.
Nop schrieb:> DPA schrieb:>> Warum sollte man aber auch unnötige 0en vor dezimale>> Zahlen schreiben wollen?>> Das ist in Zeit-Daten absolut üblich
Die einzelnen Zahlen in Zeitangaben werden aber praktisch immer dezimal
geschrieben. Deswegen wird man dafür das dritte Argument von strtol
einfach auf 10 setzen, und die "Oktalfalle" ist beseitigt.
Was mich in der Hinsicht eigentlich immer am ehesten geärgert hat: wenn
man irgendeinen generischen String braucht, kann man immer "foo" nehmen.
Für eine generische Zahl kann man jedoch nicht "0815" benutzen. :)
Jörg W. schrieb:> Was mich in der Hinsicht eigentlich immer am ehesten geärgert hat: wenn> man irgendeinen generischen String braucht, kann man immer "foo" nehmen.> Für eine generische Zahl kann man jedoch nicht "0815" benutzen. :)
Deswegen hat sich das Internet auf 42 geeinigt :-) (Stell dir mal vor du
würdest antworten "der Fehler ist in Zeile 0815")
Jobst Q. schrieb:> Für> den Fall, viele Formate einlesen zu können, habe ich jetzt eine> Rahmenfunktion geschrieben:
Bei der du statt der in C üblichen Schreibweise zwei andere umsetzt, die
beide auch sonst nirgends gebräuchlich sind. Gerade auf sowas würde ich
verzichten, wenn ich nicht einen sehr guten Grund hätte, von gängigen
Schreibweisen abzusehen.
> /*-----------------------------------*/> long int strtolx (const char* s, char** endptr){
Der Name ist übrigens ungültig. Alles, was mit str gefolgt von einem
Kleinbuchstaben beginnt, ist für den Compiler bwz. die
Standardbibliothek reserviert.
Jörg W. schrieb:> Was mich in der Hinsicht eigentlich immer am ehesten geärgert hat: wenn> man irgendeinen generischen String braucht, kann man immer "foo" nehmen.> Für eine generische Zahl kann man jedoch nicht "0815" benutzen. :)
Da nimmt man ja auch 42. Wenn's denn vierstellig sein muss, hat damals
mein Professor immer 4711 verwendet.
Übrigens wäre 0815 auch streng genommen nicht richtig. Das MG heißt ja
eigentlich 08/15.
C hält sich streng an einmal festgelegte Regeln, das ist doch schön.
Hast Du schon mal mit Excel gearbeitet?
Das hat einen eingebauten Zufallsgenerator, wie es importierte
Zahlenreihen umzuwandeln gedenkt. Mal werden es dann Währungen oder
Zeiten oder Datumsangaben oder sonstwas.
Wenn man da nicht aufpaßt, wie ein Lux, sind die mühsam erstellten
Meßreihen für die Katz.
Jobst Q. schrieb:> Damit kann man nicht nur Hex- (0x),Oktal- (0o)und Binärzahlen(0b)> einlesen, sondern Zahlen zu beliebiger Basis in der Form: _Basis_Zahl.
Und?
Wozu sollte das gut sein?
Nur um der Welt zu zeigen, wie gut du programmieren kannst?
Normalerweise weiß man als Programmierer, was für numerische Eingaben
das Programm an welcher Stelle erwartet. Folglich sollte man unbenutzte
Zahlenformate erst gar nicht implementieren, um vor Fehlern bei der
Benutzung eher geschützt zu sein und den Benutzer besser auf ein falsch
getipptes Zeichen hinweisen zu können.
W.S.
Peter D. schrieb:> C hält sich streng an einmal festgelegte Regeln, das ist doch schön.>> Hast Du schon mal mit Excel gearbeitet?> Das hat einen eingebauten Zufallsgenerator, wie es importierte> Zahlenreihen umzuwandeln gedenkt. Mal werden es dann Währungen oder> Zeiten oder Datumsangaben oder sonstwas.> Wenn man da nicht aufpaßt, wie ein Lux, sind die mühsam erstellten> Meßreihen für die Katz.
Ich weiß nicht wie es bei Excel ist, aber ein eher unbekannter online
Konkurrent wandelt die Eingabe 2015-06-05 automatisch in den Wert
2015-05-06 ;-)
(prx) A. K. schrieb:> Ich bin schon Software begegnet, die IP Adressen mit strtol(,0)> konvertiert. Wer dann seine Adresse als 192.168.178.010 eingibt...
Das hat nicht unbedingt mit strtol zu tun. Diese Interpretation ist bei
den Berkley-Sockets so vorgesehen und z.B. auch in Linux in manchen
Tools so vorhanden. Aus der man-Page der Funktion inet_aton() (und
inet_addr):
"In all of the above forms, components of the dotted address can be
specified in decimal, octal (with a leading 0), or hexadecimal, with a
leading 0X). Addresses in any of these forms are collectively termed
IPV4 numbers-and-dots notation. The form that uses exactly four decimal
numbers is referred to as IPv4 dotted-decimal notation (or sometimes:
IPv4 dotted-quad notation)."
Daher gibt es durchaus einige Kommandozeilen-Programme, die das so
interpretieren. Die POSIX-Version der Funktion (die dann inet_pton()
heißt) definiert dagegen nur die dezimale Notation. Aus dessen man-Page:
AF_INET
src points to a character string containing an IPv4 network address in
dotted-decimal format, "ddd.ddd.ddd.ddd", where ddd is a decimal number
of up to three digits in the range 0 to 255.
Interessant ist die Dokumentation von inet_addr unter Windows. Da heißt
es:
"The inet_addr function converts a string containing an IPv4
dotted-decimal address into a proper address for the IN_ADDR structure."
Weiter unten heißt es dann aber dem widersprechend, dass dort auch Oktal
und Hexadezimal gehen.
https://docs.microsoft.com/en-us/windows/win32/api/wsipv6ok/nf-wsipv6ok-inet_addr
Bei IPs hat irgendwer auch mal noch Kurzformen eingeführt. 127.1
entspricht 127.0.0.1 ...
Und dann gehen meistens noch Sachen wie 0x7F000001 oder 0x7f.1, was dann
zu 127.0.0.1 wird!!!
Peter D. schrieb:> C hält sich streng an einmal festgelegte Regeln, das ist doch schön.
Das wäre schön, wenn diese Regeln in der heutigen Zeit noch sinnvoll
wären. Sie stammen aber leider aus einer Zeit, die ca. ein halbes
Jahrhundert her ist. Speziell das Oktalsystem ist heute vollkommen
huppse. Nur genauso historische APIs und OSs wie etwa POSIX und Linux
benutzen so einen Quatsch noch. Die heilige Abwärtskompatibilität
schleppt hier völlig überflüssige Scheisse über Jahrzehnte mit.
> Hast Du schon mal mit Excel gearbeitet?> Das hat einen eingebauten Zufallsgenerator, wie es importierte> Zahlenreihen umzuwandeln gedenkt.
Quatsch. Bei Excel werden genauso Regeln angewandt wie bei C. Nur halt
andere. Wenn man sie lernt, kann man damit genauso souverän umgehen, wie
mit den (mindestens genauso) schwachsinnigen Regeln von C.
c-hater schrieb:> Bei Excel werden genauso Regeln angewandt wie bei C. Nur halt> andere. Wenn man sie lernt, kann man damit genauso souverän umgehen, wie> mit den (mindestens genauso) schwachsinnigen Regeln von C.
Wo genau sind die Regeln dokumentiert, wann und wie ein Datum in ein
anderes konvertiert werden?
Dir ist klar, dass C nicht alleine ist mit diesen Regeln? Soweit ich
weiß nutzen Java, C#, und Go die gleiche Schreibweise für oktale
Literale.
Mombert H. schrieb:> Soweit ich weiß nutzen Java, C#, und Go die gleiche Schreibweise für> oktale Literale.
Bei Java und C# wundert es nicht, denn sie haben zu einem Teil die
Syntax (vor allem von C++) geerbt. Bei Go schon eher. Gerade
nachgesehen, eine formale Syntaxbeschreibung für Rust gibt es leider
nicht, aber irgendwo im Rust-Buch erwähnen sie dann, dass Oktalzahlen
mit `0o' eingeleitet werden. Durchaus eine vernünftige Entscheidung.
Aber die Entscheidung, dass man Oktalzahlen gar nicht mehr brauchen
kann, haben sie sich auch nicht getraut. :)
Daniel A. schrieb:> Und dann gehen meistens noch Sachen wie 0x7F000001 oder 0x7f.1, was dann> zu 127.0.0.1 wird!!!
Das musst du garnicht hexadezimal machen oder mit Punkten trennen, das
funktioniert auch als einfache Dezimalzahl.
Die meisten Browser (oder alle?) wandeln folgende URL: http://3232235777
automatisch nach http://192.168.1.1
Damit sieht man auch, wie man aufpassen muss, wenn man bestimmte
IP-Adressen sperren will, ein einfacher String Match mit "192.168.*"
reicht dazu nicht.
Michael
Jörg W. schrieb:> Gerade> nachgesehen, eine formale Syntaxbeschreibung für Rust gibt es leider> nicht, aber irgendwo im Rust-Buch erwähnen sie dann, dass Oktalzahlen> mit `0o' eingeleitet werden.
Die Syntaxbeschreibung gibt es in der Doku. Zu den Integer-Literals:
https://doc.rust-lang.org/reference/tokens.html#integer-literals
Rolf M. schrieb:> Zumindest die lexikalischen Regeln sind beschrieben.
Danke, zumindest unter "Dokumentation" habe ich das auf rust-lang.org so
schnell nicht gefunden. Ja, irgendsowas hatte ich da eigentlich
erwartet.
Ich weiß, auch C hat mit einem Paper und dann später einem Buch
angefangen, aber die hatten wenigstens im Anhang auch immer die formale
Syntaxbeschreibung mit drin. Das habe ich als Anhang im Rust-Buch
vermisst.
c-hater schrieb:> Bei Excel werden genauso Regeln angewandt wie bei C. Nur halt> andere.
Das Problem ist aber, daß Excel Zahlenreihen nicht gleich behandelt.
Wenn ich eine Reihe importiere, werden die meisten Werte richtig
übernommen, aber einige werden in Unsinn konvertiert.
Man kann also nicht einfach Datensätze mit Trennzeichen einlesen. Man
muß jede Spalte für sich markieren, das richtige Format aufzwingen und
dann jede Spalte einzeln importieren. Ein weiteres Problem sind
Datensätze mit Dezimalpunkt. Die muß man mit einem extra Texteditor in
Dezimalkomma konvertieren.
Oliver S. schrieb:> Peter D. schrieb:>> Die muß man mit einem extra Texteditor in Dezimalkomma konvertieren.> Auch das geht mit Excel.
Genau. Das hängt aber auch von der Dateiendung der Datei (.csv oder
.txt) und den Locale-Einstellungen des Users an. Man kann natürlich auch
die Einstellungen beim Import selbst noch verändern und die korrekte
Codepage einstellen...
Ich bin so froh, dass das .csv Format immer mehr durch .json ersetzt
wird, auch wenn da auch nicht alles perfekt ist.
Michael
DPA schrieb:> Warum sollte man aber auch unnötige 0en vor dezimale> Zahlen schreiben wollen?
Ähnlich beschränkt haben wohl auch diejenigen gedacht, die den Bockmist
verzapft haben, dass eine Zahl mit 0 am Anfang nicht dezimal,sondern
oktal zu interpretieren ist. Und damit auf eine klare Trennung von
Syntax und Inhalt verzichtet haben. 0 ist eine gültige Dezimalziffer und
damit als alleiniges Prefix völlig ungeeignet.
Es gibt sehr wohl gute Gründe für Dezimalzahlen, die mit 0 anfangen. Ein
Grund ist die feste Breite einer Zahlenfolge. Damit werden selbst Namen
mit Buchstaben und Ziffern richtig sortiert. ZB bei Dateinamen, die das
Datum enthalten, wie foo220106. Daraus kann man an fester Stelle etwa
den Monat zurücklesen. Bis zum Juli geht das auch mit Oktalzahlen, aber
im August knallt es dann.
Ein anderer Grund ist, dass man mehrstellige Zahlen besser suchen und
ersetzen kann als einzelne Ziffern. Eine "1" findet man recht häufig,
"001" ist da wesentlich zielgerichteter.
Jobst Q. schrieb:> Es gibt sehr wohl gute Gründe für Dezimalzahlen, die mit 0 anfangen.
Ja, dann sagt man der auswertenden Funktion schlicht, dass es sich um
eine Dezimalzahl handelt (base = 10), dann funktioniert das auch.
Wurde aber alles schon geschrieben.
Peter D. schrieb:> Man kann also nicht einfach Datensätze mit Trennzeichen einlesen. Man> muß jede Spalte für sich markieren, das richtige Format aufzwingen und> dann jede Spalte einzeln importieren.
Laß mal, andere Tabellenkalkulationen sind in diesem Punkt genau so
zickig wie Excel. Ich hatte da ähnliche Erfahrungen wie du sammeln
können. Insbesondere beim Importversuch von Daten aus Meßgeräten, was
mit boshafter Hartnäckigkeit an den verschiedensten Stellen schief ging.
Naja, es sind Tabellenkalkulationen eben zuvörderst dazu gedacht, daß
Nichtprogrammierer, also Kaufleute usw. manuell damit umgehen.
W.S.
Jörg W. schrieb:> Jobst Q. schrieb:>> Es gibt sehr wohl gute Gründe für Dezimalzahlen, die mit 0 anfangen.>> Ja, dann sagt man der auswertenden Funktion schlicht, dass es sich um> eine Dezimalzahl handelt (base = 10), dann funktioniert das auch.>> Wurde aber alles schon geschrieben.
Ich habe nirgendwo geschrieben, dass es keine Lösung für das Problem
gibt. Die Änderung von 0 auf 10 war Sache von Sekunden, schon vor eurer
"Hilfe". Aufwendig war nur, die Ursache für das Problem zu finden.
Um das anderen Programmierern zu ersparen, habe ich hier die Warnung
verfasst.
Das Blöde ist ja nur, dass Excel behauptet, mit CSV-Daten umgehen zu
können und sich den Suffix krallt, dann aber Mist draus macht.
Über den umständlichen Weg (neues Dokument anlegen, Text aus Datei
einfügen) kann man das alles parametrieren, warum dann nicht gleich,
wenn man initial eine CSV-Datei damit öffnet?
Aber das ist OT hier.
Rolf M. schrieb:> Jobst Q. schrieb:>> Für>> den Fall, viele Formate einlesen zu können, habe ich jetzt eine>> Rahmenfunktion geschrieben:>> Bei der du statt der in C üblichen Schreibweise zwei andere umsetzt, die> beide auch sonst nirgends gebräuchlich sind. Gerade auf sowas würde ich> verzichten, wenn ich nicht einen sehr guten Grund hätte, von gängigen> Schreibweisen abzusehen.
Die in C übliche Schreibweise von Oktalzahlen ist ja gerade das Problem.
Dass es zu Konflikten mit Dezimalzahlen kommt, die ja eindeutig Vorrang
haben sollten.
Binär-Konstanten fehlen leider in C. Wüsste aber kein Prefix, das
üblicher und sinnvoller wäre als '0b'.
Für Zahlen mit beliebiger Basis habe ich keine gängigen Schreibweisen
gefunden. Kennst du welche, die sinnvoller sind?
>> /*-----------------------------------*/>> long int strtolx (const char* s, char** endptr){>> Der Name ist übrigens ungültig. Alles, was mit str gefolgt von einem> Kleinbuchstaben beginnt, ist für den Compiler bwz. die> Standardbibliothek reserviert.
Darüber hat sich bei mir noch kein Compiler beschwert, solange ich nicht
dieselben Namen benutze. Ich könnte es natürlich auch xstrtol nennen,
sehe dazu aber keinen Anlass.
Jobst Q. schrieb:> Binär-Konstanten fehlen leider in C.
Werden in C23 endlich drin sein (nachdem praktisch alle anderen Sprachen
sie schon hatten, inklusive C++).
> Darüber hat sich bei mir noch kein Compiler beschwert, solange ich nicht> dieselben Namen benutze. Ich könnte es natürlich auch xstrtol nennen,> sehe dazu aber keinen Anlass.
"Absence of evidence is no evidence of absence".
Das ist eine Aussage wie: "Ich gehe immer bei Rot über die Straße, ist
noch nie was passiert, ich habe keinen Anlass, daran was zu ändern."
Der Compiler muss sich auch nicht drüber beschweren, er kann einfach
komplett was anderes machen dann, ganz und gar ohne sich zu beschweren.
Im Gegensatz zu der von dir proklamierten "Oktalzahlen-Falle" ist das
nämlich wirklich eine Falle, in die man unbedarft tapsen kann.
Jobst Q. schrieb:> Dass es zu Konflikten mit Dezimalzahlen kommt, die ja eindeutig Vorrang> haben sollten.
Warum sollten sie Vorrang haben?
Jobst Q. schrieb:> Für Zahlen mit beliebiger Basis habe ich keine gängigen Schreibweisen> gefunden. Kennst du welche, die sinnvoller sind?
Und wenn du denkst, dass dezimal Vorrang haben sollte, warum benutzt du
dann überhaupt eine andere Basis in einer Konfigdatei?
Jobst Q. schrieb:> Darüber hat sich bei mir noch kein Compiler beschwert, solange ich nicht> dieselben Namen benutze. Ich könnte es natürlich auch xstrtol nennen,> sehe dazu aber keinen Anlass
Warum sollte man sich auch an die Regeln der Sprache halten? Und wenn
der Compiler warnen würde, hättest du die Warnung vermutlich genauso
ignoriert wie die anderen Warnungen, die für dein strtolx generiert
werden.
Jobst Q. schrieb:> Dass es zu Konflikten mit Dezimalzahlen kommt, die ja eindeutig Vorrang> haben sollten.
Das Dezimalsystem finde ich echt scheisse. Bei den üblichen Basen 2 8 16
64 256 (btw. 1 byte), etc. kann man recht einfach dazwischen
konvertieren, und muss nicht die ganze zahl kennen. Ich kann einige
Ziffern am Anfang, Ende, Mitte, etc. nehmen, und davon ein paar Ziffern
in der anderen Basis ableiten. Aber mit base 10 geht das nicht. (Weil,
es gibt keine Zahl x für die gilt x=2^a=10^b wo a,b,x Integer und x>0
sind). Bei Basis 10 muss ich ganz rechts anfangen, und jede Ziffer
anschauen, um das in eine der anderen Basen konvertieren zu können.
Extrem unpraktisch. Das Dezimalsystem sollte man abschaffen.
Mombert H. schrieb:> Jobst Q. schrieb:>> Dass es zu Konflikten mit Dezimalzahlen kommt, die ja eindeutig Vorrang>> haben sollten.> Warum sollten sie Vorrang haben?
Weil Menschen zehn Finger haben.
c-hater schrieb:> Mombert H. schrieb:>>> Jobst Q. schrieb:>>> Dass es zu Konflikten mit Dezimalzahlen kommt, die ja eindeutig Vorrang>>> haben sollten.>> Warum sollten sie Vorrang haben?>> Weil Menschen zehn Finger haben.
Und zwei Hände, Arme, Beinde, Beine, Augen, Ohren, Nieren, ...
Jobst Q. schrieb:>> Bei der du statt der in C üblichen Schreibweise zwei andere umsetzt, die>> beide auch sonst nirgends gebräuchlich sind. Gerade auf sowas würde ich>> verzichten, wenn ich nicht einen sehr guten Grund hätte, von gängigen>> Schreibweisen abzusehen.>> Die in C übliche Schreibweise von Oktalzahlen ist ja gerade das Problem.> Dass es zu Konflikten mit Dezimalzahlen kommt, die ja eindeutig Vorrang> haben sollten.
Ich nehme lieber etwas etabliertes, statt was eigenes zu erfinden, das
sonst keiner kennt.
> Binär-Konstanten fehlen leider in C. Wüsste aber kein Prefix, das> üblicher und sinnvoller wäre als '0b'.
Das ist ok. Ist ja auch (außer bisher noch in C) durchaus schon so
gebräuchlich.
> Für Zahlen mit beliebiger Basis habe ich keine gängigen Schreibweisen> gefunden. Kennst du welche, die sinnvoller sind?
Hast du je eine andere Basis als 2, 8, 10 oder 16 gebraucht? Ich nicht.
Daher sehe ich keine Notwendigkeit, bei jeder Zahleneingabe die
Möglichkeit zu haben, eine eigene beliebige Basis angeben zu können.
Rolf M. schrieb:>> Die in C übliche Schreibweise von Oktalzahlen ist ja gerade das Problem.>> Dass es zu Konflikten mit Dezimalzahlen kommt, die ja eindeutig Vorrang>> haben sollten.>> Ich nehme lieber etwas etabliertes, statt was eigenes zu erfinden, das> sonst keiner kennt.
Ob jemand anderes das kennt, spielt für mich kaum eine Rolle. Die
meisten meiner Programme sind Teil eines Steuerungssystems, das keine
Bedienung erfordert und von mir konfiguriert und überwacht wird. Es sind
Dämone und Konsolenprogramme, die in Scripten und manuell eingesetzt
werden. Kriterien sind Zuverlässigkeit, Vielseitigkeit auch für
zukünftige Aufgaben und dass ich damit effizient arbeiten kann.
Gerade solche Erfahrungen wie jetzt mit strtol gehen in die Richtung:
"Traue keiner Funktion, die du nicht selbst geschrieben hast." Wie
etabliert sie auch immer sein mögen.
> Hast du je eine andere Basis als 2, 8, 10 oder 16 gebraucht? Ich nicht.> Daher sehe ich keine Notwendigkeit, bei jeder Zahleneingabe die> Möglichkeit zu haben, eine eigene beliebige Basis angeben zu können.
Ich werde sie auch nur selten einsetzen. Aber wenn strtol schon die
Möglichkeit bietet, Zahlen mit jeder Basis von 2 bis 36 einzulesen,
warum dann nicht auch ein fertiges Programm mit dieser Möglichkeit. Man
weiß ja nicht, welche Aufgaben es in der Zukunft geben wird.
Zahlen mit der Basis 4 oder 32 sind für Bitmuster ähnlich geeignet wie
Oktalzahlen oder Hexadezimalzahlen. Die Basis 36 erlaubt es zB alle
Grundbuchstaben als Ziffern einzusetzen und Worte sind nunmal leichter
zu merken als Zahlen. Für die Interprozeßkommunikation zb Messagequeue
braucht man jeweils einen 32-Bit Schlüssel zur Identifikation. Bisher
habe ich es mit 4 Buchstaben eines Prozesses als Bytes gelöst:
key_t MsgKey='w'+'a'*0x100+'t'*0x10000+'c'*0x1000000;
Mit der Zahlenbasis 36 könnte man bis zu 6 Buchstaben verwenden, oder
bei Programmen mit mehreren Prozessen 5 Buchstaben aus dem Programmnamen
und eine Ziffer als durchlaufende Nummerierung.
Peter D. schrieb:> C hält sich streng an einmal festgelegte Regeln, das ist doch schön.
Ja schön. Einmal Bockmist festgelegt und gilt für immer und ewig. Und
wird auch noch von neueren Sprachen übernommen.
C wird immer pingeliger, ständig gibt es neue Regeln und Vorschriften.
Aber der alte Mist bleibt.
Josef G. schrieb:> Jobst Q. schrieb:>> Die Basis 36 erlaubt es zB alle Grundbuchstaben als Ziffern>> Vielleicht reichen 32 Buchstaben? Wäre viel einfacher.
Zahlen mit Basis 36 haben 0-9 + 26 Buchstaben als gültige Ziffern. Mit
der Basis 32 müsste auf w,x,y und z verzichtet werden.
Jobst Q. schrieb:> C wird immer pingeliger, ständig gibt es neue Regeln und Vorschriften.
Tatsächlich? In den 33 Jahren seit seiner Standardisierung gab es genau
3 neue Versionen. Da kann man nicht gerade von "ständig" sprechen. Und
mir ist auch nicht bekannt, wo da was pingeliger geworden wäre.
Was du vielleicht meinst ist, dass neuere Compiler mehr Fehler im Code
finden bzw. darüber stolpern. Das liegt aber daran, dass diese Compiler
mehr Optimierungspotenziale ausschöpfen und den Code besser analysieren.
Die allermeisten solcher Fehler sind vorher lediglich unentdeckt
geblieben, waren aber eigentlich schon immer Fehler.
> Aber der alte Mist bleibt.
C ist sehr darauf bedacht, dass bestehender Code weitestgehend gültig
bleibt. Wenn man sich mal anschaut, wie lange praktisch jedes
Linux-System python2 und python3 parallel installiert haben musste, bis
mal endlich alle Skripte nach python3 migriert waren, kann man schon den
Vorteil in der Rückwärtskompatibilität sehen.
Jobst Q. schrieb:>> Die in C übliche Schreibweise von Oktalzahlen ist ja gerade das Problem.
Wo auch immer das herkommen mag, ändern kann man das jetzt nicht mehr.
Auch wenn heute wohl die meisten C-Programmierer in Ihren Programmen nur
eine einzige Oktalzahl verwenden: 0.
> Binär-Konstanten fehlen leider in C. Wüsste aber kein Prefix, das> üblicher und sinnvoller wäre als '0b'.
Binärkonstanten gibt es in C (ab ISO C23), und das Präfix ist 0b.
DPA schrieb:> Das Dezimalsystem sollte man abschaffen.
Du solltest dich am Strang "8bit-Computing mit FPGA" beteiligen.
Josef G. schrieb:> Ein kleiner Hobby-Computer mit einem Zeichensatz, der neben> Buchstaben und Sonderzeichen auch einen auf sechzehn Ziffern> erweiterten Ziffernsatz enthält ...
Jobst Q. schrieb:> Zahlen mit Basis 36 haben 0-9 + 26 Buchstaben als gültige Ziffern. Mit> der Basis 32 müsste auf w,x,y und z verzichtet werden.
Oder auf i, L, o und B
Dirk B. schrieb:> Oder auf i, L, o und B
Wenn es darum geht, lange Bitfolgen platzsparend zu drucken:
Alle 26 Buchstaben verwenden und dazu die Ziffern 1 bis 6.
So wären ungültige Zeichen leicht zu erkennen, finde ich.
Eine Basis außerhalb des Bereichs bis 36, die auch tatsächlich oft
Verwendung findet, ist 64 (entsprechend Base64 genannt). Die nutzt neben
allen Groß- und Kleinbuchstaben sowie den Ziffern noch das + und das /,
um damit pro Stelle 6 Bit darstellen zu können.
Philipp Klaus K. schrieb:> Jobst Q. schrieb:>>>> Die in C übliche Schreibweise von Oktalzahlen ist ja gerade das Problem.>> Wo auch immer das herkommen mag, ändern kann man das jetzt nicht mehr.
Bei Python 3 hat man das Präfix auch geändert von 0 auf 0o. Warum sollte
es bei C oder Java nicht möglich sein? Da gibt es ja auch immer wieder
neue Normen:
https://de.wikipedia.org/wiki/Varianten_der_Programmiersprache_C
Es wären 2 Schritte:
1. Das Prefix 0o alternativ ermöglichen. Bei alten Konstanten mit 0 eine
Warnung ausgeben,dass es bald abgeschafft wird. Libaryfunktionen wie
strtol oder scanf auf 0o umstellen und mit Warnungen darauf hinweisen,
dass sonst dezimal gelesen wird.
2. Konstanten mit 0 als Fehler behandeln, wenn es nicht mit einem
speziellen Flag compiliert wird.
> Auch wenn heute wohl die meisten C-Programmierer in Ihren Programmen nur> eine einzige Oktalzahl verwenden: 0.
Das wäre ein Grund nicht auf alten Fehlern zu bestehen.
>>> Binär-Konstanten fehlen leider in C. Wüsste aber kein Prefix, das>> üblicher und sinnvoller wäre als '0b'.>> Binärkonstanten gibt es in C (ab ISO C23), und das Präfix ist 0b.
Leider haben wir noch nicht 23, aber es macht Hoffnung.
Wenn du so ein großes Problem mit C hast, warum benutzt du es dann?
Zwingt dich jemand dazu? Wenn ja solltest du dich über diese Person
beschweren und nicht über C ;-)
Mombert H. schrieb:> Wenn du so ein großes Problem mit C hast, warum benutzt du es dann?> Zwingt dich jemand dazu? Wenn ja solltest du dich über diese Person> beschweren und nicht über C ;-)
Nein, ich liebe C und halte sie für eine geniale Sprache. Von der Null
als Oktalprefix mal abgesehen.
Die C-Standardfunktionen dagegen sind nicht immer optimal, deshalb
bevorzuge ich eigene Funktionen.
Finger weg von C ;-) schrieb:> Jobst Q. schrieb:>> 1. Das Prefix 0o alternativ ermöglichen.>> Oja,> int a = 0O0;> int b= 0O10;>> Einfach nur toll.
Warum hast du O statt o genommen? Damit es schlechter lesbar ist und du
dich dann darüber beschweren kannst, dass es schlecht lesbar ist?
Rolf M. schrieb:> Finger weg von C ;-) schrieb:>> Jobst Q. schrieb:>>> 1. Das Prefix 0o alternativ ermöglichen.>>>> Oja,>> int a = 0O0;>> int b= 0O10;>>>> Einfach nur toll.>> Warum hast du O statt o genommen? Damit es schlechter lesbar ist und du> dich dann darüber beschweren kannst, dass es schlecht lesbar ist?
Da 0x/0X und 0b/0B erlaubt sind. sollte wohl auch 0O erlaubt sein, wenn
0o erlaubt ist. Es ist ja nicht so als wäre 0B besonders gut lesbar ...
Jobst Q. schrieb:> Bei Python 3 hat man das Präfix auch geändert von 0 auf 0o.
Python 3 ist aber auch ein typisches Beispiel, wie man sich einen
Versionswechsel in einer Programmiersprache nicht wünscht, und wie ihn
C gewiss nicht machen würde.
Wenn, dann hätte man das vielleicht 1989 schon feststellen sollen, dann
könnten wir jetzt, mehr als 30 Jahre später, vielleicht die alten
Oktalzahlen endgültig verbieten. Auch aktuelle Systeme haben nämlich
durchaus noch welche in Headerfiles drin. Als Klassiker hätte ich hier
einen Auszug aus <sys/stat.h>:
1
#define S_ISUID 0004000 /* set user id on execution */
2
#define S_ISGID 0002000 /* set group id on execution */
3
#define S_ISTXT 0001000 /* sticky bit */
4
5
#define S_IRWXU 0000700 /* RWX mask for owner */
6
#define S_IRUSR 0000400 /* R for owner */
7
#define S_IWUSR 0000200 /* W for owner */
8
#define S_IXUSR 0000100 /* X for owner */
9
10
#define S_IRWXG 0000070 /* RWX mask for group */
11
#define S_IRGRP 0000040 /* R for group */
12
#define S_IWGRP 0000020 /* W for group */
13
#define S_IXGRP 0000010 /* X for group */
14
15
#define S_IRWXO 0000007 /* RWX mask for other */
16
#define S_IROTH 0000004 /* R for other */
17
#define S_IWOTH 0000002 /* W for other */
18
#define S_IXOTH 0000001 /* X for other */
19
20
#define S_IFMT 0170000 /* type of file mask */
21
#define S_IFIFO 0010000 /* named pipe (fifo) */
22
#define S_IFCHR 0020000 /* character special */
23
#define S_IFDIR 0040000 /* directory */
24
#define S_IFBLK 0060000 /* block special */
25
#define S_IFREG 0100000 /* regular */
26
#define S_IFLNK 0120000 /* symbolic link */
27
#define S_IFSOCK 0140000 /* socket */
28
#define S_ISVTX 0001000 /* save swapped text even after use */
Jobst Q. schrieb:> es bei C oder Java nicht möglich sein? Da gibt es ja auch immer wieder> neue Normen
Die Fortentwicklung des C-Standards ist extrem konservativ. Bestehender
Code wird wenn möglich nicht angetastet. Auch manche Compiler beherzigen
dies, weshalb GCC noch heute klassischen K&R Code mit Funktionen ohne
Parameterdeklarationen aus der Zeit vor ANSI-C 1989 akzeptiert.
>> Einfach nur toll.
Die Ästhetik spielt dabei nicht so eine Rolle.
Für einen völligen Neuanfang hätte ich 0k vorgeschlagen. Bei Hex-Zahlen
wurde ja auch das x genommen und nicht das h.
Aber da in Python und vielleicht in anderen Sprachen schon 0o festgelegt
wurde, bin ich mehr für Einheitlichkeit.
Wer Probleme mit der Unterscheidung von Ziffern und Buchstaben wie 0 und
O oder 1 und l hat, sollte sich einen besseren Font für seinen Editor
wählen.
Jobst Q. schrieb:> Für einen völligen Neuanfang hätte ich 0k vorgeschlagen
nah.
Prefix durch Unterstrich klar trennen, wie Python und Rust es
beherrschen:
MaWin schrieb:> Prefix durch Unterstrich klar trennen, wie Python und Rust es> beherrschen
Ist in C23 ein Apostroph (in C++ wohl auch). Unterstrich hat man nicht
genommen, weil es ein legaler Bezeichner ist, der auch an einigen
Stellen (vor allem im Bereich der Internationalisierung) schon üblich
ist.
Also
Jörg W. schrieb:> MaWin schrieb:>> Prefix durch Unterstrich klar trennen, wie Python und Rust es>> beherrschen>> Ist in C23 ein Apostroph (in C++ wohl auch).
Ja, C++ hat das (und den Präfix 0b für Binary) mit C++14 eingefügt. Der
Apostroph ist aber eher für die Gruppierung der Ziffern z.B. in
3er-Gruppen gedacht und darf nun gerade nicht direkt nach einem 0x oder
0b kommen. 0x'101 ist also nicht erlaubt, 0x1'01 schon.
> Unterstrich hat man nicht genommen, weil es ein legaler Bezeichner ist,> der auch an einigen Stellen (vor allem im Bereich der> Internationalisierung) schon üblich ist.
In C++ auch z.B. std::placeholders::_1 u.s.w.
Jörg W. schrieb:> MaWin schrieb:>> Prefix durch Unterstrich klar trennen, wie Python und Rust es>> beherrschen>> Ist in C23 ein Apostroph (in C++ wohl auch). Unterstrich hat man nicht> genommen, weil es ein legaler Bezeichner ist, der auch an einigen> Stellen (vor allem im Bereich der Internationalisierung) schon üblich> ist.>> Also> #define S_IXOTH 0'000'001 /* X for other */>> geht dann dort.
In C++ wäre der Unterstrich nich eindeutig.
1
intx=0x00_ff;
Das gleiche Problem hätte man, falls man Literale mit der oben erwähnte
36er Basis mit einem Prefix (hier 0q) einführen möchte.
1
intx=0q00s;
oder um es etwas deutlicher zu machen ein Fall der auch in C zu
Problemen führen würde
Rolf M. schrieb:> Ja, C++ hat das (und den Präfix 0b für Binary) mit C++14 eingefügt. Der> Apostroph ist aber eher für die Gruppierung der Ziffern z.B. in> 3er-Gruppen gedacht und darf nun gerade nicht direkt nach einem 0x oder> 0b kommen. 0x'101 ist also nicht erlaubt, 0x1'01 schon.
Das Detail war mir gerade nicht bewusst.
Wobei, 0'000177 geht dann ja trotzdem. ;-)
Dass das Gruppierungszeichen vor allem der Tausendergruppierung dient,
ist mir klar, andererseits hat man natürlich bei anderen Basen eh andere
Vorzüge (Hex-Zahlen dann bspw. eher aller vier Stellen), und meines
Wissens ist es gewissermaßen ein "NOP", es wird also keinerlei weitere
Semantik dran gebunden.
Rolf M. schrieb:> Ja, C++ hat das (und den Präfix 0b für Binary) mit C++14 eingefügt. Der> Apostroph ist aber eher für die Gruppierung der Ziffern z.B. in> 3er-Gruppen gedacht und darf nun gerade nicht direkt nach einem 0x oder> 0b kommen. 0x'101 ist also nicht erlaubt, 0x1'01 schon.
Gibt es für das nicht erlauben einen triftigen Grund? Warum sollte das
erste Zeichen anders behandelt werden als die übrigen?
Das ist eine ähnliche Inkonsistenz wie die Sonderbehandlung einer 0 am
Anfang einer Dezimalzahl.
Jörg W. schrieb:> MaWin schrieb:>> Prefix durch Unterstrich klar trennen, wie Python und Rust es>> beherrschen>> Ist in C23 ein Apostroph (in C++ wohl auch). Unterstrich hat man nicht> genommen, weil es ein legaler Bezeichner ist, der auch an einigen> Stellen (vor allem im Bereich der Internationalisierung) schon üblich> ist.
Meines Wissens nach war ein Grund nicht den Unterstrich zu nehmen, dass
der Unterstrich bei Hexadezimalzahlen nicht von einem C++ user-defined
literal suffix unterscheidbar wäre.
Philipp Klaus K. schrieb:> Meines Wissens nach war ein Grund nicht den Unterstrich zu nehmen, dass> der Unterstrich bei Hexadezimalzahlen nicht von einem C++ user-defined> literal suffix unterscheidbar wäre.
Ah OK. Hatte ich mir dann falsch gemerkt.
Ich fand den Apostroph erstmal etwas ungewöhnlich, aber der ist ja in
manchen Zahlenschreibweisen auch sonst wohl üblich.
Jobst Q. schrieb:> Rolf M. schrieb:>> Ja, C++ hat das (und den Präfix 0b für Binary) mit C++14 eingefügt. Der>> Apostroph ist aber eher für die Gruppierung der Ziffern z.B. in>> 3er-Gruppen gedacht und darf nun gerade nicht direkt nach einem 0x oder>> 0b kommen. 0x'101 ist also nicht erlaubt, 0x1'01 schon.>> Gibt es für das nicht erlauben einen triftigen Grund?
Ich denke, weil es eben zur logischen Gruppierung der eigentlichen
Ziffern dient und daher nur zwischen diesen gedacht ist. Für
Dezimalzahlen geht es im übrigen ja auch nicht, weil sonst nicht klar
wäre, ob '1' das Zeichen oder die Zahl sein soll.
> Warum sollte das erste Zeichen anders behandelt werden als die übrigen?
Das erste Zeichen wird nicht anders behandelt. Der Präfix für die Basis
wird anders behandelt.
> Das ist eine ähnliche Inkonsistenz wie die Sonderbehandlung einer 0 am> Anfang einer Dezimalzahl.
Eine 0 am Anfang einer Dezimalzahl gibt es in C nicht.
Jörg W. schrieb:> Wobei, 0'000177 geht dann ja trotzdem. ;-)
Ja, das geht. Vielleicht haben sie gedacht, dass 0x und 0b ja schon von
sich aus genug Trennung zwischen Präfix und dem eigentlichen Wert
bieten, während die 0 das nicht so direkt tut. Aber da kann ich nur
mutmaßen.
> Dass das Gruppierungszeichen vor allem der Tausendergruppierung dient,> ist mir klar, andererseits hat man natürlich bei anderen Basen eh andere> Vorzüge (Hex-Zahlen dann bspw. eher aller vier Stellen), und meines> Wissens ist es gewissermaßen ein "NOP", es wird also keinerlei weitere> Semantik dran gebunden.
Ja, richtig. Man muss es nicht zwingend alle 3 Ziffern machen. Es muss
zwischen zwei Hochkommas lediglich mindestens eine Ziffer stehen. Man
kann also z.B. auch Vierergruppen machen oder alle Ziffern einzeln durch
Hochkommas trennen, wenn man will.
Jobst Q. schrieb:> Rolf M. schrieb:>> Ja, C++ hat das (und den Präfix 0b für Binary) mit C++14 eingefügt. Der>> Apostroph ist aber eher für die Gruppierung der Ziffern z.B. in>> 3er-Gruppen gedacht und darf nun gerade nicht direkt nach einem 0x oder>> 0b kommen. 0x'101 ist also nicht erlaubt, 0x1'01 schon.>> Gibt es für das nicht erlauben einen triftigen Grund? Warum sollte das> erste Zeichen anders behandelt werden als die übrigen?
Einen triftigen Grund gibt es dafür nicht, aber es ist so konsistenter:
Ein Integer-Literal setzt sich zusammen aus einem optionalen Präfix (0x
oder 0b), einer Ziffernfolge und einem optionalen Suffix. Ein Apostroph
darf nicht am Anfang oder am Ende der Ziffernfolge stehen, weil das
Literal sonst bei nicht vorhandenem Präfix und Suffix mit einem
Character-Literal verwechselt werden könnte. Diese Regel gilt aus
Konsistenzgründen auch für Zahlen mit Präfix oder Suffix, auch wenn dort
keine Verwechslungsgefahr besteht. Da das Apostroph als Trennzeichen
zwischen Ziffern gedacht ist, ist das auch keine große Einschränkung.
Dabei ist zu beachten, dass die führende 0 einer Oktalzahl kein Präfix,
sondern Bestandteil der Ziffernfolge ist, denn wäre die 0 ein Präfix,
würde das Literal 0 nur aus dem Präfix bestehen, und die Ziffernfolge
wäre leer, also undefiniert. Um die Zahl Null auszudrücken, müsste man
deswegen 00 schreiben, was natürlich nicht erwünscht ist.
Weil aber 0 kein Präfix, sondern die erste Ziffer der Zahl ist, verstößt
ein Apostroph direkt nach dieser 0 (wie bspw. in 0'123) nicht gegen die
obige Regel.
> Das ist eine ähnliche Inkonsistenz wie die Sonderbehandlung einer 0 am> Anfang einer Dezimalzahl.
Die führende 0 zur Kennzeichnung von Oktalzahlen hat historische Gründe:
Ursprünglich kannte C (wie auch dessen Vorgänger B) keine Hexadezimal-,
sondern nur Dezimal- und Oktalzahlen. Das betraf sowohl Integer-Literale
als die Formatierung mit printf. Ein Integer-Literal war unabhängig von
der Basis einfach definiert als eine nichtleere Folge von Ziffern.
Damals waren deswegen auch in Oktalzahlen die Ziffern 8 und 9 explizit
erlaubt. Aus dem "C Reference Manual" von Ritchie:
"The digits 8 and 9 have octal value 10 and 11 respectively."
Syntaktisch gab es also erst einmal keinen Unterschied zwischen Dezimal-
und Oktalzahlen, was die lexikalische Analyse des Quellcodes
vereinfachte. Erst bei der Auswertung der Ziffernfolge zu einem
Zahlenwert kam die Basis ins Spiel, wobei an dieser Stelle das einzig
mögliche Unterscheidungsmerkmal das Vorhandensein einer ansonsten
redundanten führenden 0 war.
Wenn man die Vorgeschichte nicht kennt, mutet das aus heutiger Sicht
natürlich ziemlich schräg an :)