The Go Blog — Constants — Deutsche Übersetzung
- Das Original:
-
https://blog.golang.org/constants
August 25, 2014 - Diese Übersetzung:
-
http://www.bitloeffel.de/DOC/golang/constants_de.html
Stand: 15.1.2016
© 2016 Hans-Werner Heinzen @ Bitloeffel.de
Die Nutzung dieses Werks ist unter den Bedingungen der "Creative Commons Attribution 3.0"-Lizenz erlaubt.
Go Blog: Konstanten
25 August 2014
Einführung
Go ist eine Programmiersprache mit statischer Typprüfung. Sie erlaubt es nicht,
in Operationen numerische Typen zu vermischen. Man kann kein float64
zu einem int
addieren, noch nicht mal ein int32
zu einem int
. Trotzdem ist es möglich, 1e6*time.Second
zu schreiben oder math.Exp(1)
oder sogar 1<<('\t'+2.0)
.
Anders als Variablen verhalten sich die Konstanten in Go ziemlich genau wie ganz
normale Zahlen. Dieser Aufsatz erklärt, warum das so ist, und was daraus folgt.
Im Hintergrund: C
Damals, als wir anfingen über Go nachzudenken, diskutierten wir eine Reihe von Problemen, die durch die Art und Weise verursacht werden, wie C und seine Abkömmlinge es erlauben, numerische Typen zu vermischen und zu vergleichen. Eine Menge mysteriöser Fehler, Abstürze sowie Portabilitätsprobleme werden durch Ausdrücke verursacht, die Ganzzahlen verschiedener Größe und Vorzeicheneigenschaft kombinieren. Auch wenn für einen eingefleischten C-Programmierer das Ergebnis einer Berechnung wie
unsigned int u = 1e9; long signed int i = -1; ... i + u ...
vertraut sein mag, so ist es doch nicht a priori offensichtlich. Welche Größe hat das Ergebnis? Wie lautet sein Wert? Ist er vorzeichenbehaftet oder vorzeichenfrei?
Hier lauern ekelhafte Fehler.
C hat eine Reihe von Regeln, die als "die üblichen arithmetischen Umwandlungen" bekannt sind, und es ist ein Zeichen ihrer Raffinesse, dass sie sich über die Jahre geändert haben — wodurch nachträglich neue Bugs eingeführt wurden.
Beim Entwerfen von Go entschieden wir uns, dieses Minenfeld zu meiden: wir verfügten,
das es kein Mischen von numerischen Typen geben würde.
Wenn Sie i
zu u
addieren wollen, müssen Sie explizit
sagen, welcher Art das Ergebnis sein soll. Wenn
var u uint var i int
gegeben sind, dann können Sie entweder uint(i)+u
oder i+int(u)
schreiben,
so dass sowohl Bedeutung als auch Art der Addition klar vorgegeben sind; anders als
in C dürfen Sie nicht i+u
schreiben.
Sie dürfen noch nicht mal int
und int32
kombinieren,
selbst dann nicht, wenn int
ein 32-Bit-Typ ist.
Diese Strenge beseitigt eine weit verbreitete Ursache von Fehlern und Störfällen. Sie ist eine wesentliche Eigenheit von Go. Doch sie bringt Kosten mit sich: hin und wieder wird der Programmierer gezwungen, seinen Kode mit plumpen numerischen Konvertierungen zu verzieren.
Und was ist mit den Konstanten?
Mit den obigen Deklarationen: Was würde es erlauben,
i = 0
oder u = 0
zu schreiben?
Was ist der Typ von 0
?
Es wäre unsinnig, für eine Konstante Typkonversion zu verlangen, wenn es um einen so
einfachen Ausdruck wie i = int(0)
geht.
Wir erkannten bald, dass unsere numerischen Konstanten anders arbeiten mussten als
die in den C-artigen Sprachen.
Viel Nachdenken und Experimentieren war nötig, bis wir ein Konzept hatten, von dem
wir glauben, dass es sich fast immer richtig anfühlt, dass es Programmierer vom
Konvertieren von Konstanten befreit, und dass es trotzdem möglich ist,
math.Sqrt(2)
zu schreiben, ohne dass der Compiler meckert.
Kurz gesagt, Konstanten in Go tun, was sie sollen, einfach so, jedenfalls meistens. Schauen wir uns jetzt an, wie das funktioniert.
Terminologie
Als Erstes eine kurze Definition: In Go ist const
ein Schlüsselwort,
das einen Namen für einen skalaren Wert wie 2
oder 3.14159
oder "sagenhaft"
einführt.
Solche Werte, mit oder ohne Namen, nennt man in Go Konstanten.
Sie können auch durch Ausdrücke wie 2+3
oder 2+3i
oder math.Pi/2
oder ("go"+"pher")
erzeugt werden.
Es gibt Sprachen ohne Konstanten. Andere Sprachen wiederum haben eine allgemeinere Definition
von "Konstante" oder verwenden das Schlüsselwort const
allgemeiner.
Beispielsweise ist const
in C und C++ ein Typkennzeichner, der
komplizierte Eigenschaften noch komplizierterer Werte kodifizieren kann.
In Go aber ist eine Konstante nur ein einfacher, unveränderlicher Wert, und ab jetzt werden wir nur noch über Go reden.
String-Konstanten
Es gibt viele Arten von numerischen Konstanten: Ganzzahlen, Gleitkommazahlen, Runen, welche mit Vorzeichen, welche ohne, imaginäre und komplexe. Fangen wir deshalb mit etwas einfacherem an. String-Konstanten sind leicht zu verstehen und grenzen das Gebiet ein, das wir für das Thema "Konstanten in Go" erkunden müssen.
Eine String-Konstante schließt etwas Text in Anführungsstrichen ""
ein.
(Es gibt in Go auch rohe String-Literale, die durch Gravis-Zeichen ``
eingeschlossen werden, doch im Rahmen unserer Diskussion haben die auch keine
anderen Eigenschaften.)
Hier nun eine String-Konstante:
"Hello, 世界"
(Mehr über Repräsentation und Interpretation von Strings finden Sie in diesem Blog-Beitrag.)
Welchen Typ hat nun diese String-Konstante? Die naheliegende Antwort lautet
string
, aber das ist falsch.
Sie ist eine typfreie String-Konstante, was heißen soll: sie ist ein
konstanter textueller Wert, der noch keinen fixierten Typ hat. Ja natürlich,
sie ist ein String, aber kein Go-Wert vom Typ string
.
Und sie bleibt eine typfreie String-Konstante, auch wenn wir ihr einen Namen geben:
const hello = "Hello, 世界"
Nach dieser Deklaration ist hello
also eine typfreie String-Konstante.
Eine typfreie Konstante ist nur ein Wert, und zwar ohne einen definierten Typ, der
ihn zur Einhaltung der strengen Regeln zwingen würde, die das Kombinieren verschieden
typisierter Werte verhindern.
Es ist dieses Konzept einer typfreien Konstanten, die uns Konstanten in Go unbeschwert benutzen lässt.
Und was ist nun eine typbehaftete String-Konstante? Das ist eine, der ein Typ zugewiesen wurde, wie hier:
const typedHello string = "Hello, 世界"
Beachten Sie den expliziten Typ string
vor dem Gleichheitszeichen.
Das bedeutet, dass typedHello
vom Go-Typ string
ist,
und keiner Go-Variablen eines anderen Typs zugewiesen werden kann.
Also funktioniert dieser Kode:
var s string s = typedHello fmt.Println(s)
aber der hier nicht:
type MyString string var m MyString m = typedHello // Typfehler fmt.Println(m)
Die Variable m
ist vom Typ MyString
; ihr kann also kein
Wert eines anderen Typs zugewiesen werden. Man kann ihr nur Werte vom Typ
MyString
zuweisen, also etwa so:
const myStringHello MyString = "Hello, 世界" m = myStringHello // OK fmt.Println(m)
oder indem man eine Typkonversion erzwingt, also so:
m = MyString(typedHello) fmt.Println(m)
Kommen wir zurück zu unserer typfreien String-Konstanten. Sie hat die nützliche Eigenschaft, dass man sie einer typbehafteten Variablen zuweisen kann, ohne einen Fehler auszulösen. Also können wir schreiben:
m = "Hello, 世界"
oder:
m = hello
weil die typfreien Konstanten "Hello, 世界"
und hello
,
anders als die typbehafteten Konstanten typedHello
und
myStringHello
, keinen Typ haben.
Die Zuweisung zu einer Variablen beliebigen Typs, solange der nur mit Strings
kompatibel ist, funktioniert fehlerfrei.
Diese typfreien String-Konstanten sind natürlich Strings, können also nur
benutzt werden, wo Strings erlaubt sind, aber sie haben nicht den Typ
string
.
Standardtypen
Als Go-Programmiererin sind Ihnen Deklarationen, wie die folgende, vertraut:
str := "Hello, 世界"
und inzwischen werden Sie sich fragen, "Wenn die Konstante typfrei ist, wie kommt
str
dann hier zu ihrem Typ?" Die Antwort lautet,
dass typfreie Konstanten jeweils einen Standardtyp haben, einen impliziten Typ,
den sie auf den Wert transferieren, wenn kein anderer Typ bereit steht.
Für typfreie String-Konstanten ist der Standardtyp offensichtlich string
,
so dass
str := "Hello, 世界"
oder
var str = "Hello, 世界"
dasselbe bedeuten wie
var str string = "Hello, 世界"
Man kann sich das so vorstellen: Typfreie Konstanten leben in einer Art idealem
Werteraum, der weniger restriktiv ist als Go's komplettes Typsystem.
Nur, um überhaupt etwas mit ihnen anfangen zu können, müssen wir sie Variablen
zuweisen, und in diesem Augenblick braucht die Variable (nicht die Konstante)
einen Typ, und die Konstante kann der Variablen mitteilen, welcher Typ das sein
könnte. In unserem Beispiel wird str
ein Wert vom Typ
string
, weil die typfreie Konstante der Deklaration seinen
Standardtyp string
mitgibt.
In so einer Deklaration wird eine Variable mit Typ und Initialwert deklariert. Aber manchmal, wenn wir eine Konstante benutzen, ist ihre Bestimmung nicht so klar. Nehmen wir zum Beispiel diese Anweisung:
fmt.Printf("%s", "Hello, 世界")
Die Signatur von fmt.Printf
ist
func Printf(format string, a ...interface{}) (n int, err error)
und das bedeutet, dass seine Argumente (also die nach dem Format-String)
Interface-Werte sind.
Was passiert also, wenn fmt.Printf
mit einer typfreien Konstanten
gerufen wird und dabei ein Interface-Wert als Argument erzeugt wird, wobei der
konkrete Typ, weicher für das Argument gespeichert wird, der Standardtyp der
typfreien String-Konstanten wird.
Das Ergebnis sehen Sie an diesem Beispiel, welches das Format %v
benutzt,
um den Wert zu drucken, und %T
für den Typ des an fmt.Printf
übergebenen Werts.
[Das Ausführen des Beispielkodes funktioniert leider nur im
Originaldokument. A.d.Ü.]
fmt.Printf("%T: %v\n", "Hello, 世界", "Hello, 世界") fmt.Printf("%T: %v\n", hello, hello)
Und wenn die Konstante typbehaftet ist, dann wandert dieser Typ ins Interface, wie das folgende Beispiel zeigt:
fmt.Printf("%T: %v\n", myStringHello, myStringHello)
(Mehr Informationen dazu, wie Interfaces funktionieren, gibt's im ersten Abschnitt dieses Blog-Beitrags)
Zusammengefasst: Eine typbehaftete Konstante gehorcht allen Regeln typbehafteter Werte in Go. Eine typfreie Konstante hingegen transportiert einen Go-Typ nicht in dieser Weise; sie kann freier vermischt und verglichen werden. Allerdings hat sie einen Standardtyp, der dann und nur dann sichtbar wird, wenn keine andere Typinformation zur Verfügung steht.
Durch Syntax festgelegter Standardtyp
Der Standardtyp einer typfreien Konstanten ist durch ihre Syntax festgelegt.
Für String-Konstanten ist der einzig mögliche implizite Typ der string
.
Für numerische Konstanten
[de] gibt
es da eine größere Auswahl.
Standardtyp für ganzzahlige Konstanten ist int
, für Gleitkommakonstanten
float64
, für Runenkonstanten rune
(ein Alias für
int32
)
und für Imaginäkonstanten complex128
.
Im folgenden wird die schon bekannte Printf-Anweisung mehrfach benutzt, um die
Standardtypen in Aktion zu zeigen:
fmt.Printf("%T %v\n", 0, 0) fmt.Printf("%T %v\n", 0.0, 0.0) fmt.Printf("%T %v\n", 'x', 'x') fmt.Printf("%T %v\n", 0i, 0i)
(Übung: Erklären Sie das Ergebnis für 'x'
.)
Bool'sches
Alles, was wir über typfreie String-Konstanten gesagt haben, kann auch über typfreie
Bool'sche Konstanten gesagt werden.
Die Werte true
und false
sind typfreie Bool'sche Konstanten,
die jeder Bool'schen Variablen zugewiesen werden können;
doch einmal mit Typ versehen, kann Bool'sches nicht mehr vermischt werden.
type MyBool bool const True = true const TypedTrue bool = true var mb MyBool mb = true // OK mb = True // OK mb = TypedTrue // Schlecht fmt.Println(mb)
Starten Sie das Beispiel, um zu sehen, was passiert. Dann kommentieren Sie die "schlechte" Zeile aus und starten neu. Hier wie bei den String-Konstanten ergibt sich dasselbe Verhaltensmuster.
Gleitkommazahlen
Gleitkommakonstanten sind größtenteils wie die Bool'schen. Unser übliches Beispiel, übersetzt für Gleitkommazahlen, funktioniert wie erwartet:
type MyFloat64 float64 const Zero = 0.0 const TypedZero float64 = 0.0 var mf MyFloat64 mf = 0.0 // OK mf = Zero // OK mf = TypedZero // Schlecht fmt.Println(mf)
Dumm nur, dass es in Go zwei Gleitkommatypen gibt: float32
und float64
. Der Standardtyp für Gleitkommakonstanten ist
float64
, ungeachtet dessen, dass eine typfreie Gleitkommakonstante
ohne weiteres auch einem float32
-Wert zugewiesen werden kann:
var f32 float32 f32 = 0.0 f32 = Zero // OK: Zero ist typfrei f32 = TypedZero // Schlecht: TypedZero ist float64 nicht float32 fmt.Println(f32)
Die Gleitkommawerte eignen sich gut dazu, hier das Konzept des Überlaufs bzw. des Wertebereichs einzuführen.
Numerische Konstanten leben in einem numerischen Universum beliebiger Genauigkeit; sie sind ganz normale Zahlen. Nur wenn sie einer Variablen zugewiesen werden, müssen sie in ihren Zielort hineinpassen. Wir könne Konstanten mit sehr großen Werten deklarieren:
const Huge = 1e1000
— das ist schließlich nur eine Zahl — aber wir können sie weder zuweisen noch drucken. Die folgende Anweisung lässt sich noch nicht mal kompilieren:
fmt.Println(Huge)
Die Fehlermeldung lautet "constant 1.00000e+1000 overflows float64", was ja auch
stimmt. Huge
kann trotzdem von Nutzen sein: Wir können es in
Ausdrücken mit anderen Konstanten benutzen und dann die Ergebnisse der Ausdrücke verwenden,
solange sie im Wertebereich eines float64
liegen. Die Anweisung
fmt.Println(Huge / 1e999)
druckt 10
, was man auch erwarten würde.
Damit verbunden ist, dass Gleitkommakonstanten sehr genau sein können, so dass
Berechnungen mit ihnen korrekter sind.
Konstanten im Paket math haben sehr
viel mehr Ziffern als ein float64
fassen könnte.
Hier die Definition von math.Pi
:
Pi = 3.14159265358979323846264338327950288419716939937510582097494459
Sobald dieser Wert einer Variablen zugewiesen wird, geht ein Teil der Genauigkeit
verloren; die Zuweisung ergibt einen float64
- oder
float32
-Wert möglichst nahe am Hochpräzisionswert.
Dieses Programmschnipsel
pi := math.Pi fmt.Println(pi)
druckt 3.141592653589793
.
Dass so viele Ziffern zur Verfügung stehen, bedeutet, dass eine Berechnung wie
Pi/2
oder kompliziertere Auswertungen mit größerer Präzision
durchgeführt werden können, bevor das Ergebnis zugewiesen wird. Dadurch sind
Berechnungen mit Konstanten leichter zuschreiben, ohne dass dabei Genauigkeit
verloren ginge. Es bedeutet außerdem, dass Grenzfälle wie unendlich,
arithmetischer Unterlauf oder NaN
in konstanten Ausdrücken nicht
auftreten können.
(Division mit einer Konstanten Null ist ein Umwandlungsfehler; und wenn alles
eine Zahl ist, dann gibt es einfach kein "not a number".)
Komplexe Zahlen
Komplexkonstanten verhalten sich ziemlich genau wie Gleitkommakonstanten. Hier eine Version der bekannten Litanei, übersetzt für Komplexzahlen:
type MyComplex128 complex128 const I = (0.0 + 1.0i) const TypedI complex128 = (0.0 + 1.0i) var mc MyComplex128 mc = (0.0 + 1.0i) // OK mc = I // OK mc = TypedI // Schlecht fmt.Println(mc)
Der Standardtyp für Komplexzahlen ist complex128
, also die genauere
der beiden Version; sie ist aus zwei float64
-Werten zusammengesetzt.
Nur wegen der Klarheit haben wir den vollständigen Ausdruck (0.0+1.0i)
hingeschrieben; diesen Wert kann aber man kürzen zu 0.0+1.0i
oder
1.0i
oder sogar 1i
.
Versuchen wir mal einen Trick. Wir wissen, dass in Go eine numerische Konstante einfach eine Zahl ist. Was nun, wenn diese Zahl eine Komplexzahl ohne imaginären Anteil ist, d.h. wenn sie eine Reale Zahl ist? Hier ist so eine:
const Two = 2.0 + 0i
Das ist eine typfreie Komplexkonstante.
Auch wenn sie keinen Imaginäranteil hat, definiert die Syntax des Ausdrucks,
dass sie den Standardtyp complex128
hat.
Deshalb wird, wenn wir sie zum Deklarieren einer Variablen hernehmen, diese vom Typ
complex128
sein. Das Programmschnipsel
s := Two fmt.Printf("%T: %v\n", s, s)
druckt complex128: (2+0i)
.
Die Zahl kann jedoch auch als skalare Gleitkommazahl in einem
float64
- oder float32
-Wert ohne Informationsverlust
gespeichert werden.
Also können wir Two
problemlos einer float64
-Variablen
zuweisen, beim Initialisieren und woanders:
var f float64 var g float64 = Two f = Two fmt.Println(f, "und", g)
Die Ausgabe lautet 2 und 2
.
Obwohl Two
eine Komplexkonstante ist, kann sie einer skalaren
Gleitkommavariablen zugewiesen werden.
Diese Fähigkeit von Konstanten, den Typ zu "wechseln", wird sich noch als nützlich
erweisen.
Ganzzahlen
Zu guter Letzt kommen wir zu den Ganzzahlen. hier gibt es mehr bewegliche Teile,
vielerlei Größen, mit Komma,
ohne Komma, und so weiter
[de],
doch sie spielen alle nach denselben Regeln.
Ein letztes Mal das bekannte Beispiel, nur dieses Mal mit int
:
type MyInt int const Three = 3 const TypedThree int = 3 var mi MyInt mi = 3 // OK mi = Three // OK mi = TypedThree // Schlecht fmt.Println(mi)
Das gleiche Beispiel könnten wir für jeden der Ganzzahltyp bilden, welche da wären:
int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr
(Dazu kommen noch die Aliase byte
für uint8
und
rune
für int32
.)
Das ist eine ganze Menge. Das Muster, nach dem Konstanten arbeiten, sollte aber
inzwischen so vertraut sein, dass Sie schon sehen können, worauf es hinausläuft.
Wie schon erwähnt kommen Ganzzahlen in einer Reihe von Formen vor, und jede Form hat ihren
eigenen Standardtyp: int
für einfache Konstanten wie 123
oder 0xFF
oder -14
, und rune
für Zeichen in
Anführungsstrichen wie 'a', '世' or '\r'.
Keine Konstantenform hat einen vorzeichenlosen Integer-Typ als Standardtyp. Doch
so flexibel unsere typfreien Konstanten sind, können wir Ganzzahlvariablen ohne
Vorzeichen mit einfachen Konstanten initialisieren, solange wir den Typ klarstellen.
Das ist analog zum float64
, das wir mit einer Komplexzahl mit
Imaginäranteil Null initialisiert hatten.
Es folgen verschiedene Möglichkeiten, ein uint
zu initialisieren;
sie sind alle gleichwertig, aber alle müssen den für das Ergebnis gewünschten Typ
explizit erwähnen.
var u uint = 17 var u = uint(17) u := uint(17)
Wie beim Problem des Wertebereichs bei Gleitkommas passen auch nicht alle
Ganzzahlwerte in alle Ganzzahltypen.
Zwei Probleme können auftreten: der Wert könnte zu groß sein, oder es könnte ein
negativer Wert einem Ganzzahltyp ohne Vorzeichen zugewiesen werden.
Beispielsweise hat int8
den Wertebereich -128 bis 127, also darf man eine
Konstanten außerhalb dieses Bereichs einer Variablen vom Typ int8
nicht
zuweisen:
var i8 int8 = 128 // Fehler: zu groß
Ähnlich hat uint8
, auch bekannt als byte
, den Wertebereich
0 bis 255, so dass ein großer oder ein negativer Wert nicht zugewiesen werden darf:
var u8 uint8 = -1 // Fehler: negativer Wert
Mit dieser Typprüfung erwischt man auch den folgenden Fehler:
type Char byte var c Char = "世" // Fehler: "世" hat den Wert 0x4e16, zu groß
Wenn der Compiler über Ihre Art, Konstanten zu verwenden, meckert, dann handelt es sich wahrscheinlich um einen echten Programmierfehler.
Übung: Die größte Ganzzahl ohne Vorzeichen
Dies ist eine kleine aber lehrreiche Übung.
Was schreiben wir, damit eine Konstante den größten Wert erhält, der in ein
uint
passt?
Würden wir von uint32
anstatt von uint
reden, dann könnten
wir das so schreiben:
const MaxUint32 = 1<<32 - 1
aber wir wollen ja uint
, nicht uint32
.
Die Typen int
und uint
haben dieselbe, aber unbestimmte,
Anzahl von Bits, entweder 32 oder 64. Weil das von der Prozessorarchitektur abhängt
können wir nicht einfach eine Zahl hinschreiben.
Ganzzahlen in Go benutzen die Zweierkomplement-Arithmetik, und Fans wissen, dass die Darstellung von
-1
alle Bits auf 1 hat; also ist die interne Darstellung dieselbe, wie
für die größte Ganzzahl ohne Vorzeichen. Wir könnten auf die Idee kommen
const MaxUint uint = -1 // Fehler: negativer Wert
zu schreiben, aber das ist verboten, weil -1
nicht in eine Variable ohne
Vorzeichen hineinpasst; -1
liegt nicht im Wertebereich von Werten ohne
Vorzeichen. Konvertieren hilft genausowenig — aus demselben Grund:
const MaxUint uint = uint(-1) // Fehler: negativer Wert
Auch wenn zur Laufzeit ein Wert von -1 in eine Ganzzahl ohne Vorzeichen konvertiert werden kann, so verbieten doch die Regeln [de] für Konstanten eine solche Zwangsmaßnahme. Was wir damit meinen, ist, das das hier funktioniert:
var u uint var v = -1 u = uint(v)
aber nur, weil v
eine Variable ist; wenn wir aber aus v
eine
Konstante machen, sind wir zurück auf verbotenem Gelände, selbst wenn sie typfrei ist:
var u uint const v = -1 u = uint(v) // Fehler: negativer Wert
Probieren wir's nochmal mit dem vorhergehenden Ansatz. Nur, dass wir es diesmal
anstatt mit -1
mit ^0
versuchen, also mit der bitweise
Negation einer beliebigen Anzahl von Null-Bits.
Aber das scheitert auch, aus einem ähnlichen Grund: Im Bereich numerischer Werte
steht ^0
für eine unendliche Anzahl von Einsen; wir würden also
Präzision verlieren, wiesen wir sie einer Ganzzahl mit fester Länge zu:
const MaxUint uint = ^0 // Fehler: Überlauf
Wie schaffen wir es also, die größte Ganzzahl ohne Vorzeichen zu einer Konstanten zu machen?
Entscheidend ist, die Negation auf die Anzahl der Bits eines uint
einzugrenzen, und nicht-darstellbare Werte, wie negative Zahlen zu vermeiden.
Der einfachste uint
-Wert ist die typbehaftete Konstante
uint(0)
. Uints haben 32 oder 64 Bits, also hat uint(0)
entsprechend ebenfalls 32 oder 64 Null-Bits. Wenn wir diese alle invertieren, erhalten
wir die korrekte Anzahl an Einser-Bits, und das ist der größte uint
-Wert.
Wir drehen also nicht die Bits der typfreien Konstanten 0
um, sondern
die der typbehafteten Konstanten uint(0)
. Hier ist unsere Konstante:
const MaxUint = ^uint(0) fmt.Printf("%x\n", MaxUint)
Für jede Anzahl von Bits, die ein uint
in der jeweils aktuellen
Laufumgebung hat (auf dem "Playground"
sind es 32) steht diese Konstante korrekt für den größten Wert, die eine Variablen
vom Typ uint
enthalten kann.
Wenn Sie dieser Analyse bis zum Ergebnis folgen konnten, dann verstehen Sie alles, was über Konstanten in Go zu verstehen wichtig ist.
Zahlen
Das Konzept typfreier Konstanten in Go bedeutet, dass alle numerischen Konstanten, ob jetzt Ganzzahlen, Gleitkomma- oder komplexe Zahlen, oder sogar Zeichenwerte, in einer Art einheitlichem Raum leben. Erst, wenn wir sie in die Welt der Variablen, in die Welt der Zuweisungen und Berechnungen bringen, spielen Typen eine Rolle. Solange wir aber in der Welt der numerischen Konstanten bleiben, können wir Werte vermischen und vergleichen, g'rad wie es uns gefällt.. Die folgenden Konstanten haben alle den numerischen Wert 1:
1 1.000 1e3-99.0*10-9 '\x01' '\u0001' 'b' - 'a' 1.0+3i-3.0i
Deshalb, und obwohl sie verschiedene implizite Standardtypen haben: als typfreie Konstanten können sie Variablen eines jeden Ganztahltyps zugewiesen werden:
var f float32 = 1 var i int = 1.000 var u uint32 = 1e3 - 99.0*10.0 - 9 var c float64 = '\x01' var p uintptr = '\u0001' var r complex64 = 'b' - 'a' var b byte = 1.0 + 3i - 3.0i fmt.Println(f, i, u, c, p, r, b)
Die Ausgabe davon ist: 1 1 1 1 1 (1+0i) 1
.
Man kann auch verrückte Sachen machen:
var f = 'a' * 1.5 fmt.Println(f)
was dann 145.5 ergibt — ziemlich sinnlos, aber es geht!
Worum es hier im Kern geht, ist Flexibilität.
Das bedeutet, dass es trotz dem Verbot, in einem Go-Ausdruck Gleitkomma- und
Ganzzahlvariablen (oder nur int
and int32
) zu mischen,
in Ordnung ist, zu schreiben
sqrt2 := math.Sqrt(2)
oder
const millisecond = time.Second/1e3
oder
bigBufferWithHeader := make([]byte, 512+1e6)
und man erhält das erwartete Ergebnis.
Weil numerische Konstanten in Go tun, was sie tun sollen. Sie sind wie Zahlen.
[Original] von Rob Pike