OOP Einsteiger "cross initialisierung"

Guten Abend zusammen,

bei dem Versuch mein bisher angeeignetes Halbwissen in die Tat umzusetzen stieß ich auf ein für mich nicht erklaerbares Problem. Compiler meldet:

C:\Users\XXX\Desktop\PID_OOP\PID_OOP.ino: In function 'void loop()':

C:\Users\XXX\Desktop\PID_OOP\PID_OOP.ino:87:7: warning: jump to case label [-fpermissive]

       default:

       ^

PID_OOP:82: error: crosses initialization of 'float r_f'

         float r_f = Serial.parseFloat(); //WARUM "cross initialisierung" ?

               ^

exit status 1
crosses initialization of 'float r_f'

Der Rest soll nur eine Lernumgebung für Klassen und deren Methoden darstellen. Das Kapitel mit Konstruktoren wird nochmals gelesen und dann angewand. :)

/******** globale_Konstanten ********/
const uint8_t Sensor = A0;
const uint8_t Heizung = 3;

/******** class PID ********/
//const / static anwenden?!
class PID
{
  private:
    unsigned long Zyklus = 500;  //(t) je XXms neue Berechnung
    unsigned long kal_ms = 0;
    float Sollwert = 0.0;
    float Kp =  25.0;  //(25.0)Verstaerkungsfaktor P
    float Ki =  0.10;  //(0.10)Verstaerkungsfaktor I
    float Kd =  1.00;  //(1.00)Verstaerkungsfaktor D
    float e[2] = {0.0, 0.0};
    float esum = 0.0;
    float d_t = 0.0;
    float PID_ = 0.0;
  public:
    unsigned long get_Zyklus() {
      return Zyklus;
    }
    float get_Kp()  {
      return Kp;
    }
    float get_Ki()  {
      return Ki;
    }
    float get_Kd()  {
      return Kd;
    }
    float get_PID(const float Messwert, const unsigned long _akt_ms)
    {
      if (_akt_ms - kal_ms >= Zyklus) {
        float d_t = ((float)_akt_ms - (float)kal_ms) / 1000.0;
        kal_ms = _akt_ms;
        e[1] = e[0];
        e[0] = Sollwert - Messwert;
        esum += e[0];
        PID_ = (Kp * e[0]) + (Ki * esum * d_t) + (Kd * (e[0] - e[1]) / d_t);
      }
      return PID_; //unveraenderte Ausgabe
    }
    void set_Zyklus(const unsigned long _Zyklus) {
      Zyklus = _Zyklus;
    }
    void set_Kp(const float _Kp) {
      Kp = _Kp;
    }
    void set_Ki(float _Ki) {
      Ki = _Ki;
    }
    void set_Kd(float _Kd) {
      Kd = _Kd;
    }
};//class PID ENDE

/******** PID Objekte ********/
PID Regler_1/*, Regler_2*/;
/******** Prototypen ********/
float NTC_Temperatur(const float U_in);
/****************************/

void setup()
{
  Serial.begin(19200);
  while (!Serial) {}
  pinMode(Sensor, INPUT);
  pinMode(Heizung, OUTPUT);
}//void setup() ENDE

void loop()
{
  if (Serial.available() > 0)
  {
    switch (Serial.read())
    {
      case 3:
        Serial.print(F("Kp alt = "));
        Serial.println(Regler_1.get_Kp(), 2);
        float r_f = Serial.parseFloat(); //WARUM "cross initialisierung" ?
        Regler_1.set_Kp(r_f);
        Serial.print(F("Kp neu = "));
        Serial.println(Regler_1.get_Kp(), 2);
        break;
      default:
        Serial.println(F("Fehlerhafte Eingabe"));
        break;
    }
  }
  unsigned long akt_ms = millis();
  float T_ist = NTC_Temperatur((float)analogRead(Sensor));
  /******** PID_Wert ********/
  float PID_Wert = Regler_1.get_PID(T_ist, akt_ms);
  /******** Regler Ausgang ********/
  uint8_t PWM = constrain((long)PID_Wert, 0, 255);
  analogWrite(Heizung, PWM);
}//void loop() ENDE


float NTC_Temperatur(const float U_in)
{
  const float T_0 = 273.15;        //abs. Nullpunkt
  const float B_NTC = 3977.0;      //Beta Materialkonstante
  const float T_NTC = 25.0 + T_0;  //NTC Nenntemperatur
  const float R_NTC = 10000.0;     //NTC Nennwiderstand
  const float R_v = 9935.0;        //10k R (Spannungsteiler)
  float pU = U_in / 1023.0;
  float R = R_v * pU / (1.0 - pU);
  float T_k = T_NTC * B_NTC / (B_NTC + T_NTC * log(R / R_NTC));
  return T_k - T_0;
}//float NTC_Temperatur() ENDE

Besten Dank Grillgemuese

Ich interpretiere die Meldung so: Du sollst in einem case keine lokalen Variablen definieren.

Übrigens: Ich bin noch nie auf die Idee gekommen... Die Meldung ist also auch für mich neu.

@combie

Du sollst in einem case keine lokalen Variablen definieren.

Wird als Merksatz abgespeichert!! Welchen Grund hat es? Kannst du das kurz erläutern.

Nicht exakt.... Muss da auch mehr raten.... als ich weiß.

Es hat wohl damit zu tun, dass Switch/case keine wirklichen und abgeschlossenen { } Blocks hat. Und die case Lables quasi das selbe wie Goto Jump Ziele sind.

Damit kann der Compiler nicht gewährleisten, dass diese Variablen wirklich schon Initialisiert sind, wenn die Ausführung durch den Sprung da hin gezwungen wird.

Abhilfe A: Die Variable außerhalb des Switch definieren.

Abhilfe B: Einen Block einführen! z.B. so:

     case 3:
      {
        Serial.print(F("Kp alt = "));
        Serial.println(Regler_1.get_Kp(), 2);
        float r_f = Serial.parseFloat(); // ENDE mit "cross initialisierung" ?
        Regler_1.set_Kp(r_f);
        Serial.print(F("Kp neu = "));
        Serial.println(Regler_1.get_Kp(), 2);
      }  
      break;

Denn in einem Block, ohne Quereinstieg, ist die korrekte Initialisierung gewährleistet. Das ist in der Sprachdefinition so festgelegt. Völlig unabhängig vom gerade verwendetem Sprachkonstrukt. Nur Switch und Goto erlauben den Quereinstieg in einen Block, und unterlaufen damit die den Initialisierungsmechanismus. Und deshalb haut einem der Kompiler das um die Ohren. Meinen Dank dafür. Früher war das nicht so, da hat der Kompiler das geschluckt und das Programm nur stumm versagt. Das hat schon so manchem eine längere Debugsitzung beschert.

Leider ist die irrige Meinung weit verbreitet, ein switch/case Konstrukt wäre was ganz tolles, ein Heilsbringer, das ultimative Mittel gegen Ohrensausen und Fußpilz. Oder sowas wie eine if/else Verkettung. Das ist aus Anwender Sicht vielleicht so, aber in der Realität, aus Compilersicht, ist es eher/meist eine (Goto) Sprungleiste. Das kann man auch schön im Assemblercode sehen. Switch/case ist aus technischer Sicht nicht wirklich mehr als ein (heimliches/verstecktes) Goto Konstrukt mit einer bunten Schleife drum rum, und rosa Punkten drauf.

Wie sollte es anders sein... Sowohl mit Goto, als auch mit Switch/case, kann man mitten in Blöcke hinein springen, selbst mitten in Schleifen, und dann ist man u.a. wieder beim "error: crosses initialization" Problem.

Tipp: Die Optimierungen abstellen und den generierten Code ansehen, dann springt einen die Äquivalenz von Switch und Goto förmlich an.

Danke für die Erklärung.

mfG Thorsten4171