Wie macht man so etwas eleganter?



  • Ich habe z.B. folgenden Code:

    #define PAKET_S23 75437
    #define PAKET_S22 60349
    #define PAKET_S21 48279
    #define PAKET_S20 38623 
    #define PAKET_S19 0
    #define PAKET_S18 24719
    #define PAKET_S17 19775
    #define PAKET_S16 15820
    #define PAKET_S15 12656
    #define PAKET_S14 10125
    
    #define S23 (3+3)
    #define S22 7
    #define S21 0
    #define S20 11
    #define S19 0
    #define S18 0
    #define S17 0
    #define S16 0
    #define S15 0
    #define S14 0
    
    int main(){
      long int paketschnitt = (S14 * PAKET_S14 
                             + S15 * PAKET_S15 
                             + S16 * PAKET_S16 
                             + S17 * PAKET_S17 
                             + S18 * PAKET_S18 
                             + S19 * PAKET_S19 
                             + S20 * PAKET_S20 
                             + S21 * PAKET_S21 
                             + S22 * PAKET_S22 
                             + S23 * PAKET_S23)/(S14+S15+S16+S17+S18+S19+S20+S21+S22+S23);
      printf("%li = \n", paketschnitt);
      return 0;
    }
    

    Bei den DEFINE Anweisungen soll nun immer ein weiterer Eintrag dazu und die bestehenden S* Werte werden verändert, das Programm wird dafür neu compiliert.
    So weit so gut. Das Problem ist allerdings, dass ich dann die Berechnung in Zeile 24 jedes mal ebenfalls erweitern muss, sobald ein neuer PAKET_S* und S* Eintrag hinzukommt und das ist schlecht.

    Das würde ich gerne verhindern.
    Behalten will ich allerdings, wenn irgendwie möglich, dass neue Konstanten als DEFINE Anweisungen beibehalten werden, weil die eben ganz oben im Quellcode oder bei Bedarf in einem Header stehen und man nicht lange suchen muss.

    Wie würdet ihr das am besten Lösen?

    Überlegt habe ich mir hierzu daher auch so etwas wie das da:

    #define PAKETFELD long int paketfeld[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10125, 12656, 15820, ..., 75437};
    #define PAKETE long int pakete[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 7, (3+3)}; 
    
    usw.
    

    Damit könnte ich dann später die Arraywerte anhand der Arraylänge im main Block in einer Schleife zusammenrechnen und das würde dann so gehen, dass ich diesen Codeabschnitt nicht mehr verändern müsste, da weitere Einträge ja von der Arraylänge abhängig wären.

    Das Problem an dieser Lösung ist allerdings, dass man dann die Stellen im Array bei der Defineanweisung jedesmal abzählen müßte, damit man weiß, wo was reinkommt und das ist wiederum unschön und auch wesentlich umständlicher als obige Lösung, wo man noch den Code erweitern muss.
    Fehleranfälliger wäre das so auch, weil man ja die falschen Zahlen an der falschen Stelle im Array eintragen könnte.

    Ich suche also noch etwas eleganteres.
    Hat jemand vorschläge?

    PS:
    Prinzipiell könnte ich die veränderlichen Werte natürlich auch einfach in eine Textdatei packen und diese dann bei Programmstart immer einlesen und dann berechnen, dadurch müsste ich den Code auch nicht mehr neu compilieren, aber
    mich würde jetzt erst einmal interessieren, ob es bessere Lösungen gibt die innerhalb der Quellcodedatei ohne ausgelagerte Textdatei oder Parameterübergabe beim Aufruf funktionieren.
    Es soll hierbei auch nicht nach einer Eingabe gefragt werden.



  • Ein 2D-Array.
    Dann sind die Werte immer paarweise. Als Endekennung kannst du ja ungültige Werte nehmen. {-1, -1}
    Auf Index 0 der Paketschnitt und Index 1 die Pakete.

    Du kannst das auch alleine in eine .c Datei packen.



  • DirkB schrieb:

    Ein 2D-Array.
    Dann sind die Werte immer paarweise. Als Endekennung kannst du ja ungültige Werte nehmen. {-1, -1}

    Paarweise ist ne gute Idee, dann hängen die konstanten Größen (PAKET_S*) schon einmal mit der Anzahl (S*) zusammen, allerdings ist es dann immer noch nicht durchnummeriert.

    Die Größe kann dann zwar zum orientieren helfen, aber da die Werte als Nummerierung von 1-n vorgegeben sind muss ich dann trotzdem die richtige Stelle im Array finden. So ganz optimal ist das also noch nicht.

    Als Endkennung habe ich die Arraylänge.

    Auf Index 0 der Paketschnitt und Index 1 die Pakete.

    Das ist eine gute Idee, danke.

    Würde aber so etwas gehen:

    #define long int paketfeld[][] = { /* Paketschnitt, Anzahl */ (0, 0),
                                       /* S1 = */ (0, 0),
                                       /* S2 = */ (0, 0),
                                       ...
                                       /* S20 = */ (11, 38623),
                                       ...
                                       /* S22 = */ (7, 60349)
                                     };
    ...
    

    Damit hätte ich eine Durchnummerierung durch die Kommentarzeichen und man würde sehr schnell die richtige Position im Array für das Eintragen finden.
    Es sieht allerdings nicht so schön aus.



  • Hm, geht:

    #define PAKETFELD  long int paketfeld[5][2] = { /* S1 */ {0, 0}, /* S2 */ {0, 0}, {0, 0}, {11, 38623}, {7, 60349} };
    

    geht nicht:

    #define PAKETFELD  long int paketfeld[5][2] = { /* S1 */ {0, 0}, 
                                                    /* S2 */ {0, 0}, {0, 0}, {11, 38623}, {7, 60349} };
    

    Kann man die Define Anweisungen irgendwie in mehrere Zeilen umbrechen oder müssen die zwingend in einer Zeile stehen?

    Auch stört mich an der Lösung, dass die die Feldgrößen fest vorgeben muss.
    Die zweier Tupel geht ja noch, die zwei bleibt eh konstant, aber wenn ich immer weitere Werte hinzufüge, dann muss ich die Dimensionsdefinition des Arrays abenfalls ändern.

    So richtig gefallen tut mir das daher eigentlich nicht.



  • Hab's raus:

    geht:

    #define PAKETFELD  long int paketfeld[5][2] = { /* S1 */ {0, 0},\
                                                    /* S2 */ {0, 0}, {0, 0}, {11, 38623}, {7, 60349} };
    


  • und lass noch das define weg, das brauchst du ja jetzt nicht mehr.



  • Shade@urlaub schrieb:

    und lass noch das define weg, das brauchst du ja jetzt nicht mehr.

    Doch, das war ja Sinn der Sache.

    Beispiel:

    vorher:

    #define PAKETFELD  long int paketfeld[5][2] = { /* S1 */ {0, 0},\
                                                    /* S2 */ {0, 0},\
                                                    /* S3 */ {0, 0},\
                                                    /* S4 */ {11, 38623}, \
                                                    /* S5 */ {7, 60349} \
                                                   };
    

    nachher mit erweitertem Wert

    #define PAKETFELD  long int paketfeld[6][2] = { /* S1 */ {0, 0},\
                                                    /* S2 */ {0, 0},\
                                                    /* S3 */ {0, 0},\
                                                    /* S4 */ {11, 38623}, \
                                                    /* S5 */ {7, 60349}, \
                                                    /* S6 */ {42, 72039} \
                                                   };
    

    Man sieht hier leider auch, dass es nicht genügt, nur das Array um

    /* S6 */ {42, 72039} \
    

    zu erweitern, sondern ich muss auch noch in der Zeile davor zwischen der schließenden geschweiften Klammer und dem Backslash ein Komma setzen und dann auch noch die 1. dimension der Arraygröße um 1 erhöhen.

    EDIT:

    Kleine Verbesserung, wenn ich das Komma davor schreibe, dann genügt ein einfaches Copy&Pasten der letzten Zeile und ändern der Werte.
    Am ändern der Dimension ändert das aber nichts.
    Ich muss also weiterhin immer noch mindestens zwei Stellen ändern.

    Und da sich die Anzahl in allen Reihen ändern kann, muss ich auch alle [*][0] ändern.

    #define PAKETFELD  long int paketfeld[6][2] = {  /* S1 */ {0, 0}\
                                                    ,/* S2 */ {0, 0}\
                                                    ,/* S3 */ {0, 0}\
                                                    ,/* S4 */ {11, 38623} \
                                                    ,/* S5 */ {7, 60349} \
                                                    ,/* S6 */ {42, 72039} \
                                                   };
    


  • Im Programm sieht's jetzt ungefähr so aus:

    #define PAKETFELD  long int paketfeld[25][2] = { /* Schnitt, Anzahl */ {0, 0} \
                                                     , /*  S1 */ {0, 0} \
                                                     , /*  S2 */ {0, 0} \
                                                     , /*  S3 */ {0, 0} \
                                                     , /*  S4 */ {0, 0} \
                                                     , /*  S5 */ {0, 0} \
                                                     , /*  S6 */ {0, 0} \
                                                     , /*  S7 */ {0, 0} \
                                                     , /*  S8 */ {0, 0} \
                                                     , /*  S9 */ {0, 0} \
                                                     , /* S10 */ {0, 0} \
                                                     , /* S11 */ {0, 0} \
                                                     , /* S12 */ {0, 0} \
                                                     , /* S13 */ {0, 0} \
                                                     , /* S14 */ {10125, 0} \
                                                     , /* S15 */ {12656, 0} \
                                                     , /* S16 */ {15820, 0} \
                                                     , /* S17 */ {19775, 0} \
                                                     , /* S18 */ {24719, 0} \
                                                     , /* S19 */ {0, 0} \
                                                     , /* S20 */ {38623, 11} \
                                                     , /* S21 */ {48279, 0} \
                                                     , /* S22 */ {60349, 7} \
                                                     , /* S23 */ {75437, 6} \
                                                     , /* S24 */ {0, 0} \
                                                   };
    
    ...
    int main(){
      ...
      PAKETFELD
    
      for(int i = 1; i < (sizeof(paketfeld) / sizeof(paketfeld[0][0]))/2 ; i++) {
          paketfeld[0][0] += paketfeld[i][0]*paketfeld[i][1]; // Schnitt
          paketfeld[0][1] += paketfeld[i][1]; // Anzahl
      }
    
      if (paketfeld[0][1] == 0){
        return 1;
      }
      paketfeld[0][0] = paketfeld[0][0] / paketfeld[0][1]; // Paketgrößen durch Anzahl
      printf("Anzahl: %li\n", paketfeld[0][1]);
      printf("Durchschnitt: %li\n", paketfeld[0][0]);
      ...
    

    So ganz das wahre ist es nicht, aber es ist besser als vorher, als ich die Formel immer erweitern mußte.
    Rechnen tut es ebenfalls richtig.

    Falls jemand noch bessere Ideen oder Vorschläge hat, würde ich die natürlich immer noch gerne hören.

    Und dann habe ich auch noch eine kleine Frage zu letzten Code.
    Wäre es schöner, wenn ich die Abfrage auf 0 mit der Berechnung, wo ich teilen muss zu einem If Block zusammenfasse, damit die Berechnung bei der durch den Wert geteilt wird, nicht völlig so alleine herumsteht.

    Also anstatt:

    if (paketfeld[0][1] == 0){
        return 1;
      }
      paketfeld[0][0] = paketfeld[0][0] / paketfeld[0][1]; // Paketgrößen durch Anzahl
    

    besser so?

    if (paketfeld[0][1] == 0){
        return 1;
      } else {
        paketfeld[0][0] = paketfeld[0][0] / paketfeld[0][1]; // Paketgrößen durch Anzahl
      }
    

    oder so

    if (paketfeld[0][1] != 0){
        paketfeld[0][0] = paketfeld[0][0] / paketfeld[0][1]; // Paketgrößen durch Anzahl    
      } else {
        return 1;
      }
    

    Welche der 3 Varianten ist da schöner/besser?

    Bezüglich der Wartbarkeit könnte ich mir nämlich gut vorstellen, dass das besser ist, wenn man es zusammenfasst.
    Ob's dadurch einen Performancenachteil gibt, weiß ich allerdings nicht.



  • Du machst eine Datei paketfeld_def.c

    long int paketfeld[][2] = { /* Schnitt, Anzahl */ {0, 0} 
                                                     , /*  S1 */ {0, 0} 
                                                     , /*  S2 */ {0, 0} 
                                                     , /*  S3 */ {0, 0} 
                                                     , /*  S4 */ {0, 0} 
                                                     , /* ENDE */ {-1, -1} 
                                                   };
    

    Und in deiner Main

    int main(){
      ...
      #include "paketfeld_def.c"
    
      for(int i = 1;  paketfeld[ i][0] >= 0 ; i++) {
          paketfeld[0][0] += paketfeld[ i][0]*paketfeld[ i][1]; // Schnitt
          paketfeld[0][1] += paketfeld[ i][1]; // Anzahl
      }
    

    Bei if (paketfeld[0][1] == 0){ würde ich die Variante 1 nehmen, da durch das return das else keine Relevanz mehr hat.
    Du willst damit ja die Fehlerüberprüfung machen.

    Eine weiter Möglichkeit wäre, eine struct zu machen

    struct paket { long int schnitt, anzahl };
    

    Daraus dann das Array:

    struct paket paketfeld[] = { /* Schnitt, Anzahl */ {0, 0} 
                                                     , /*  S1 */ {0, 0} 
                                                     , /*  S2 */ {0, 0} 
                                                     , /*  S3 */ {0, 0} 
                                                     , /*  S4 */ {0, 0} 
                                                     , /* ENDE */ {-1, -1} 
                                                   };
    

    Dann spielt für deine Berechnung keine Rolle mehr, wo paketfeld herkommt.



  • Die Idee mit dem Struct gefällt mir, dann muss ich die Länge des dann eindimensionalen Arrays nicht mehr jedes mal neu definieren, wenn ein neuer Wert hinzukommt.

    Kann ich in die ausgelagerte *.c Datei auch define Anweisungen reinpacken?
    Dann würde das gehen, denn in meinem Programm muss ich auch noch ein paar Werte via define jedesmal neu anpassen.

    Und dann noch eine weitere Frage.
    Kann ich von der main.c dann eine Objektdatei *.o erstellen, so dass ich diese nicht mehr jedesmal neu compilieren muss?
    Also dass nur noch paketfeld_def.c neu compiliert werden muss?

    Bei if (paketfeld[0][1] == 0){ würde ich die Variante 1 nehmen, da durch das return das else keine Relevanz mehr hat.
    Du willst damit ja die Fehlerüberprüfung machen.

    Diese Begründung verstehe ich noch nicht ganz.
    Alles was im 1. If Bereich stattfindet, hat doch sowieso keine Relevanz für den else Bereich, auch wenn da kein return stehen würde, würde der else Bereich nicht aufgerufen werden, wenn der 1. Bereich zur Anwendung käme.



  • #define Array schrieb:

    Die Idee mit dem Struct gefällt mir, dann muss ich die Länge des dann eindimensionalen Arrays nicht mehr jedes mal neu definieren, wenn ein neuer Wert hinzukommt.

    Musst du beim 2D-aArray auch nicht machen. Zumindest die für dich wichtige Dimension.

    #define Array schrieb:

    Kann ich in die ausgelagerte *.c Datei auch define Anweisungen reinpacken?
    Dann würde das gehen, denn in meinem Programm muss ich auch noch ein paar Werte via define jedesmal neu anpassen.

    Ja.

    #define Array schrieb:

    Und dann noch eine weitere Frage.
    Kann ich von der main.c dann eine Objektdatei *.o erstellen, so dass ich diese nicht mehr jedesmal neu compilieren muss?
    Also dass nur noch paketfeld_def.c neu compiliert werden muss?

    So wie in meinem Beispiel nicht.
    #include und auch das von dir gewünschte #define ist reiner Textersatz.

    Es würde gehen, wenn du das Array paketfeld als globale Variable anlegst und in deiner main.c ein

    extern struct paket paketfeld[];
    

    schreibst. (außerhalb von jeder Funktion).
    Deine per #define gewünschten weiteren Werte bekommst du so aber nicht. Die musst du dann auch als globale Variablen nehmen. Aber auch da bietet sich eine struct an, die alle diese Werte aufnimmt. Dann hast du nur eine Variable und nicht dutzende.


Anmelden zum Antworten