K&R The C Programming Language: Word Lengths Histogram Aufgabe



  • Hallo Wutz,

    vielen Dank für Deine Antwort.

    Das mit dem Schreibfehler ist natürlich peinlich... Ich nehme da immer ausversehen das r mit 😃

    Ich habe versucht Deine Tips umzusetzten. Außerdem sind mir noch zwei weitere Dinge aufgefallen:

    - Ich verwendete die gefundene Länge direkt als Index. Dabei wird aber das erste Element (Index 0) niemals benutzt werden. Indem ich die Länge-1 als Index verwende,
    kann ich statt 99 bis zu 100
    Zeichen in einem Wort lesen. Ob das die erhöhte Komplexität wert ist
    (man muss immer beachten den Index richtig zu "übersetzten") ist fraglich,
    vor allem da ich ja auch einfach MAX_LEN als 200 definieren könnte.

    - Wenn die Datei nicht mit whitespace aufhört, dann wird das letzte Wort
    nicht mitgezählt. Hier musste ich also auch eine kleine Anpassung einführen.

    Ich habe mich entschieden diese stdin-loop vorerst nicht einzubauen, werde das Konstrukt aber "for future reference" abspeichern 👍

    Mein Programm sieht inzwischen so aus:

    #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LEN     100 /* maximal word length */
    #define MAX_COUNT   99
    
    #define CORNER      192 /* Bottom left corner of histogram */
    #define BAR         219 /* segment for graph */
    #define TOP_BAR     223 /* top half of graph segment */
    #define BOTTOM_BAR  220 /* bottom half of graph segment */
    #define BAR_WIDTH   3   /* # of characters used for displaying one length count */
    #define VERT_LINE   180 /* vertical line for y axis */
    #define VERT_DASH   180 /* display dash on y axis */
    #define HORI_LINE   196 /* Horizontal line for x axis */
    #define HORI_DASH   194 /* display dash in x axis */
    
    #define GRAPH_OFFST 9 /* # of characters (hori) before first graph begins */
    #define Y_STEP 5
    #define Y_AXIS_MIN_HEIGHT 5
    
    void count_word_lengths(FILE *fp, unsigned numWordsOfLen[], unsigned maxLen);
    void count_length(unsigned numWordsOfLen[], unsigned len, unsigned maxLen);
    
    void output_histogram(unsigned arr[], unsigned len);
    void output_header(unsigned numLengths, const char* title);
    void output_graph(unsigned arr[], unsigned len);
    void output_y_axis_segment(unsigned y, unsigned step);
    void output_graph_segment(unsigned arr[], unsigned len, unsigned currentY);
    void output_x_axis(unsigned arr[], unsigned len);
    
    unsigned find_max(unsigned arr[], unsigned len);
    void char_line(int ch, unsigned length);
    
    int main(int argc, char *argv[])
    {
        /* index = length, value = count */
        unsigned numWordsOfLen[MAX_LEN] = {0};
        FILE *fp;
    
    	if (argc != 2) {
    		printf("Error: Expecting one argument (filepath)\n");
    		return 1;
    	} else if ((fp = fopen(argv[1], "r")) == NULL) {
    		printf("Unable to open file %s\n", argv[1]);
    		return 2;
    	}
    
        count_word_lengths(fp, numWordsOfLen, MAX_LEN);
        output_histogram(numWordsOfLen, MAX_LEN);
        fclose(fp);
        return 0;
    }
    
    void count_word_lengths(FILE *fp, unsigned numWordsOfLen[], unsigned maxLen)
    {
        int ch;
        unsigned len = 0;
        while ((ch = fgetc(fp)) != EOF) {
            if (isspace(ch) && len) {
                count_length(numWordsOfLen, len, maxLen);
                len = 0;
            } else 
                ++len;
        }
        /* handle last word in file, if it was directly followed by EOF */
        if (len) count_length(numWordsOfLen, len, maxLen);
    }
    
    void count_length(unsigned numWordsOfLen[], unsigned len, unsigned maxLen)
    {  /* assumes val > 0 */
        if (len <= maxLen)
            ++numWordsOfLen[len - 1]; /* 0 will never be used, so we can read up to 100 char words */
        else
            printf("Word found exceeding max length (%d).\n"
                   "It will not be included in the histogram\n", maxLen);
    }
    
    void output_histogram(unsigned arr[], unsigned len)
    {
        unsigned i = 0, numLengths = 0;
        int exceededMaxCount = 0;
        unsigned outputLen = 0;
    
        for (i = 0; i != len; ++i)
            if (arr[i]) ++numLengths;
        outputLen = numLengths * BAR_WIDTH + GRAPH_OFFST;
    
        output_header(outputLen, "Histogram");
        output_graph(arr, len);
    
        char_line(BOTTOM_BAR, outputLen);
        /* output counts greater than MAX_COUNT which where cut-off */
        for (i = 0; i != len; ++i)
            if (arr[i] > MAX_COUNT) {
                printf("Total amount of words with length %d is: %d\n", i + 1, arr[i]);
                exceededMaxCount = 1;
            }
        if (exceededMaxCount) char_line(BOTTOM_BAR, outputLen);
    }
    
    void output_header(unsigned width, const char* title)
    {
        unsigned i;
        char_line(TOP_BAR, width);
    
        /* display the title aligned in the center */
        for (i = 0; i != (width - strlen(title)) / 2; ++i)
            putchar(' ');
        printf("%s\n", title);
    
        char_line(TOP_BAR, width);
    }
    
    void output_graph(unsigned arr[], unsigned len)
    {
        unsigned i = 0;
        unsigned maxCount = find_max(arr, len);
        if (maxCount < Y_AXIS_MIN_HEIGHT) maxCount = Y_AXIS_MIN_HEIGHT;
        if (maxCount > MAX_COUNT) maxCount = MAX_COUNT;
    
        printf(" count\n");
        for (i = maxCount; i != 0; --i) {
            output_y_axis_segment(i, Y_STEP);
            output_graph_segment(arr, len, i);
            putchar('\n');
        }
        output_x_axis(arr, len);
    }
    
    void output_y_axis_segment(unsigned y, unsigned step)
    {
        if (y % step == 0) 
            printf("%2d %c", y, VERT_DASH);
        else 
            printf("   %c", VERT_LINE);
    }
    
    void output_graph_segment(unsigned arr[], unsigned len, unsigned currentY)
    {
        unsigned i;
        for (i = 0; i != len; ++i) {
            if (arr[i]) {
                if (arr[i] == currentY)
                    printf("%c%c ", BOTTOM_BAR, BOTTOM_BAR);
                else if (arr[i] > currentY)
                    printf("%c%c ", BAR, BAR);
                else
                    printf("   ");
            }
        }
    }
    
    void output_x_axis(unsigned arr[], unsigned len)
    {
        unsigned i;
        printf("   %c", CORNER);
        for (i = 0; i != len; ++i) {
            if (arr[i]) {
                printf("%c%c%c", HORI_LINE, HORI_DASH, HORI_LINE);
            }
        }
        printf("> len\n    ");
        for (i = 0; i != len; ++i) {
            if (arr[i]) {
                printf("%2d ", i + 1);
            }
        }
        putchar('\n');
    }
    
    unsigned find_max(unsigned arr[], unsigned len)
    {
        unsigned max = 0;
        while (len--)
            if (arr[len] > max)
                max = arr[len];
        return max;
    }
    
    void char_line(int ch, unsigned len)
    {
        while (len--)
            putchar(ch);
        putchar('\n');
    }
    

    LG

    P.S.: Anmerkungen zum Stil sind auch nicht daneben, ich möchte schließlich "guten Stil" praktizieren, sodass für andere Programmierer mein Code möglichst gut lesbar ist!



  • Das sieht schon gut aus, geht als strikt konform durch, läuft somit auf jedem ANSI C Compiler (mit den schon genannten Codepage Einschränkungen), hat aber noch Mängel:
    - wichtig: dein Algorithmus fehlinterpretiert mehrere auseinanderfolgende whitespaces (wenn der Compiler nicht mehr meckert heißt das nicht, dass das Programm fehlerfrei ist, nicht technisch und erst recht nicht fachlich, deswegen sind Testfälle mit vielen Testdaten nötig)
    - wegen Windows-Spezifika sollte man auch Textfiles mit fopen(.."rb") lesen
    - der Aufruf deines Programms sollte flexibel auch mit stdin klarkommen, etwa mit dem üblichen "-" als Argument

    program - < file.txt
    

    bzw. mit Pipe

    echo "bla fasel" | program -
    

    und dann etwa

    fp = strcmp(argv[1],"-") ? fopen(argv[1],"rb") : stdin;
    

    Die Funktionen aus ctype sollten für int-Parameter wegen der char/unsigned char/signed char-Problematik immer (unsigned char) gecastet werden, also

    isspace((unsigned char)ch)
    

    - deine {} Einrückungen sollten einheitlich sein

    Und da du brav meine Hinweise berücksichtigt hast gibts hier einen Bonus:

    statt selber mit isspace rumzufrickeln kann das in deinem Fall auch fscanf übernehmen, das bei richtigem Gebrauch die whitespace-Auflösung überimmt (und vor allen Dingen sich dabei nie verrechnet)

    void count_word_lengths(FILE *fp, unsigned numWordsOfLen[], unsigned maxLen)
    {
    #if 0
        int ch;
        unsigned len = 0;
        while ((ch = fgetc(fp)) != EOF) {
            if (isspace(ch) && len) {
                count_length(numWordsOfLen, len, maxLen);
                len = 0;
            } else
                ++len;
        }
        /* handle last word in file, if it was directly followed by EOF */
        if (len) count_length(numWordsOfLen, len, maxLen);
    #else
        char s[1000];
        while( EOF!=fscanf(fp,"%999s",s) )
            count_length(numWordsOfLen, strlen(s), maxLen);
    #endif
    }
    


  • Hallo,

    danke, dass Du nochmals so detailliert auf meinen Code eingehst, das ist super hilfreich.

    Ich habe tatsächlich diesen blöden whitespace bug eingebaut. Liegt wohl an der if-Abfrage, welche selbst bei whitespace in das else springt, da die zweite Bedingung (len > 0) fehlschlägt, und somit eigentlich geschachtelt stattfinden sollte. Wobei fscanf hier wirklich viel simpler ist.

    Was "r" vs "rb" angeht habe ich versucht etwas nachzulesen, scheint also das "r" die text files so "umformt", damit Dinge wie \r\n auf Windows trotzdem als \n im Programm erscheinen. Hat es damit etwas zu tun? Wenn ja, inwiefern kann das einem zum Verhängnis werden, wenn man nicht "rb" verwendet (windows)? Ich interessiere mich einfach nur dafür.

    Genauso würde mich interessieren, weshalb die ctype Funktionen einen unsigned char nehmen sollten (ist es damit der Wert nie größer sein wird als der größte Wert, welcher in einen uchar passt, weil dann etwas schlimmes passieren könnte?)

    Das stdin schon ein "vordefinierter" filepointer zum stanard input ist wusste ich nicht, und war genau das was mir fehlte ("Wie kann ich stdin und Dateien im selben Stil handhaben"). Ich habe mich entschieden entweder "-" als Argument, oder gar kein Argument für stdin zu akzeptieren.

    Zum erleichterten Ablesen wird bei Balken, die innerhalb des Angezeigten Bereiches liegen, noch die Zahl darüber angezeigt.

    Ob eine Ausgabe als Tabelle hier nicht viel einfacher und praktischer wäre, sei mal dahingestellt. Ich habe mich halt nach der Aufgabenstellung gerichtet.

    Stellt sich noch die Frage, wie schlimm ein fclose(stdin) wäre, habe kurz gegoogelt und es wird in diesem Fall wohl nicht schlimm sein.

    Ich habe versucht das mit den {} und der Einrückung zu verbessern. Ich habe mich entschieden das öffnende { immer in eine eigene Zeile zu packen. Ich finde das gibt allem mehr Luft und ist leichter zu lesen.

    Jetziges Programm:

    #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LEN     100 /* maximal word length */
    #define MAX_COUNT   99
    
    #define EXTENDED_ASCII
    
    #ifdef EXTENDED_ASCII
    #define CORNER      192 /* Bottom left corner of histogram */
    #define BAR         219 /* segment for graph */
    #define TOP_BAR     223 /* top half of graph segment */
    #define BOTTOM_BAR  220 /* bottom half of graph segment */
    #define BAR_WIDTH   3   /* # of characters used for displaying one length count */
    #define VERT_LINE   180 /* vertical line for y axis */
    #define VERT_DASH   180 /* display dash on y axis */
    #define HORI_LINE   196 /* Horizontal line for x axis */
    #define HORI_DASH   194 /* display dash in x axis */
    #else
    #define CORNER      '+'
    #define BAR         '#'
    #define TOP_BAR     '#'
    #define BOTTOM_BAR  '#'
    #define BAR_WIDTH   3
    #define VERT_LINE   '|'
    #define VERT_DASH   '+'
    #define HORI_LINE   '-'
    #define HORI_DASH   '+'
    #endif
    
    #define GRAPH_OFFST 9 /* # of characters (hori) before first graph begins */
    #define Y_STEP 5
    #define Y_AXIS_MIN_HEIGHT 5
    
    void count_word_lengths(FILE *fp, unsigned numWordsOfLen[], unsigned maxLen);
    void count_length(unsigned numWordsOfLen[], unsigned len, unsigned maxLen);
    
    void output_histogram(unsigned arr[], unsigned len);
    void output_header(unsigned numLengths, const char* title);
    void output_graph(unsigned arr[], unsigned len);
    void output_y_axis_segment(unsigned y, unsigned step);
    void output_graph_segment(unsigned arr[], unsigned len, unsigned currentY);
    void output_x_axis(unsigned arr[], unsigned len);
    
    unsigned find_max(unsigned arr[], unsigned len);
    void char_line(int ch, unsigned length);
    
    int main(int argc, char *argv[])
    {
        unsigned numWordsOfLen[MAX_LEN] = {0};  /* index = length, value = count */
        FILE *fp;
    
    	if (argc > 2) 
        {
    		printf("Error: Expecting not more than one argument (filepath)\n");
    		return 1;
    	} 
        else if (argc == 2 && strcmp(argv[1], "-")) 
        {
            if ((fp = fopen(argv[1], "rb")) == NULL) 
            {
        		printf("Unable to open file %s\n", argv[1]);
        		return 2;
            }
        } 
        else 
        {
            fp = stdin;
        }
    
        count_word_lengths(fp, numWordsOfLen, MAX_LEN);
        output_histogram(numWordsOfLen, MAX_LEN);
        fclose(fp);
        return 0;
    }
    
    void count_word_lengths(FILE *fp, unsigned numWordsOfLen[], unsigned maxLen)
    {
    #if 0
        int ch;
        unsigned len = 0;
        while ((ch = fgetc(fp)) != EOF) 
        {
            if (isspace((unsigned char) ch)) 
            {
                if (len) 
                {
                    count_length(numWordsOfLen, len, maxLen);
                    len = 0;
                }
            } 
            else 
            {
                ++len;
            }
        }
        /* handle last word in file, if it was directly followed by EOF */
        if (len) count_length(numWordsOfLen, len, maxLen);
    #else
        char s[1000];
        while (fscanf(fp, "%999s", s) != EOF) 
        {
            count_length(numWordsOfLen, strlen(s), maxLen);
        }
    #endif
    }
    
    void count_length(unsigned numWordsOfLen[], unsigned len, unsigned maxLen)
    {  /* assumes val > 0 */
        if (len <= maxLen) 
        {
            /* 
             * 0 will never be used, so we can read up to maxLen char words 
             * (versus up to maxLen-1)
             */
            ++numWordsOfLen[len - 1]; 
        } 
        else 
        {
            printf("Word found exceeding max length (%d).\n"
                   "It will not be included in the histogram\n", maxLen);
        }
    }
    
    void output_histogram(unsigned arr[], unsigned len)
    {
        unsigned i = 0, numLengths = 0;
        int exceededMaxCount = 0;
        unsigned outputLen = 0;
    
        for (i = 0; i != len; ++i) 
        {
            if (arr[i]) 
            {
                ++numLengths;
            }
        }
        outputLen = numLengths * BAR_WIDTH + GRAPH_OFFST;
    
        output_header(outputLen, "Histogram");
        output_graph(arr, len);
    
        char_line(BOTTOM_BAR, outputLen);
        /* output counts greater than MAX_COUNT which where cut-off */
        for (i = 0; i != len; ++i) 
        {
            if (arr[i] > MAX_COUNT) 
            {
                printf("Total amount of words with length %d is: %d\n", i + 1, arr[i]);
                exceededMaxCount = 1;
            }
        }
        if (exceededMaxCount) 
        {
            char_line(BOTTOM_BAR, outputLen);
        }
    }
    
    void output_header(unsigned width, const char* title)
    {
        unsigned i;
        char_line(TOP_BAR, width);
    
        /* display the title aligned in the center */
        for (i = 0; i != (width - strlen(title)) / 2; ++i) 
        {
            putchar(' ');
        }
        printf("%s\n", title);
    
        char_line(TOP_BAR, width);
    }
    
    void output_graph(unsigned arr[], unsigned len)
    {
        unsigned i = 0;
        unsigned maxCount = find_max(arr, len);
        if (maxCount < Y_AXIS_MIN_HEIGHT) 
        {
            maxCount = Y_AXIS_MIN_HEIGHT;
        }
        else if (maxCount > MAX_COUNT) 
        {
            maxCount = MAX_COUNT;
        }
    
        printf(" count\n");
        for (i = maxCount; i != 0; --i) 
        {
            output_y_axis_segment(i, Y_STEP);
            output_graph_segment(arr, len, i);
            putchar('\n');
        }
        output_x_axis(arr, len);
    }
    
    void output_y_axis_segment(unsigned y, unsigned step)
    {
        if (y % step == 0) 
        {
            printf("%2d %c", y, VERT_DASH);
        }
        else 
        {
            printf("   %c", VERT_LINE);
        }
    }
    
    void output_graph_segment(unsigned arr[], unsigned len, unsigned currentY)
    {
        unsigned i;
        for (i = 0; i != len; ++i) 
        {
            if (arr[i]) 
            {
                if (arr[i] + 1 == currentY) 
                {
                    printf("%2d ", currentY - 1);
                }
                else if (arr[i] == currentY) 
                {
                    printf("%c%c ", BOTTOM_BAR, BOTTOM_BAR);
                }
                else if (arr[i] > currentY) 
                {
                    printf("%c%c ", BAR, BAR);
                }
                else 
                {
                    printf("   ");
                }
            }
        }
    }
    
    void output_x_axis(unsigned arr[], unsigned len)
    {
        unsigned i;
        printf("   %c", CORNER);
        for (i = 0; i != len; ++i) 
        {
            if (arr[i]) 
            {
                printf("%c%c%c", HORI_LINE, HORI_DASH, HORI_LINE);
            }
        }
        printf("> len\n    ");
        for (i = 0; i != len; ++i) 
        {
            if (arr[i]) 
            {
                printf("%2d ", i + 1);
            }
        }
        putchar('\n');
    }
    
    unsigned find_max(unsigned arr[], unsigned len)
    {
        unsigned max = 0;
        while (len--) 
        {
            if (arr[len] > max) 
            {
                max = arr[len];
            }
        }
        return max;
    }
    
    void char_line(int ch, unsigned len)
    {
        while (len--) 
        {
            putchar(ch);
        }
        putchar('\n');
    }
    

    Nochmals vielen Dank für die Tips.

    LG



  • - fclose(stdin) ist kein Problem, solange du danach im selben Prozess nicht mehr darauf zugreifen möchtest (jeder Prozess erhält beim Start einen eigenen Satz von stdin/stdout/stderr mit dem er machen kann was er will und niemals andere Prozesse dabei stören wird)

    - ctype.h und (unsigned char)-Cast
    der Standard gebietet UB falls du es nicht machst
    http://www.iso-9899.info/n1570.html#7.4

    - 0x1a/EOF und Windows-Textstreams
    https://www.c-plusplus.net/forum/p2432245?sid=0a19f92e2e3a36293364aec367585843#2432245
    http://stackoverflow.com/questions/11717120/unexpected-return-value-from-fread/11717405#11717405

    In einer richtigen Anwendung gehören dann all deine Funktionen außer main in ein (oder mehrere) Modul, und nur die wirklich nach außen sichtbaren (count_word_lengths,output_histogram) in die Headerdatei, die anderen werden mit static im C-Modul unsichtbar gemacht.

    MAX_LEN ist wenig signifikant, könnte sein, dass sowas in größeren Programmen kollidiert was dann nicht sofort auffällt, da hier der Präprozessor und nicht der Compiler selbst zuständig ist und naturgemäß weniger warnt.
    Deshalb: ruhig etwas längere Namen wählen oder auch deutsche, das macht Kollisionen weniger wahrscheinlich, insbesondere wenn du deinen Code in fremden Programmen als Library wiederverwenden willst.



  • Hallo,

    danke für die Links etc. ist mir jetzt einleuchtend. MAX_LEN ist in der Tat sehr generisch. An sowas hatte ich bisher garnicht gedacht (C++ Hintergrund).

    Ist das mit dem Modul allgemein dann z.B. so gemeint (nur zum Verständnis)?

    // main.c
    #include "histogram.h"
    // use output_histogram
    
    // histogram.h
    void output_histogram(unsigned arr[], unsigned len);
    static void output_header(unsigned numLengths, const char* title);
    static void output_graph(unsigned arr[], unsigned len);
    static void output_y_axis_segment(unsigned y, unsigned step);
    static void output_graph_segment(unsigned arr[], unsigned len, unsigned static currentY);
    static void output_x_axis(unsigned arr[], unsigned len);
    
    // histogram.c
    // implementierung
    


  • Also, man könnte natürlich auch mal überlegen, wie man denn sowas reusable machen könnte.

    Dann würde es sich anbieten, einen Histogram-Typen zu machen, in welchem du die Dinge wir die Bin-Inhalte, wie viele Bins etc. speicherst (ggf. auch noch Dinge wie Overflow-Bins, Mittelwerte etc.).

    // Header-Datei:
    Histogram *historgram_create(const char *title, int nBins, double from, double to);
    void histogram_fill(Histogram* h, double value, double weight = 1.);
    void histogram_draw(Histogram* h);
    void histogram_delete(Histogram* h);
    

    oder so ähnlich.

    Dann könntest du in deinem Programm ein Histogramm-Objekt erzeugen, die Datei einlesen und für jedes Wort einmal histogram_fill() aufrufen. Am Ende dann ein histogram_draw() und fertig.

    Wenn du nämlich, wie jetzt, deiner Funktion (unsigned arr[], unsigned len); übergeben musst, dann musst du dich z.B. um das Füllen von arr im Hauptprogramm kümmern - was, wenn du sehr viele und lange Worte hast und vielleicht Wortlängen [0..10) in das erste Bin, [10..20) ins zweite Bin usw. füllen willst? D.h. Binbreiten sind bei dir nicht variabel und die entsprechende Berechnung dazu müsstest du im main machen, was schlecht wäre.

    Andererseits natürlich die Frage, wie weit du mit dieser Aufgabe gehen willst.



  • Die klassische Vorgehensweise für dein Beispiel sieht so aus:

    main.c

    #include "histogram.h"
    
    int main(){...}
    

    histogram.h

    void count_word_lengths(FILE *fp, unsigned numWordsOfLen[], unsigned maxLen); 
    void output_histogram(unsigned arr[], unsigned len);
    

    histogram.c

    #include "histogram.h"
    
    /* Prototypen */
    void count_length(unsigned numWordsOfLen[], unsigned len, unsigned maxLen);
    void output_header(unsigned numLengths, const char* title);
    void output_graph(unsigned arr[], unsigned len);
    void output_y_axis_segment(unsigned y, unsigned step);
    void output_graph_segment(unsigned arr[], unsigned len, unsigned currentY);
    void output_x_axis(unsigned arr[], unsigned len);
    unsigned find_max(unsigned arr[], unsigned len);
    void char_line(int ch, unsigned length);
    
    /* Implementierungen */
    void count_word_lengths(FILE *fp, unsigned numWordsOfLen[], unsigned maxLen){...} 
    void output_histogram(unsigned arr[], unsigned len){...}
    
    static void count_length(unsigned numWordsOfLen[], unsigned len, unsigned maxLen){...}
    static void output_header(unsigned numLengths, const char* title){...}
    static void output_graph(unsigned arr[], unsigned len){...}
    static void output_y_axis_segment(unsigned y, unsigned step){...}
    static void output_graph_segment(unsigned arr[], unsigned len, unsigned currentY){...}
    static void output_x_axis(unsigned arr[], unsigned len){...}
    static unsigned find_max(unsigned arr[], unsigned len){...}
    static void char_line(int ch, unsigned length){...}
    

    Integer-basierte Konstanten (MAX_LEN) verwendet man professionell auch nicht als #define
    sondern als enum.
    Das hat den Vorteil, dass der Compiler bei Doppelverwendung (in anderen Modulen) immer einen Fehler wirft, während bei #define der Präprozessor eine Warnung werfen kann.
    Obendrein haben #defines immer global Scope und bei Doppelverwendung ist es zufällig, welcher der beiden Doppelbelegungen aus unterschiedlichen Modulen "gewinnt", da die Abarbeitungsreihenfolge der einzelnen Header/Module durch den Präprozessor nicht vom Standard vorgegeben ist.



  • Hallo,

    ersteinmal vielen Dank, dass weiterhin so viel Feedback kommt.

    @wob
    Ich habe nicht vor, eine komplette Histogramm-Lib zu entwickeln, mit der man jedes mögliche Histogramm in jedem möglichen Szenario handhaben kann. Es wäre aber schon nett, wenn das Modul so viel Flexibilität hat, dass ich z.B. die zweite Aufgabe ebenfalls damit lösen könnte, ohne großen Mehraufwand:

    Write a program to print a histogram of the frequencies of different characters in its input

    Da könnte ich ja ähnlich die char-codes in Array-indices übersetzten, und müsste dann halt irgendwie bei der Ausgabe die chars ausgeben können. Mein spontaner Gedanke wäre gewesen, irgendwie einen Format String als Parameter anzunehmen, welcher dann zur Ausgabe verwendet wird (also praktisch die Bin Beschriftung, z.B. "%2d" vs "%c"). Hier stellt sich natürlich die Frage, was passiert wenn man printf mit mehr Argumenten als Formats aufruft (wenn ich einen Bereich in Bins zulasse, kann ich ja immer low und high an printf übergeben und dann entweder "%d..%d" oder "%d" als format verwenden, wenn low == high).

    Ich möchte hier natürlich nicht, dass mir jemand das vorprogrammiert, da darf ich bitte erstmal selbst Knobeln. Wenn es sich hier anbietet die "Objektorientierung" in C anzuwenden, werde ich das natürlich gerne ausprobieren.

    @wutz
    Das Codebeispiel ist jetzt mehr als ich erwartet hatte, erklärt aber auch super was ich nicht ganz verstanden hatte (was nun in den Header kommt und wo das static hingehört). Ist ja schon etwas anders als bei C++ (da komme ich her). Das mit dem enum für die Konstanten finde ich eine gute Idee, werde ich auf jeden Fall machen.

    Ich meld mich wieder, wenn ich neuen Code haben sollte.

    LG

    P.S.: Ich wünschte ich hätte mich mehr für Deutsch interessiert, dann könnte ich mich hier sauberer ausdrücken 🙄



  • HarteWare schrieb:

    Wenn es sich hier anbietet die "Objektorientierung" in C anzuwenden, werde ich das natürlich gerne ausprobieren.

    c ist eine prozedurale programmiersprache, das heißt start -> mach was -> ende.

    wenn du objektorientiert programmieren willst, solltest du vielleicht lieber c++ oder java verwenden.


  • Mod

    Quark. Jedes bekannte C-Programm ist objektorientiert geschrieben.

    start -> mach was -> ende.

    Das hat so was von überhaupt nichts mit prozedural vs. objektorientiert zu tun.



  • also das, was ich so über oop gelernt habe, kann man unter c (im gegensatz zu c++, java usw) wenn überhaupt nur mit großem aufwand umsetzen, dafür kann man aber sehr schön den ablauf innerhalb der hardware nachbilden und die hardware läuft doch prozedural, oder nicht?


  • Mod

    Vielleicht hast du falsches gelernt? Deine Vorstellungen sind jedenfalls total wirr. OOP und Prozdural haben nichts Hardware zu tun, das sind philosophische Arten und Weisen, wie man über Daten und Abläufe nachdenkt.



  • dass man prinzipiell mit jeder programmiersprache alles programmieren kann, ist mir schon klar. soll ja auch jeder machen, wie er will.

    trotzdem bleibe ich dabei, dass - zumindest im rahmen meines beschränkten horizonts - der funktionsumfang von c besser für die prozedurale programmierung geeignet ist und dass es daher unsinn ist, mit c irgendwelche objekte zusammen zu basteln, wenn es andere sprachen gibt, die das alles viel besser und bequemer können.



  • Aha. Sind für dich die wichtigsten Merkmale von OOP also die Existenz der "class"- und "virtual"-Keywords?

    Warum ist Code wie folgender für dich nicht OOP

    GRand *rand = g_rand_new_with_seed(0);
    int value = g_rand_int_range(rand, 0, 42);
    g_rand_free(rand);
    

    Während es folgender aber wäre?

    GRand *rand = new GRand(0);
    int value = rand.int_range(rand, 0, 42);
    delete rand;
    


  • Oder ein Beispiel aus der stdlib:

    File* f = fopen('foo');
    fwrite(f, ...);
    fclose(f)
    

    Das ist OO pur!



  • g_rand_int_range(rand, 0, 42);
    

    die obige funktion ist global und bekommt eine (beliebige) instanz einer datenstruktur übergeben.

    rand.int_range(rand, 0, 42);
    

    die obige funktion ist fest an die instanz eines (bestimmten) objektes gebunden und damit eigentlich eine methode. warum das objekt sich selbst übergeben bekommt, weiß ich allerdings nicht. tippfehler?

    SG1 schrieb:

    Oder ein Beispiel aus der stdlib:

    File* f = fopen('foo');
    fwrite(f, ...);
    fclose(f)
    

    Das ist OO pur!

    oo pur wäre f.write();


  • Mod

    Dann hast du das Konzept wirklich nicht verstanden. Die gezeigten Codes sind jedenfalls tatsächlich OO pur. OO hat nichts mit Punkten in der Syntax zu tun. Ein anderer Unterschied besteht zwischen dem C und dem C++-Code aber nicht.

    Oder anders gesagt, in C++ (doer jeder anderen Sprache, die du als oo bezeichnen würdest) ist f.write("Bla") nur Syntaxzucker für write(f, "Bla") .



  • ne mit punkten nicht, aber damit, dass funktionen bei oop in ein objekt gekapselt werden sollen, damit das alles schön übersichtlich ist und man weiß, dass f.write() eine methode der durch f instanziierten klasse File ist und daher nicht zig dateien durchsucht werden müssen, wenn man da irgendwas dran ändern will.

    den vorzug hast du bei write(f,...) nicht.


  • Mod

    So? Was ist denn anders? Der erste Parameter von fwrite nimmt schließlich einen FILE* entgegen, weil fwrite eine Methode der Klasse FILE ist. Erinnert dich das vielleicht an etwas? *hust*this*hust*



  • Am Ende des Tages wird doch eh alles (völlig Hunz ob C, C++, Python, Haskell, oder Brainfuck) in die Maschinensprache des Computers übersetzt, denn das ist das einzige was er versteht, alles andere ist nur für den Menschen (ausgenommen letzteres). Jedes Programm ist start -> tu was -> stop.

    Ein Objekt bestimmt meiner Ansicht nach eine Menge von Daten (Also irgendwelche Bytes im Speicher) und auf diese Daten ist eine Menge möglicher Operationen definiert. Wie das jetzt konkret aussieht, ist doch wurscht.


Anmelden zum Antworten