Forum: PC-Programmierung Oktalzahlfalle strtol


von Jobst Q. (joquis)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

Es wird da nicht „selten“, sondern genau einmal erwähnt, unübersehbar an 
der dazu passenden Stelle. Mehr braucht’s nicht.

https://man7.org/linux/man-pages/man3/strtol.3.html
https://en.cppreference.com/w/c/string/byte/strtol

Oliver

von Jochen (Gast)


Lesenswert?

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.

von Thomas (Gast)


Lesenswert?

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.

von Jochen (Gast)


Lesenswert?

> 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".

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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
long int strtolx (const char* s, char** endptr){
3
4
int b =10;
5
char c;
6
7
if(endptr != NULL)*endptr=s;
8
9
if(*s=='0'){
10
  c=*(s+1)|32;
11
  if (c =='x')b=16;
12
  else if (c =='o'){b=8;s+=2;}
13
  else if (c =='b'){b=2;s+=2;}
14
  }
15
16
else if (*s=='_'){
17
  b=strtol(++s,&s,10);
18
  if((*s != '_') || b<2 || b>36)return 0;
19
  s++;
20
  }
21
22
return strtol(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.

: Bearbeitet durch User
von Jochen (Gast)


Lesenswert?

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...

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Wenn ich so Sprüche wie
> Man kann also nur davor warnen,
lese habe ich eigentlich schon die Nase voll. Statt die Doku zu lesen 
Schwurblerniveau.

von DPA (Gast)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Nop (Gast)


Lesenswert?

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:

von Mombert H. (mh_mh)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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. :)

von Mombert H. (mh_mh)


Lesenswert?

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")

von Rolf M. (rmagnus)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Ich bin schon Software begegnet, die IP Adressen mit strtol(,0) 
konvertiert. Wer dann seine Adresse als 192.168.178.010 eingibt...

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
> Das MG heißt ja eigentlich 08/15.

In hiesiger Branche also nicht von 08/14 unterscheidbar, weil beides 0.

von Peter D. (peda)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

Es sei noch erwähnt, dass sich der Formatspecifier %i bei scanf genauso 
verhält.

von A. S. (Gast)


Lesenswert?

(prx) A. K. schrieb:
> In hiesiger Branche also nicht von 08/14 unterscheidbar, weil beides 0.

08 ist schon keine gültige Zahl.

von Mombert H. (mh_mh)


Lesenswert?

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 ;-)

von Rolf M. (rmagnus)


Lesenswert?

(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

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

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!!!

von c-hater (Gast)


Lesenswert?

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.

von Mombert H. (mh_mh)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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. :)

von (prx) A. K. (prx)


Lesenswert?

Jörg W. schrieb:
> mit `0o' eingeleitet werden

Ich hätte ja 0O empfohlen. ;-)

von Michael D. (nospam2000)


Lesenswert?

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

von Daniel A. (daniel-a)


Lesenswert?

Es wäre praktisch, wenn man Sachen wie 16xABCDEF 8x0123457 64xdEf= 
machen könnte. also basis x zahl.

von (prx) A. K. (prx)


Lesenswert?

Daniel A. schrieb:
> Es wäre praktisch, wenn man Sachen wie 16xABCDEF 8x0123457 64xdEf=
> machen könnte. also basis x zahl.

Ada: 16#ABCDEF#.

von Rolf M. (rmagnus)


Lesenswert?

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

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

Peter D. schrieb:
> Die muß man mit einem extra Texteditor in
> Dezimalkomma konvertieren.

Auch das geht mit Excel.

Oliver

von Michael D. (nospam2000)


Lesenswert?

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

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Mombert H. (mh_mh)


Lesenswert?

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.

von DPA (Gast)


Lesenswert?

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.

von Josef G. (Gast)


Lesenswert?

Ich war's nicht.

von c-hater (Gast)


Lesenswert?

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.

von Mombert H. (mh_mh)


Lesenswert?

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, ...

von Oliver S. (oliverso)


Lesenswert?

DPA schrieb:
> Extrem unpraktisch. Das Dezimalsystem sollte man abschaffen.

Ist dein Vorname Sheldon?

Oliver

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

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.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

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.

von Josef G. (Gast)


Lesenswert?

Jobst Q. schrieb:
> Die Basis 36 erlaubt es zB alle Grundbuchstaben als Ziffern

Vielleicht reichen 32 Buchstaben? Wäre viel einfacher.

von Jobst Q. (joquis)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Philipp Klaus K. (pkk)


Lesenswert?

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.

von Josef G. (Gast)


Lesenswert?

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 ...

von Dirk B. (dirkb2)


Lesenswert?

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

von Josef G. (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

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.

von Mombert H. (mh_mh)


Lesenswert?

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 ;-)

von Jobst Q. (joquis)


Lesenswert?

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.

von Finger weg von C ;-) (Gast)


Lesenswert?

Jobst Q. schrieb:
> 1. Das Prefix 0o alternativ ermöglichen.

Oja,
1
int a = 0O0;
2
int b= 0O10;

Einfach nur toll.

von Rolf M. (rmagnus)


Lesenswert?

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?

von Mombert H. (mh_mh)


Lesenswert?

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 ...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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 */

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

Finger weg von C ;-) schrieb:
> Jobst Q. schrieb:
>> 1. Das Prefix 0o alternativ ermöglichen.
>
> Oja,
>
>
1
> int a = 0O0;
2
> int b= 0O10;
3
>
>
> 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.

von Mombert H. (mh_mh)


Lesenswert?

Jobst Q. schrieb:
> Für einen völligen Neuanfang hätte ich 0k vorgeschlagen. Bei Hex-Zahlen
> wurde ja auch das x genommen und nicht das h.

Warum k?

von MaWin (Gast)


Lesenswert?

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:
1
>>> 0o_101
2
65
3
>>> 0x_101
4
257
5
>>> 0b_101
6
5
1
fn main() {
2
    let _x = 0o_101;
3
    let _x = 0x_101;
4
    let _x = 0b_101;
5
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
1
#define S_IXOTH 0'000'001                 /* X for other */

geht dann dort.

von Rolf M. (rmagnus)


Lesenswert?

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.

Beitrag #6937706 wurde vom Autor gelöscht.
von Mombert H. (mh_mh)


Lesenswert?

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
int x = 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
int x = 0q00s;
oder um es etwas deutlicher zu machen ein Fall der auch in C zu 
Problemen führen würde
1
int x = 0q00l;

von Unterstrich (Gast)


Lesenswert?

MaWin schrieb:
>>>> 0o_101
> 265
> 3>>> 0x_101
> 4257
> 5>>> 0b_101
> 65

Schön dass es so einheitlich ist ;-)
1
>>> bin(5)
2
'0b101'
3
>>> hex(255)
4
'0xff'
5
>>> print(oct(8))
6
0o10

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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.

von Philipp Klaus K. (pkk)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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 :)

: Bearbeitet durch Moderator
von MaWin O. (mawin_original)


Lesenswert?

Yalu X. schrieb:
> "The digits 8 and 9 have octal value 10 and 11 respectively."

herrlich :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.