Wie kann ich die Verwendung von Konstanten erzwingen?
Mein Beispielcode:
float a,b;
const float Winkel = 45.0;
a = b * sin(Winkel / 360 2 PI);
Worum es mir geht, dass der uC zur Laufzeit nicht jedes mal den Sinus
von 45° ausrechnen muss, sondern dass der Compiler einfach eine
Konstante 0.707106781 dafür einsetzt. Klar, ich kann das während der
Quellcodeeingabe einfach selber mit dem Taschenrechner ausrechnen, will
das aber nicht! Sinus ist auch nur ein Beispiel, genau so gut könnte da
auch log oder atn stehen.
Ben S. schrieb:> Das macht der Compiler bereits für dich, ohne dass du es weißt - sofern> du Optimierung eingeschaltet hast.
Für den gcc ist das korrekt. Fragt sich nur, ob der TO überhaupt gcc
verwendet oder gar die Frage ganz allgemein für alle üblichen C-Compiler
stellt. Er verrät ja auch nicht den µC.
Code:
Frank M. schrieb:> Ohne die Optimierung wird demnach die Funktion sin() aus libm benötigt.
Ganz herzlichen Dank. So konnte das endlich einmal geklärt werden. :-)
Nowhereman schrieb:> Klar, ich kann das während der Quellcodeeingabe einfach selber mit dem> Taschenrechner ausrechnen, will das aber nicht!
Der Mittelweg wäre doch, Winkel als #define zu nehmen. Oder C++.
Echte Konstanten gibt es in C halt nicht, bzw. nur mit Compiler-options
oder als #defines.
Hallo,
ich verstehe das Problem nicht. Wenn 'const Winkel' in main() berechnet
wird, dann wird 'Winkel' automatisch in while(1) zur Laufzeit überall
verwendet.
Etwas anderes wäre es wenn man im Laufzeitcode überall die
Berechnungsfunktion verwendet/aufruft statt der schon berechneten const
Variablen. Dann kommt es auf den Compiler an wie er optimieren kann.
Veit D. schrieb:> ich verstehe das Problem nicht. Wenn 'const Winkel' in main() berechnet> wird, dann wird 'Winkel' automatisch in while(1) zur Laufzeit überall> verwendet.
Winkel.ist.konstant. 45.0
Es geht um das a, das zur Laufzeit berechnet.wird (in C, nicht C++),
statt vom Compiler.
Nur für mich als stiller Mitleser: Hat die Initialisierung von const
float mit einem double-Literal keinen Overhead oder sonstige Nachteile?
(Ich hätte eine Compiler-Warnung erwartet.)
Nein.
Bei der Initialisierung mit einer Konstanten bzw. einem Literal
konvertiert der Compiler bereits und initalisiert nur mit einer float.
(Bei einer Zuweisung zur Laufzeit aus einer double-Variablen müsste
konvertiert werden, ist hier aber nicht der Fall.)
Hallo,
gut, dann war ich zum lesen schon zu müde. Dann beziehe ich mich nicht
auf 'Winkel' sondern auf 'a'. Meine Aussage bleibt die Gleiche. Wenn a
als Konstante initialisiert wird, dann kann und wird diese zur Laufzeit
verwendet. Denn das ist kein Funktionsaufruf der an anderer Stelle
wiederholt werden könnte sondern eine Initialisierung.
Veit D. schrieb:> Meine Aussage bleibt die Gleiche. Wenn a als Konstante initialisiert> wird, dann kann und wird diese zur Laufzeit verwendet
Natürlich. Sie wird aber auch zur Laufzeit initialisiert, mit echter
Berechnung des sin. Wenigstens im Default.
Du verwechselt anscheinend häufiger C und C++.
Hier steht die Sprache zwar nicht explizit, es wurde aber mehrfach
darauf hingewiesen, da C++ das Problem nicht hat.
Das einzige mir bekannte Konstrukt, was dir das garantiert (abgesehen
davon, einfach ein Literal hinzuschreiben), ist consteval aus C++.
https://en.cppreference.com/w/cpp/language/consteval
In anderen Faellen musst du dich auf Compileroptimierungen verlassen.
Sven B. schrieb:> Das einzige mir bekannte Konstrukt, was dir das garantiert (abgesehen> davon, einfach ein Literal hinzuschreiben), ist consteval aus C++.
Mit consteval deklariert man immediate-functions: diese müssen zur
Compilezeit ausgewertet werden.
Mit constexpr deklariert man Funktionen, die in einem constexpr-Kontext
ausgewertet werden können, wenn es möglich ist, d.h. ihr Argumente
ebenfalls constexpr sind.
Constexpr-"Variablen" sind Variablen, deren Initialisierung zur
Compilezeit bestimmt wird, entweder durch constexpr (falls möglich) oder
consteval Funktionen. Das Verwenden einer solchen "Variablen" ist als
eine constant-expression. Übeall dort, wo sie eingesetzt wird, für das
zur einer constant-expression (wie ein Literal) und der Compiler wird
dasselbe damit machen wie mit einem Literal. Das führt im Endeffekt
dazu, dass die constexpr-"Variablen" keine Variablen (benannte Objekte)
mehr sind und deswegen sich nicht materialisieren.
Wilhelm M. schrieb:> Das führt im Endeffekt dazu, dass die constexpr-"Variablen" keine> Variablen (benannte Objekte) mehr sind und deswegen sich nicht> materialisieren.
Das gilt doch prinzipiell auch für const Variablen, die zur kompilezeit
bzw. Linkzeit bekannt sind, also wie beim TO. Nur das es halt keinen
Fehler gibt, wenn das nicht der Fall ist.
A. S. schrieb:> Wilhelm M. schrieb:>> Das führt im Endeffekt dazu, dass die constexpr-"Variablen" keine>> Variablen (benannte Objekte) mehr sind und deswegen sich nicht>> materialisieren.>> Das gilt doch prinzipiell auch für const Variablen, die zur kompilezeit> bzw. Linkzeit bekannt sind, also wie beim TO. Nur das es halt keinen> Fehler gibt, wenn das nicht der Fall ist.
Ja.
constexpr bedeutet const, und const (bei global bzw. namespace) bedeutet
internal-linkage. Solange man nicht die Adresse bestimmt, wird nicht
materialisiert.
extern const kann man zwar schreiben und ist dann eine Deklaration. Die
wieder kann sich nur auch eine vorher definiertes const-Objekt beziehen
(e.g. im Header).
Mit mehrfachen const-Objekte bekommt man also in unterschiedlichen TU
unterschiedliche Adresse für const.
constexpr ist implizit inline. Hier bekommt man dieselbe Adresse. Das
kann man mit const auch machen, dann als inline const etwa in Headern.
Ja, und wie schon gesagt, constexpr Objekte erzwingen ein Compile-Zeit
Initialisierung, const nicht.
Um jetzt etwas pingelig zu werden:
In C und C++ heißt const genau genommen nicht, daß ein Objekt konstant
ist.
Sondern nur, daß man es im Geltungsbereich des const nicht ändert.
Beispiel:
const volatile int i = 0;
heißt: hier wird i nicht mehr geändert, aber in einem anderen Thread
oder einem Signalhandler, Interrupt o.ä. kann es geändert werden. Dort
muß es dann ohne const deklariert werden natürlich.
Wilhelm M. schrieb:> constexpr ist implizit inline. Hier bekommt man dieselbe Adresse. Das> kann man mit const auch machen, dann als inline const etwa in Headern.
Das stimmt nicht. Constexpr Variablen sind nicht implizit inline.
Wilhelm M. schrieb:> const == read-only
Für C++ heißt const auch konstant. Zumindest bei const int x=3;
Und wie bitte wird in C bei
Klaus W. schrieb:> Beispiel:> const volatile int i = 0;> heißt: hier wird i nicht mehr geändert, aber in einem anderen Thread> oder einem Signalhandler, Interrupt o.ä. kann es geändert werden.
i ins RAM gelegt wird?
Wilhelm M. schrieb:> A. S. schrieb:>> Für C++ heißt const auch konstant.>> Nein. Und das Beispiel mit const volatile hast Du ja selbst geliefert.>> Oder>
1
>intx;
2
>constint&rx=x;
3
>
>> Hier ist ganz klar ersichtlich, dass const hier read-only bedeutet.
Wilhelm, Du hast Klaus Behauptung
Klaus W. schrieb:> C++ heißt const genau genommen nicht, daß ein Objekt konstant> ist.
bestätigt mit
Wilhelm M. schrieb:> const == read-only
Natürlich gibt es Fälle, wie Dein Beispiel, wo es nur Read-Only
bedeutet.
Hier im Forum verwechseln Leute häufig den Fundamentalen Unterschied
eines Objektes mit const in C und C++: In C nur Read-Only, in C++ jedoch
konstant.
Dass bei einem Zeiger oder einer Referenz etwas anderes gilt, bleibt da
unbenommen. Ein Zeiger auf ein Objekt ist kein Objekt.
A. S. schrieb:> Dass bei einem Zeiger oder einer Referenz etwas anderes gilt, bleibt da> unbenommen. Ein Zeiger auf ein Objekt ist kein Objekt.
Aber das, worauf der Zeiger zeigt, ist ein Objekt.
Und auf dasselbe Objekt kann auch in C++ ein Zeiger zeigen, der es als
const sieht, und ein anderer, über den es geändert werden kann.
A. S. schrieb:> Klaus W. schrieb:>> Beispiel:>> const volatile int i = 0;>> heißt: hier wird i nicht mehr geändert, aber in einem anderen Thread>> oder einem Signalhandler, Interrupt o.ä. kann es geändert werden.>> i ins RAM gelegt wird?
Auch in C++ kann ich von einer const-Variablen eine Adresse bilden.
Spätestens dann wird der Compiler die auch im RAM halten.
A. S. schrieb:> In C nur Read-Only, in C++ jedoch> konstant.
Das musst Du mal erklären.
A. S. schrieb:> Ein Zeiger auf ein Objekt ist kein Objekt.
Eine Variable ist (im engeren Sinn) ein benanntes Objekt - auch eine
Zeigervariable.
Ein Objekt ist ein Speicherbereich, in dem ein Wert abgelegt ist.
Ein Wert ist eine Bitfolge, die entsprechend einem Datentyp
interpretiert wird.
Ein Datentyp beschreibt die möglichen Operationen und die möglichen,
(beobachtbaren) Werte.
Wilhelm M. schrieb:> A. S. schrieb:>>> In C nur Read-Only, in C++ jedoch>> konstant.>> Das musst Du mal erklären
const int x = 2;
In C++ kann ich das x als Konstante verwenden, z.b. int a[x];
X ist konstant, eine Konstante.
A. S. schrieb:> In C++ kann ich das x als Konstante verwenden, z.b. int a[x];
Und die meinst, das geht in C nicht?
A. S. schrieb:> X ist konstant, eine Konstante.
Nö:
[c]
*const_cast<int*>(&x) = 42;
[c]
Wilhelm M. schrieb:> Nö:> [c]> *const_cast<int*>(&x) = 42;> [c]
Wobei das durch den Compiler geht, aber nicht zwangsläufig zur Laufzeit
auch funktioniert (unabhängig davon ob C oder C++).
Es könnte nämlich sein, daß es x in einem Speicher abgelegt wird, der
gar nicht beschreibbar ist zur Laufzeit.
Wilhelm M. schrieb:> Und die meinst, das geht in C nicht?
nein.
Wilhelm M. schrieb:> *const_cast<int*>(&x) = 42;
Ändert nichts mehr daran, dass der Compiler 2 für x eingetragen hat.
Sag mal, was soll das. Du kennst C++ deutlich besser als ich und das
sind doch wirklich Anfängersachen.
A. S. schrieb:> Sag mal, was soll das. Du kennst C++ deutlich besser als ich und das> sind doch wirklich Anfängersachen.
Ja, wohl geistige Umnachtung mit Traum im UB-Land.
Danke für den Weckruf!
Vielleicht zur allgemeinen Erbauung ein kleines Programm?
Es wird eine const i vereinbart, mit 12 initialisiert, und ein Zeiger
darauf. Der ist jetzt nicht ein Zeiger auf const i, sondern auf const
(was man natürlich nicht machen sollte, aber der Compiler verwehrt es
nicht.
Man hat also im RAM nur eine int-Variable.
Die wird zweimal ausgegeben, erst als i selbst und dann über den Zeiger.
Über den Zeiger wird sie dann zu 42 geändert und wieder auf beide Arten
ausgegeben.
1
#include<iostream>
2
3
voidzeigeInt(constchar*kommentar,constinti)
4
{
5
std::cout<<kommentar<<i<<std::endl;
6
}
7
8
intmain(intnargs,char**args)
9
{
10
constinti=12;
11
12
int*non_const_ptr_i=const_cast<int*>(&i);// böse, aber nicht verboten
klaus@lap7:~$ g++ -Wall -std=c++14 a.cpp -o a && ./a
2
vorher i direkt 12
3
vorher ueber Zeiger 12
4
nachher i direkt 12
5
nachher ueber Zeiger 42
6
klaus@lap7:~$ g++ -Wall -std=c++17 a.cpp -o a && ./a
7
vorher i direkt 12
8
vorher ueber Zeiger 12
9
nachher i direkt 12
10
nachher ueber Zeiger 42
11
klaus@lap7:~$ g++ -Wall -O3 -std=c++0x a.cpp -o a && ./a
12
vorher i direkt 12
13
vorher ueber Zeiger 12
14
nachher i direkt 12
15
nachher ueber Zeiger 42
16
klaus@lap7:~$
Weil der böse cast zu undefiniertem Verhaöten führt, kann das Ergebnis
mit einem anderen Compiler theoretisch auch anders aussehen.
Aber zumindest sieht man hier, was der gcc sich dabei denkt:
Die Variable i ist als const deklariert, also denkt sich der gcc, daß
sie sich nicht ändert und nimmt bei der Verwendung des Werts gar nichts
aus dem Speicher, sondern setzt gleich direkt den Wert 12 jeweils ein.
Weil ich aber die Adresse von i verwende, ist er gezwungen, tatsächlich
auch eine int-Variable anzulegen (was er sonst zumnindest mit
Optimierung weglassen würde).
Über diese Adresse kann ich in der zweiten Ausgabe ebenfalls 12 sehen,
Ebenfalls über die Adresse ändere ich den Wert zu 42, und gebe wieder
auf beide Arten aus. Bei i nimmt der Code gar nicht den Wert aus dem
Speicher ("ist ja const..."), sondern der Compiler setzt wieder gleich
die vermutete 12 ein.
Über den Zeiger gebe ich dagegen den wirklichen Wert 42 aus.
Interessant: ändere ich i von const i zu const volatile i, dann verlässt
sich der Compiler nicht mehr auf das const, sondern macht es richtig:
1
#include<iostream>
2
3
voidzeigeInt(constchar*kommentar,constinti)
4
{
5
std::cout<<kommentar<<i<<std::endl;
6
}
7
8
intmain(intnargs,char**args)
9
{
10
constvolatileinti=12;
11
12
int*non_const_ptr_i=const_cast<int*>(&i);// böse, aber nicht verboten
Klaus W. schrieb:> volatile
Ich vermute Mal, das volatile in C++ eine ähnliche Bedeutung hat, wie in
C. Nämlich das Objekt tatsächlich auszulesen.
Überraschend für mich wäre, wenn Du nach der Änderung von x ein Array
char a[x] anlegst. Und sizeof(a) dann 42 ergibt. Das wäre ein Hinweis,
dass der Compiler x nicht als Konstante sieht.
Wilhelm M. schrieb:> Das führt im Endeffekt dazu, dass die constexpr-"Variablen" keine Variablen> (benannte Objekte) mehr sind und deswegen sich nicht materialisieren.
Im Sine der Sprache sind sie immer noch Variablen.
Wilhelm M. schrieb:> A. S. schrieb:>> Für C++ heißt const auch konstant.>> Nein. Und das Beispiel mit const volatile hast Du ja selbst geliefert.>> Oder> int x;> const int& rx = x;>> Hier ist ganz klar ersichtlich, dass const hier read-only bedeutet.
rx ist aber kein Objekt.
A. S. schrieb:> Dass bei einem Zeiger oder einer Referenz etwas anderes gilt, bleibt da> unbenommen. Ein Zeiger auf ein Objekt ist kein Objekt.
Ein Zeiger ist schon ein Objekt. Aber das const bezieht sich in nicht
auf den Zeiger. Ein const int* ist kein konstanter Zeiger, sondern ein
Zeiger, über den man das Objekt, auf das er zeigt, nicht ändern kann.
Das sagt weder etwas über die Konstantheit des Zeigers aus, noch etwas
darüber, ob das Zielobjekt selbst eine Konstante ist.
Klaus W. schrieb:> A. S. schrieb:>> Klaus W. schrieb:>>> Beispiel:>>> const volatile int i = 0;>>> heißt: hier wird i nicht mehr geändert, aber in einem anderen Thread>>> oder einem Signalhandler, Interrupt o.ä. kann es geändert werden.>>>> i ins RAM gelegt wird?>> Auch in C++ kann ich von einer const-Variablen eine Adresse bilden.> Spätestens dann wird der Compiler die auch im RAM halten.
Er muss sie so oder so ins RAM legen, wenn sie volatile ist.
A. S. schrieb:> Wilhelm M. schrieb:>> A. S. schrieb:>>>>> In C nur Read-Only, in C++ jedoch>>> konstant.>>>> Das musst Du mal erklären>> const int x = 2;>> In C++ kann ich das x als Konstante verwenden, z.b. int a[x];>> X ist konstant, eine Konstante.
Ja. In C++ gibt es allerdings zwei Arten von Initialisierung, nämlich
statische und dynamische. Statische Initialisierung erfolgt zur
Compilezeit, dynamische zur Laufzeit. In C könnten Objekte mit
statischer Lebensdauer auch nur statisch initialisiert werden, während
diese Einschränkung in C++ nicht besteht. Dein x kann hier verwendet
werden, weil es statisch initialisiert wurde. Würde es dynamisch
initialisiert, z.B. durch einen Funktionsaufruf, dann würde das nicht
mehr gehen, obwohl x const ist.
Wilhelm M. schrieb:>> A. S. schrieb:>>>>> In C nur Read-Only, in C++ jedoch>>> konstant.
In C++ konstant oder nur read-only, je nach Kontext.
> A. S. schrieb:>> In C++ kann ich das x als Konstante verwenden, z.b. int a[x];>> Und die meinst, das geht in C nicht?
Es geht in C nur, wenn das Array lokal ist und VLAs unterstützt werden.
Sonst geht es nicht, da eben nur konstante Ausdrücke als Array-Größen
verwendet werden dürfen und x kein solcher ist.
> A. S. schrieb:>> X ist konstant, eine Konstante.>> Nö:> [c]> *const_cast<int*>(&x) = 42;> [c]
Das hat undefiniertes Verhalten, und es wird in der Praxis auch oft
nicht so funktionieren, wie du dir das denkst.