Forum: Compiler & IDEs Frage bzgl. Verarbeitung einer Konstantendeklaration


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Yves E. (yves19)


Lesenswert?

Moin,

ich habe dieser Tage ein einfaches Thermostat gebaut mit einem Atmega88 
und nem DS18B20.
Das Thermostat hat eine Hysterese damit das Aggregat nicht zu oft 
anspringen muss.
Als Parameter habe ich eine Konstante deklariert und einen Wert 
zugewiesen:
1
const uint32_t hysterese = (15 * 60 * 1000);  //Hysterese in ms

Beim Trockentest ging noch alles, da habe ich den Wert auf 10000 
gesetzt, aber bei Verwendung obiger Zeile kam offenbar ein Wert raus, 
der nicht 900000 Betrug, da nie geschalten wurde.
Ich benutze als IDE Visual Studio Code mit Platformio und dem Arduino 
Framework.
Für Konstanten habe ich sonst immer Makros bzw. einfache 
Präprozessoranweisungen genutzt, aber ich dachte mir wenn man eine 
Konstante deklariert, dann weiß man wenigstens genau, was die für einen 
Typ hat, das ist am Ende transparenter und sicherer.

Meine Frage ist jetzt, wieso hat die Zuweisung so nicht funktioniert? 
Ich würde das gerne verstehen.

: Bearbeitet durch User
von DerEinzigeBernd (Gast)


Lesenswert?

Der Compiler rechnet mit int, wenn man ihm bei numerischen Konstanten 
nichts anderes mitteilt. Auf Deinem AVR-Arduino ist int ein 
16-Bit-Datentyp.

Ändere Deine Initialisierung mal zu
1
const uint32_t hysterese = (15 * 60 * 1000L);  //Hysterese in ms

(Das Suffix L weist den Compiler an, mit long zu rechnen. Analog gibt 
es auch das Suffix UL, das hat unsigned long zur Folge).

von Markus F. (mfro)


Lesenswert?

... und wenn Du die Compiler-Warnungen eingeschaltet hättest (etwas, was 
man immer machen sollte, -Wall, mindestens), hätte dir der Compiler 
hier eine Warnung bezüglich des Integer-Overflows ausgeworfen.

von Yves E. (yves19)


Lesenswert?

DerEinzigeBernd schrieb:
> Der Compiler rechnet mit int, wenn man ihm bei numerischen
> Konstanten
> nichts anderes mitteilt. Auf Deinem AVR-Arduino ist int ein
> 16-Bit-Datentyp.
>
> Ändere Deine Initialisierung mal zu
> const uint32_t hysterese = (15  60  1000L);  //Hysterese in ms
>
> (Das Suffix L weist den Compiler an, mit long zu rechnen. Analog gibt
> es auch das Suffix UL, das hat unsigned long zur Folge).

Ah, ok. Also damit hab ich nicht gerechnet. Klar dass ein int beim 
Atmega 16-bit hat, aber ich ging davon aus, dass der Compiler das 
ohnehin zur Compilezeit berechnet und dabei, entsprechend der CPU 
Architektur des PCs, mit 32-bit rechnet.
Alles klar, wieder was gelernt.

von Dirk B. (dirkb2)


Lesenswert?

Markus F. schrieb:
> ... und wenn Du die Compiler-Warnungen eingeschaltet hättest (etwas, was
> man immer machen sollte, -Wall, mindestens), hätte dir der Compiler
> hier eine Warnung bezüglich des Integer-Overflows ausgeworfen.

Mann muss die Warnungen auch beachten und deren Ursache beheben.
(-Wall ist nicht die Ursache)

von Markus F. (mfro)


Lesenswert?

Dirk B. schrieb:
> Mann muss die Warnungen auch beachten und deren Ursache beheben.
> (-Wall ist nicht die Ursache)

Ach.

Frau übrigens auch.

von W.S. (Gast)


Lesenswert?

Yves E. schrieb:
> //Hysterese in ms

Mal eine ganz andere Frage:
Wie kommst du auf eine Hysterese in Zeiteinheiten?
Das Ganze soll ja wohl ein Thermostat werden, also wäre da eine 
Hysterese in Temperatureinheiten angesagt.

W.S.

von Kaj (Gast)


Lesenswert?

Yves E. schrieb:
> aber ich ging davon aus, dass der Compiler das
> ohnehin zur Compilezeit berechnet und dabei, entsprechend der CPU
> Architektur des PCs, mit 32-bit rechnet.
Warum sollte der Compiler die Architektur des PC (heute üblicherweise 
64-bit) berücksichtigen, wenn doch für ein ganz anderes Target, hier 
AVR, compiliert wird?
Wenn Du etwas von Sprache X nach Sprache Y übersetzen sollst, spielt es 
für Dich doch auch keine Rolle, in welchem Land du dich befindest.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yves E. schrieb:
> Moin,
>
>
1
const uint32_t hysterese = (15 * 60 * 1000);  //Hysterese in ms
>
> Ich benutze als IDE Visual Studio Code mit Platformio und dem Arduino
> Framework.

Weil Du Arduino benutzt, benutz Du also C++ ...

> Für Konstanten habe ich sonst immer Makros bzw. einfache
> Präprozessoranweisungen genutzt, aber ich dachte mir wenn man eine
> Konstante deklariert, dann weiß man wenigstens genau, was die für einen
> Typ hat, das ist am Ende transparenter und sicherer.

CPP-Macros kannst Du bei C++ immer vermeiden (weil sie Mist sind).

Mache
1
constexpr uint32_t hysterese = (15 * 60 * 1000);  //Hysterese in ms

draus, und Du bekommst auch einen Fehler.
Und schalte in Zukunft Deine Warnungen ein (mindestens -Wall -Wextra 
...).

> Meine Frage ist jetzt, wieso hat die Zuweisung so nicht funktioniert?

Es ist keine Zuweisung!
Den Unterschied zwischen Zuweisung und Initialisierung ist etwas, was 
man ganz am Anfang des Lernens von C++ verstehen sollte / muss!

von Yves E. (yves19)


Lesenswert?

Alles klar, danke für die zahlreichen Antworten.



W.S. schrieb:
> Yves E. schrieb:
>> //Hysterese in ms
>
> Mal eine ganz andere Frage:
> Wie kommst du auf eine Hysterese in Zeiteinheiten?
> Das Ganze soll ja wohl ein Thermostat werden, also wäre da eine
> Hysterese in Temperatureinheiten angesagt.
>
> W.S.

Weil sich diese Hysterese auf die Schalthäufigkeit bezieht und nicht auf 
die Temperatur. Das System, das gekühlt werden soll ist recht träge, da 
spielt es keine Rolle, wenn höchstens alle 15 Min. etwas am 
Schaltzustand geändert werden kann. Die Temperaturabweichungen sind 
überschaubar und das Aggregat wird so nicht sinnlos strapaziert.

Kaj schrieb:
> Warum sollte der Compiler die Architektur des PC (heute üblicherweise
> 64-bit) berücksichtigen, wenn doch für ein ganz anderes Target, hier
> AVR, compiliert wird?
> Wenn Du etwas von Sprache X nach Sprache Y übersetzen sollst, spielt es
> für Dich doch auch keine Rolle, in welchem Land du dich befindest.

Schon klar, aber Compiler optimieren ja auch ne Menge Dinge weg, von 
daher ging ich davon aus, da es sich hier um eine Konstante handelt, 
würde der Compiler aus dieser Initialisierung einfach eine Zuweisung 
machen.
So dass am Ende einfach:
1
const uint32_t hysterese = 900000;

daraus wird.
Wie ja nun hinreichend geklärt wurde, war das eine falsche Annahme.

von Rolf M. (rmagnus)


Lesenswert?

Markus F. schrieb:
> ... und wenn Du die Compiler-Warnungen eingeschaltet hättest (etwas, was
> man immer machen sollte, -Wall, mindestens), hätte dir der Compiler
> hier eine Warnung bezüglich des Integer-Overflows ausgeworfen.

Es geht um Arduino. Das unterdrückt defaultmäßig alle Warnungen, weil 
sowas  für die Arduino-Zielgruppe als zu kompliziert angesehen wird…

Yves E. schrieb:
> Klar dass ein int beim
> Atmega 16-bit hat, aber ich ging davon aus, dass der Compiler das
> ohnehin zur Compilezeit berechnet und dabei, entsprechend der CPU
> Architektur des PCs, mit 32-bit rechnet.

Es wird zur Compilezeit berechnet, allerdings nach genau den selben 
Regeln wie eine Berechnung zur Laufzeit. Man will ja nicht, dass ein und 
die selbe Berechnung zur Compilezeit ein anderes Ergebnis hat als wenn 
sie zur Laufzeit durchgeführt worden wäre.

von Léo (Gast)


Lesenswert?

Yves E. schrieb:
> Schon klar, aber Compiler optimieren ja auch ne Menge Dinge weg, von
> daher ging ich davon aus, da es sich hier um eine Konstante handelt,
> würde der Compiler aus dieser Initialisierung einfach eine Zuweisung
> machen.
> So dass am Ende einfach:
> const uint32_t hysterese = 900000;
>
> daraus wird.

Das ist nach wie vor keine Zuweisung, sondern eine Initialisierung.

Korrekterweise solltest Du
1
const uint32_t hysterese = 900000UL;

schreiben, denn 900000 ist kein int, und Du willst eine 
unsigned-Variable damit initialisieren.

von Rolf M. (rmagnus)


Lesenswert?

Léo schrieb:
> Korrekterweise solltest Du
> const uint32_t hysterese = 900000UL;
>
> schreiben, denn 900000 ist kein int, und Du willst eine
> unsigned-Variable damit initialisieren.

Noch besser:
1
const uint32_t hysterese = UINT32_C(900000);

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wenn man den Initwert von Hand ausrechnet, gibt's ja kein Problem mehr 
weil 900000 long ist, 15*60*1000 jedich int.  Also wenn schon, dann
1
const uint32_t hysterese = 15ul * 60ul * 1000ul;
oder eben
1
const uint32_t hysterese = UINT32_C(15) * UINT32_C(60) * UINT32_C(1000);

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wenn man den Initwert von Hand ausrechnet, gibt's ja kein Problem mehr
> weil 900000 long ist, 15*60*1000 jedich int.  Also wenn schon, dann
>
>
1
const uint32_t hysterese = 15ul * 60ul * 1000ul;

Es reicht:
1
constexpr auto hysterese{15 * 60 * 1000ul};

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Es reicht auch
1
15ul * 60 * 1000
Und jetzt?

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.