Name schrieb:> Anders gefragt. Warum erwartest du eine Warnung?
Ich vermute, es geht darum, dass 1 und damit auch 1 << i vom Typ int
ist. Man müsste eigentlich UINT32_C(1) << i schreiben. Bei einem
32-Bit-int führt 1 << 31 zu undefiniertem Verhalten. Beim AVR ist int
sogar nur 16 Bit breit.
> Bei einem 32-Bit-int führt 1 << 31 zu undefiniertem Verhalten.
Ist nicht sogar jeglicher left-shift eines signed Typen undefiniert?
Eine gcc-Warn-Option dafür ist mir aber nicht bekannt (wäre wohl noch
unpopulärer als -Wsign-conversion ;-) ).
foobar schrieb:>> Bei einem 32-Bit-int führt 1 << 31 zu undefiniertem Verhalten.>> Ist nicht sogar jeglicher left-shift eines signed Typen undefiniert?
Nein, nur wenn der zu schiebende Wert negativ ist oder das Ergebnis der
entsprechenden Multiplikation mit 2ᶦ vom Typ nicht repräsentiert werden
kann.
Jemand schrieb:> Name schrieb:>> Anders gefragt. Warum erwartest du eine Warnung?>> Ggf. ist int nur 16 Bit groß, dann wäre der Code so fehlerhaft.
Beim avr-gcc ist das so.
Oliver
Manfred schrieb:> Ja, int ist 16 Bit breit, daher ist der Code falsch. Da muss es> doch eine Warnung geben...
Da müsste der gcc aber schon sehr clever sein um hier das Problem zu
sehen. Kann man so etwas heutzutage erwarten?
Ist eigentlich Aufgabe des Programierers derartige Probleme zu erkennen.
D.h. wenn man mit Bits rummacht sollte man generell wissen was für
Datentypen man da benutzt.
test schrieb:> Kann man so etwas heutzutage erwarten?>> Ist eigentlich Aufgabe des Programierers derartige Probleme zu erkennen.
Darüber kann man gespaltener Meinung sein. Es gibt auch die Theorie
derzufolge es das Ziel sein sollte eine Sprache (oder in Ermangelung
einer neuen Sprache dann halt eben einen Compiler für eine existierende)
so zu entwerfen daß der Programmierer bestimmte (möglichst viele) Arten
von Fehlern schlichtweg einfach nicht mehr machen kann da man
mittlerweile gelernt hat daß menschliche Programmierer zwar unglaublich
clevere Algorithmen ersinnen können aber dennoch immer wieder so blöde
kleine Flüchtigkeitsfehler machen.
Das resoniert auch gut mit der Utopie daß eigentlich die Maschinen dem
Menschen dienen sollen und nicht umgekehrt.
Schaut euch mal die ersten Kommentare an. Ich hatte meine Frage bewusst
schwammig formuliert, um zu demonstrieren, wie leicht man hier den
Fehler übersehen kann.
Der Compiler ist schon recht schlau. Wenn ich ein Array einbaue, mit
Größe 31 und das in der Schleife befülle, kommt ja auch sofort eine
Warnung.
test schrieb:> Manfred schrieb:>> Ja, int ist 16 Bit breit, daher ist der Code falsch. Da muss es>> doch eine Warnung geben...>> Da müsste der gcc aber schon sehr clever sein um hier das Problem zu> sehen.
Der Compiler müsste etwas Code-Analyse betreiben. Wenn man die Zeile
1
uint32_tmask=1<<i;
für sich betrachtet, ist da erstmal nichts falsch dran. Eine 1 wird um
eine variable Anzahl von Bits geschoben. Solange man den Wert von i
nicht kennt, ist eine Warnung nicht gerechtfertigt. Jetzt ist es an der
Stelle aber nicht übermäßig schwer für den Compiler, zu wissen, mit
welchen Werten von i dieser Code definitiv ausgeführt werden wird. Der
Optimizer analysiert solche Schleifen sowieso, um z.B. loop-unrolling
betreiben zu können. Von daher könnte er hier schon merken, dass der
Code definitiv fehlerhaft ist.
> Kann man so etwas heutzutage erwarten?>> Ist eigentlich Aufgabe des Programierers derartige Probleme zu erkennen.
Heutzutage ist es aber Aufgabe des Compilers, nicht nur das Programm
einfach stur in Maschinencode zu übersetzen, wenn das irgendwie geht,
sondern auch, dem Programmierer zu helfen, Fehler zu vermeiden.
Er übernimmt heute auch die Rolle eines Lint-Tools.
> D.h. wenn man mit Bits rummacht sollte man generell wissen was für> Datentypen man da benutzt.
Das sollte man. Wie das aber so bei Menschen ist, macht man dabei auch
schnell mal einen Flüchtigkeitsfehler.
Rolf M. schrieb:> Das sollte man. Wie das aber so bei Menschen ist, macht man dabei auch> schnell mal einen Flüchtigkeitsfehler.
Wobei die Tatsache das ein uint32_t offenbar nur 16bit hat (oder auch
nicht, hängt bei c offenbar auch von der Mondphase und dem Wochentag ab)
nicht wirklich hilfreich dabei ist den Überblick zu behalten ;-)
Wobei ich da vermutlich auch unfair gegenüber c++ bin, vermutlich gibt
es geeignete Datentypen und Arten vernünftig zu programmieren. Macht nur
keiner ;-) Ich meine wenn ich sehe das heutzutage immer noch Bytes in
Char Arrays gesammelt werden...
test schrieb:> Rolf M. schrieb:>> Das sollte man. Wie das aber so bei Menschen ist, macht man dabei auch>> schnell mal einen Flüchtigkeitsfehler.>> Wobei die Tatsache das ein uint32_t offenbar nur 16bit hat (oder auch> nicht, hängt bei c offenbar auch von der Mondphase und dem Wochentag ab)> nicht wirklich hilfreich dabei ist den Überblick zu behalten ;-)
Ein uint32_t hat auf jeder Plattform 32-Bit. Ein int/unsigned int hat
mindestens 16-Bit.
test schrieb:> Wobei die Tatsache das ein uint32_t offenbar nur 16bit hat (oder auch> nicht, hängt bei c offenbar auch von der Mondphase und dem Wochentag ab)> nicht wirklich hilfreich dabei ist den Überblick zu behalten ;-)
Ich weiß nicht, wie du auf den Quatsch kommst. Das ist jedenfalls
falsch.
> Wobei ich da vermutlich auch unfair gegenüber c++ bin, vermutlich gibt> es geeignete Datentypen und Arten vernünftig zu programmieren. Macht nur> keiner ;-) Ich meine wenn ich sehe das heutzutage immer noch Bytes in> Char Arrays gesammelt werden...
Sollte unsigned char sein.
test schrieb:> Wobei ich da vermutlich auch unfair gegenüber c++ bin, vermutlich gibt> es geeignete Datentypen und Arten vernünftig zu programmieren. Macht nur> keiner ;-) Ich meine wenn ich sehe das heutzutage immer noch Bytes in> Char Arrays gesammelt werden...
Das machen nur C-Programmierer. In C++ nimmt man std::byte.
Bernd K. schrieb:> Geht das mit bare-metal targets auch (denn im OP ist von avr-gcc die> Rede)?
Theoretisch schon.
Ich weiß nicht, ob avr-libc da Unterstützung hat (libubsan).
Vermutlich aber eher nicht.
Noch ein Antwort, weil es mir gerade über die Füße lief:
der UB-sanititer ist in C++ programmiert und benutzt die libstdc++. Da
aber für das AVR target keine libstdc++ existiert, wird im configure des
avr-gcc die lubsan deaktiviert.
Genau genommen gibt's die Libs schon; sowohl libstdc++-v3 als auch
libsupc++, wobei letztere Teil der libstdc++-v3 ist und den rudimentären
C++ Support bereitstellt wie z.B. new und new[]. Müsste halt mal jemand
die handvoll Anpassungen für AVR machen. Vorgesehen ist das, müsste nur
noch ausgefüllt werden.
Die Sanitizer wollen aber noch mehr als libstdc++v3. Aus
libsanitizer/configure.ac:
1
# Common libraries that we need to link against for all sanitizer libs.
test schrieb:> vermutlich gibt es geeignete Datentypen und Arten vernünftig zu> programmieren. Macht nur keiner ;-)> Ich meine wenn ich sehe das heutzutage immer noch Bytes in> Char Arrays gesammelt werden...
Ja, ist leider immer noch sehr verbreitet.
Seit C99 gibt es die stdint.h und stdbool.h (daraus kommt der schon
erwähnte uint32_t). Damit ist es für einen Programmierer möglich seine
Algorithmen so zu schreiben, dass sie Chance haben einen
Plattformwechsel zu überleben und gibt auch den Tools mehr Chance für
hilfreiche Warnungen.
Wird aus meiner Sicht zu wenig geschult bzw. ist zu selten Bestandteil
der Firmen Coding-Guidelines.
test schrieb:> Da müsste der gcc aber schon sehr clever sein um hier das Problem zu> sehen. Kann man so etwas heutzutage erwarten?
Ja.
test schrieb:> Ist eigentlich Aufgabe des Programierers derartige Probleme zu erkennen.> D.h. wenn man mit Bits rummacht sollte man generell wissen was für> Datentypen man da benutzt.
Ja. Darum sollten auch anständig definierte Datentypen (wie z. B. aus
stdint.h) verwendet werden. So ist gleichzeitig auch der Code besser
dokumentiert ohne Mehraufwand.
Rolf M. schrieb:> Er übernimmt heute auch die Rolle eines Lint-Tools.
Ein heutiger Compiler soll heutzutage viele Prüfungen eines Linters
(Statisches Codeanalyse Werkzeug) übernehmen, so weit wie er das halt
kann. Zusätzlich kann der Compiler Dinge prüfen, die abhängig von der
Zielplattform sind, was nicht in den Bereich eines Linters passt.
Da dazu noch keine Namen genannt wurden, hier zwei OpenSource Linter für
C/C++:
http://splint.org/http://cppcheck.sourceforge.net/
Spielt mal damit herum, ist sehr spannend was die Tools so finden
können.
Hatte mir mal in einem Firmenprojekt (durfte ein Projekt von einem
Kollegen übernehmen) massiv Zeit eingespart.
Christoph Z. schrieb:> http://splint.org/
Splint Release 3.1.2
12 July 2007
Wer mit Compilern unterwegs ist oder sein muß, die auch aus dieser Zeit
stammen, für den mag sowas hilfreich sein.
Aktuelle Compiler können bzgl. statischer Codeanalyse inzwischen sehr
viel mehr.
Oliver
Wilhelm M. schrieb:> Das machen nur C-Programmierer. In C++ nimmt man std::byte.
Das is ein Ding. ;-O
Was ist denn jetzt besser für Geschwindigkeit, Sicherheit
und Größe?
Bit Funktionen als Makro / Inline zu schreiben oder
die Byte-Klasse zu nehmen?
mfg
Oliver S. schrieb:> Christoph Z. schrieb:>> http://splint.org/>> Splint Release 3.1.2> 12 July 2007>> Wer mit Compilern unterwegs ist oder sein muß, die auch aus dieser Zeit> stammen, für den mag sowas hilfreich sein.>> Aktuelle Compiler können bzgl. statischer Codeanalyse inzwischen sehr> viel mehr.
Ist zwar kein Compiler aber https://clang.llvm.org/extra/clang-tidy/
sollte auch nicht im eigenen Werkzeugkasten fehlen.
Matthias
Und verschiedene Compiler in möglichst aktueller Version. gcc und clang
finden viel und viel unterschiedliches.
Und da wir hier bei µC.net sind: Die "Geschäftslogik" einer Anwendung
sollte sich nicht nur für den µC übersetzen lassen. Denn nur so kann man
die Laufzeitchecker (asan, ubsan, valgrind) richtig einsetzen.
Matthias
Der TO wollte sicher keine Laufzeitlösung mit sanitizern, etc., sondern
eine Compilezeitlösung für sein Problem.
Abgesehen davon, dass es "richtiger" wäre zu schreiben
1
for(uint8_ti{};i<32;i++){
2
uint32_tmask=uint32_t{1}<<i;
3
}
liegt die Wurzel des Übels aber an anderer Stelle:
- eine Ganzzahl vom Type uint32_t wird als Bitmaske missbraucht
- der Datentyp für den Bit-Shift (uint8_t) steht in keiner Relation zu
dem Typ der Bitmaske
Eine simple Laufzeitlösung wäre korrekterweise folgendes:
1
for(uint8_ti{};i<33;i++){
2
assert(i<(sizeof(uint32_t)*8));
3
uint32_tmask=uint32_t{1}<<i;
4
}
Das schreibt zwar so keiner hin, wäre aber der korrekte Ansatz, denn der
gültige Wertebereich für den BitShift kann durch den Datentyp uint8_t
nicht eingehalten werden. Also trägt der Programmierer die Verantwortung
das zuzusichern: eine klassische Vorbedingung.
Für eine "richtige" Lösung braucht man
- einen (generischen) Datentyp (etwa: mask_t<>) für eine Bitmaske mit N
Bits
- einen Shiftoperator für mask_t<>,
- einen ganzzahligen Datentyp, der nur den Wertebereich des Shifts der
Maske abdeckt.
Damit hätten wir eine Lösung der Form:
1
usingm_t=mask_t<32>;
2
for(m_t::shift_typei;i;++i){
3
m_tmask=m_t{0x0001}<<i;
4
}
Die obigen Fehlerquellen sind ausgeschlossen.
Ist nicht schwer umzusetzen (std::byte ist da nur ein erster Schritt in
diese Richtung), genauso wie etwa bounded_integer (ein ganzzahliger
Datentyp mit beliebigen Grenzen und einstellbarer overrun-policy).
Das lässt sich mal wieder in das Thema strong types einordnen bzw. "Die
eingebauten Datentypen sind dazu da, nicht benutzt zu werden".
Wilhelm M. schrieb:> Das lässt sich mal wieder in das Thema strong types einordnen bzw. "Die> eingebauten Datentypen sind dazu da, nicht benutzt zu werden".
Genau so ist es.
Oliver S. schrieb:> Wilhelm M. schrieb:>> Hier aber wieder nicht:>> Wenn du das (an der Stelle) fürchterliche i{} durch ein übliches i=0> ersetzt, dann doch.
Ich finde es sehr praktisch und gar nicht fürchterlich.
Das ist uniform-initialization-syntax seit C++11 und cppcheck sollte das
so langsam mal mitbekommen haben.
Dies zeigt aber das grundsätzliche Problem bei der Verwendung externer
Werkzeuge.
Wilhelm M. schrieb:> Dies zeigt aber das grundsätzliche Problem bei der Verwendung externer> Werkzeuge.
Stimmt, deswegen schreibe ich inzwischen auch wieder
Wilhelm M. schrieb:> Oliver S. schrieb:>> Wilhelm M. schrieb:>>> Hier aber wieder nicht:>>>> Wenn du das (an der Stelle) fürchterliche i{} durch ein übliches i=0>> ersetzt, dann doch.>> Ich finde es sehr praktisch und gar nicht fürchterlich.
Ich finde, dass man den Wert, den man da in den Integer rein schreibt,
durchaus sehen sollte, und das nicht nur, wenn er von 0 verschieden ist.
Yalu X. schrieb:> Stimmt, deswegen schreibe ich inzwischen auch wieder> if (x < 0) ...
... würde ich jetzt bei std::is_same_v<decltype(x), std::string> == true
nicht machen.
Wilhelm M. schrieb:> Das ist uniform-initialization-syntax seit C++11 und cppcheck sollte das> so langsam mal mitbekommen haben.
Sorry, schon seit C++03.
Rolf M. schrieb:> Ich finde, dass man den Wert, den man da in den Integer rein schreibt,> durchaus sehen sollte, und das nicht nur, wenn er von 0 verschieden ist.
Nennt sich value-initialization und ist in der generischen
Programmierung sehr praktisch.
Aber selbst mit dem redundanten bzw. manchmal nicht möglichem
Initialwert (hier bspw: 0) findet cppcheck das Problem nicht.
Wilhelm M. schrieb:> Rolf M. schrieb:>> Ich finde, dass man den Wert, den man da in den Integer rein schreibt,>> durchaus sehen sollte, und das nicht nur, wenn er von 0 verschieden ist.>> Nennt sich value-initialization und ist in der generischen> Programmierung sehr praktisch.
Mir ist schon bewusst, dass das praktisch sein kann. An dieser Stelle
ist es aber fehl am Platz. Hier haben wir auch keine generische
Programmierung, sondern eine ganz simple Zählschleife, deren Zähler am
Anfang mit einem Wert vorbelegt wird. Und den Wert an dieser Stelle nur
deshalb zu verstecken, weil er zufälligerweise mit dem dem Default für
die Initialisierung übereinstimmt, finde ich nicht sinnvoll. Man muss
nicht jedes Feature überall nutzen, nur weil's halt geht.
> Aber selbst mit dem redundanten bzw. manchmal nicht möglichem> Initialwert (hier bspw: 0) findet cppcheck das Problem nicht.
Das ist ein anderes Problem.
Rolf M. schrieb:> Hier haben wir auch keine generische> Programmierung, sondern eine ganz simple Zählschleife, deren Zähler am> Anfang mit einem Wert vorbelegt wird.
Warum sollte man da einen Unterschied machen? Initialisierung immer mit
{} und man muss sich nicht umstellen.
Rolf M. schrieb:> weil er zufälligerweise mit dem dem Default für> die Initialisierung übereinstimmt,
Zufällig ist hier nichts.
Rolf M. schrieb:>> Aber selbst mit dem redundanten bzw. manchmal nicht möglichem>> Initialwert (hier bspw: 0) findet cppcheck das Problem nicht.>> Das ist ein anderes Problem.
Sicher. Bedeutet aber, dass mir cppcheck nichts bringt.
Wie schon oben erläutert, halte ich das Vorgehen des TO bzw. das
"übliche" Vorgehen für falsch.
Man kann es aber recht einfach lösen. Für eine Laufzeitlösung s.o. und
eine Compilezeit-Lösung ist auch einfach:
1
usingm_t=mask_t<32>;
2
3
m_tm{0b0000'00001};
4
m<<=33_c;// compiletime-error
5
6
while(m.any()){
7
m<<=1_c;// ok
8
}
BTW: std::bitmask löst zwar das Vokabularproblem, aber hat sonst die
übliche Semantik bzw. Möglichkeiten.
Wilhelm M. schrieb:> Nennt sich value-initialization und ist in der generischen> Programmierung sehr praktisch.
Da gehört das auch hin, in eine for-Schleife mit int aber nicht.
Allerdings ist das hier alles OT, für das Thema gibt ja einen eigenen
Thread (in dem schon alles gesagt wurde).
Oliver
Oliver S. schrieb:> Da gehört das auch hin, in eine for-Schleife mit int aber nicht.
Dein Argument ist: weil es immer so war, soll es so bleiben.
Mein Argument ist: Vereinheitlichung und damit Vereinfachung der
Notation.
Wilhelm M. schrieb:> Mein Argument ist: Vereinheitlichung und damit Vereinfachung der> Notation.
Meine Kritik (und wohl auch die anderer Diskussionsteilnehmer hier)
bezieht sich nicht primär auf die Verwendung der geschweiften Klammern¹,
sondern auf das Weglassen der 0.
Einheitlich wäre es deswegen, wenn man wie bei
1
for(uint8_ti{1};i<32;i++)
auch beim Start mit 0 den Startwert explizit hinschreibt, also so:
1
for(uint8_ti{0};i<32;i++)
und nicht so:
1
for(uint8_ti{};i<32;i++)
Ich bin ja prinzipiell auch ein Freund knapper Schreibweisen, aber nur
dann, wenn man sie auch halbwegs durchgängig verwenden kann. Warum
sollte man bei den 4294967296 möglichden Initialisierungswerten für ein
int in einem einzigen Fall (nämlich für die 0) eine andere Schreibweise
verwenden?
Bei der generischen Programmierung sieht das natürlich anders aus. Dort
stehen die leeren geschweiften Klammern i.Allg. nicht für die numerische
Null, sondern für das neutrale Element eines Monoiden. Da dieses Element
etwas ganz Besonderes ist, darf auch seine Initialisierung etwas anders
aussehen.
―――――――――――――
¹) Dennoch empfinde ich sie als häßlich, weil sie keinerlei Bezug nur
mathematischen Notation haben, wo eine Variablenbelegung immer und
unabhängig vom Typ mit einem Gleichheitszeichen erfolgt.
Yalu X. schrieb:> Wilhelm M. schrieb:>> Mein Argument ist: Vereinheitlichung und damit Vereinfachung der>> Notation.>> Meine Kritik (und wohl auch die anderer Diskussionsteilnehmer hier)> bezieht sich nicht primär auf die Verwendung der geschweiften Klammern¹,> sondern auf das Weglassen der 0.
Ja, zumindest meine auch. Daher passt diese Frage eigentlich genau dazu:
Wilhelm M. schrieb:> Warum sollte man da einen Unterschied machen?
Genau das ist eben auch mein Gedanke: Warum sollte man einen Unterschied
machen, ob die Schleife jetzt bei 0 oder z.B. bei 42 beginnt?
Yalu X. schrieb:> Einheitlich wäre es deswegen, wenn man wie bei> auch beim Start mit 0 den Startwert explizit hinschreibt, also so:
Meine Aussage bezieht sich nicht (nur) auf die Schreibweise, sondern
eben auf die Semantik:
1
template<typenameT>
2
voidbar(){
3
Tv;// default-initialization: non-class type -> indeterminate value
4
Tv1{};// value-initialization: non-class type -> zero-initialization
5
Tv2{0};// direct-initialization: non-class type -> no narrowing; class-type -> need ctor callable with one argument
6
Tv3=0;// copy-initialization: non-class-type -> narrowing; class-type -> need conversion-ctor
7
Tv4();// function declaration
8
while(!end(v)){
9
++v;
10
}
11
}
Im Beispiel oben sieht man eben den Unterschied: ggf. ist es für einen
Datentyp T gar nicht möglich. Die geringste implizite
template-Typanforderung ist die default-Konstruktion über die
value-initialization. Und bei primitiven DT resultiert
zero-initialization. Das ist ganz eindeutig und klar.
Yalu X. schrieb:> Dennoch empfinde ich sie als häßlich, weil sie keinerlei Bezug nur> mathematischen Notation haben, wo eine Variablenbelegung immer und> unabhängig vom Typ mit einem Gleichheitszeichen erfolgt.
Mathematische Variablen und benannte Objekte aka Variablen im Kontext
einer imperativen Programmiersprache sind zwei unterschiedliche Dinge.
Wilhelm M. schrieb:> Im Beispiel oben sieht man eben den Unterschied: ggf. ist es für einen> Datentyp T gar nicht möglich.
Das ist aber genau der Punkt.
Wilhelm M. schrieb:> for(uint8_t i{}; i < 33; i++) {
Da gibt es kein T und keine semantisch erforderliche
default-Initialisierung.
Da steht ein POD uint8_t, und dessen Semantik ist "Startwert der
Schleifenvariablen". Da nicht die 0 hinzuschreiben, geht am Ziel vorbei.
Oliver
Wilhelm M. schrieb:> T v1{}; // value-initialization: non-class type -> zero-initialization> T v2{0}; // direct-initialization: non-class type -> no narrowing;> class-type -> need ctor callable with
Und was spricht jetzt genau dagegen, für die Laufvariable mit dem festen
Typ uint8_t die direct-initialization statt der value-initialization zu
verwenden?
Das kostet zwar einen Tastendruck mehr, dafür hätten aber alle
Zählschleifen unabhängig von ihrem Startwert ein einheitliches Aussehen,
und Vereinheitlichung scheint ja auch dir wichtig zu sein:
Wilhelm M. schrieb:> Mein Argument ist: Vereinheitlichung und damit Vereinfachung der> Notation.Wilhelm M. schrieb:> Im Beispiel oben sieht man eben den Unterschied: ggf. ist es für einen> Datentyp T gar nicht möglich. Die geringste implizite> template-Typanforderung ist die default-Konstruktion über die> value-initialization.
Dass die value-initialization in der generischen Programmierung sinnvoll
sein kann, habe ich ja oben bereits geschrieben. Im konkreten Beispiel
mit der Schleife ist aber nichts generisch, sondern wir haben es mit
einem klar festgelegten Typ (uint8_t) zu tun.
Wilhelm M. schrieb:> Yalu X. schrieb:>> Dennoch empfinde ich sie als häßlich, weil sie keinerlei Bezug nur>> mathematischen Notation haben, wo eine Variablenbelegung immer und>> unabhängig vom Typ mit einem Gleichheitszeichen erfolgt.>> Mathematische Variablen und benannte Objekte aka Variablen im Kontext> einer imperativen Programmiersprache sind zwei unterschiedliche Dinge.
Das ist klar. Trotzdem finde ich es praktisch, wenn sich die Syntax
einer Programmiersprache an bereits bekannte Schreibweisen anlehnt, die
bspw. von
- der natürlichen Sprache (wie bspw. bei "if", "while" und "return"),
- der Mathematik (wie bspw. "+", "-", "sin") oder
- anderen bereits verbreiteten Programmiersprachen
übernommen werden können.
In den allermeisten Programmiersprachen (insbesondere auch den
imperativen) wird nun mal mit einem Gleichheitszeichen initialisiert.
Nur C++ entfernt sich immer mehr von dieser Konvention.
Yalu X. schrieb:> Wilhelm M. schrieb:>> Im Beispiel oben sieht man eben den Unterschied: ggf. ist es für einen>> Datentyp T gar nicht möglich. Die geringste implizite>> template-Typanforderung ist die default-Konstruktion über die>> value-initialization.>> Dass die value-initialization in der generischen Programmierung sinnvoll> sein kann, habe ich ja oben bereits geschrieben. Im konkreten Beispiel> mit der Schleife ist aber nichts generisch, sondern wir haben es mit> einem klar festgelegten Typ (uint8_t) zu tun.
Wobei das hier nicht mal eine Rolle spielt. Selbst wenn es generisch
wäre: Wenn ich eine Zählschleife habe und die explizit bei 0 starten
lassen will, schreibe ich 0 als Startwert hin, egal von welchem Typ mein
Zähler ist. Und ich schreibe den Wert hin, egal ob er 0 oder 1 oder
11149 ist.
Die Semantik ist eben "ich möchte bei 0 anfangen zu zählen" und nicht
"ich möchte bei dem default-Wert des Datentyps anfangen".