Modularisierung
-
Hallo,
ich bin neuer C-User und würde gerne einige generelle Fragen stellen, um ein C/C++-Projekt zu schreiben.
Ich möchte ein Programm schreiben, dass eine Kommando-Datei parst und anschließend Textdaten (ASCII) einließt. Diese sollen von dem Programm manipuliert und letzlich wieder herausgeschrieben werden. Prinzipiell sind das ganz einfache Operationen.
1. Ich arbeite unter LINUX mit gcc/g++. Wie stelle ich sicher, dass mein Programm in C und nicht in C++ geschrieben ist? Was wäre in meinem Fall sinnvoll?
2. Muss man für C bestimmte String-Parse-Funktionen (Suchwort-Filtern, Leerzeichen eliminieren, ...) selber schreiben?
Beispiel für die Kommando-Datei:BEGIN.BLADE .PARAM1 = TRUE .PARAM2 = [14, 15 16] .PARAM3 = 3.14152 END
3. Kann mir jemand eine Quelle nennen/empfehlen, wo man sich solche Parser abgucken kann?
4. Ich würde das Programm sehr gerne modular gestalten. Wie sollte ich Funktionen und Datentypen in einzelnen Dateien zusammenfassen? Stehen in Header-Datei ausschließlich die Definitionen einer Funktion bzw. Konstanten?
Vielen Dank für die Hilfe
-
Hallo, da hast du dir was vorgenommen, auch wenn es am Anfang gar nicht so komplex aussieht.
-
Bin kein Linux Spezi ... keine Ahnung. Glaube aber gcc kompiliert ausschließlich reines C.
-
Ja. Um z.B. alle Whitespaces zu ignorieren mußte dir selber ne Funktion schreiben, die diese Zeichen in einem Buffer überspringen. In C mußt du alles was mit Stringerkennung in abstrakterem Sinne zu tun hat, selber schreiben.
-
Nein. Keine guten, wo man mal eben sich schnell was abschauen kann. Parser sind recht Komplex. Es ist aber sehr lehrreich sich sowas selber zu schreiben.
-
Kleiner Überblick: du parst sowas in 2 Schritten. Erst geht ein Lexer drüber und dann dein Parser, der das syntaktisch prüft.
Der Lexer zerhackt dir den Text in kleine Teile, die je nach Spezifikation der Sprache die kleinsten Teile darstellt. Dabei benutzt er Symboltables.
Der Parser holt sich dann vom Lexer diese Atome und schaut, ob sie in dem gegebenen Zusammenhang syntaktisch zusammenpassen.
In deinem Fall bietet sieht das dann vielleicht so aus:
Eine große Symboltabelle mit dem folgenden (Pseudocode)Progammsymbol -> internes Symbol BEGIN -> beginsym END -> endsym = -> identsym [ -> lbracketsym ] -> rbracketsym "zahl" -> constsym (u.s.w)
Der LExer geht nun rüber und zerlegt den Text in genau diese interne Representation der Symbole. "zahl" und Variablen sowie Prozedurnamen und co werden in eine andere Tabelle geschrieben, als Nachschlagewek. Der Lexer stellt dabei die Funktion getNextSym() zur Verfügung, die das jweils nächste Symhol zurückliefert.
Der Parser meistens eine Baumstruktur, fragt nun nach und nach getNextSym ab und guckt, ob alles OK ist. Z.B. muß auf ein beginsym definitiv irgendwann ein endsym folgen usw. Dabei abstrahiert der Parser und bringt das ganze in eine Art pseudo Anweisungssprache für deinen Computer. Hat der Parser keine Einwände so kannst du aus deinem pseudo Anweisungscode Assemblercode machen, der dann von einem Assembler in Computersprache übersetzt wird.
Solltest du wirklich ein Neuling in C sein, würde ich dir davon abraten, sowas selber zu probieren. Hast du aber ein wenig Geduld und es ist dir auch egal, daß das ein wenig dauern kann. Gibts vermutlich keinen besseren Weg eine Sprache zu lernen.
Edit zu 4) in header dateien stehen die Funktionisprototypen und Deklarationen/DEfinitionition die für andere Dateien notwendig sind um mit dem bestimmten Modul arbeiten zu können. So ungefähr wie das was man bei einer Klasse public machen würde. alles was privat ist, steht in deinem Source file und nicht dem Header.
-
-
Ich geb dir mal was zum Starten. Ist nicht besonders hübsch anzusehen, aber das Beste was ich auf die Schnelle zusammenschustern konnte. Es ist der Lexer für deine Sprache:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> enum symbol {nulsym, beginsym, endsym, identsym, lbracketsym, rbracketsym, constsym, commasym, dotsym, varsym, procsym, NUM_SYMS}; // table of reserved words #define NUMRESERVED 3 static char* reserved[] = {"BEGIN", "END", "TRUE"}; int main(void) { char* nextchar; int i, j, k, quotient; float num = 0; char word[256]; char buffer[] = "BEGIN.BLADE \n" " .PARAM1 = TRUE \n" " .PARAM2 = [14, 15 16]\n" " .PARAM3 = 3.14152 \n" "END \n"; for(nextchar = buffer; *nextchar != '\0'; nextchar++) { if(isalpha(*nextchar)) { for(i = 0; isalpha(*nextchar) && *nextchar != '\0'; i++, nextchar++) { word[i] = *nextchar; } word[i] = '\0'; // TODO: check if the following character is a number // binary search reserved words i = 0; j = NUMRESERVED - 1; do { k = (i + j) / 2; if(stricmp(word, reserved[k]) <= 0) j = k - 1; if(stricmp(word, reserved[k]) >= 0) i = k + 1; } while(i <= j); if(i - 1 > j) { // found reserved word printf("reserved word found: %s\n", word); } else { // no reserved word, must be identifier printf("identifier found: %s\n", word); } nextchar--; } else if(isdigit(*nextchar)) { for(; isdigit(*nextchar) && *nextchar != '\0'; nextchar++) { num = num * 10 + (*nextchar - '0'); } // catch floating points if(*nextchar == '.') { quotient = 1; for(nextchar++; isdigit(*nextchar) && *nextchar != '\0'; nextchar++) { num = num * 10 + (*nextchar - '0'); quotient *= 10; } num = num/quotient; } printf("symbol found: %f constsym\n", num); num = 0; nextchar--; } else { // catch all the other symbols that are not numbers, not identifiers and // not reserved words switch(*nextchar) { case '=': printf("symbol found: '=' identsym\n"); break; case ',': printf("symbol found: ',' commasym\n"); break; case '.': printf("symbol found: '.' dotsym\n"); break; case '[': printf("symbol found: '[' lbracketsym\n"); break; case ']': printf("symbol found: ']' rbracketsym\n"); break; } } } system("pause"); return 0; }
-
NDEBUG schrieb:
- Bin kein Linux Spezi ... keine Ahnung. Glaube aber gcc kompiliert ausschließlich reines C.
GCC stand mal für "GNU C Compiler", heute steht es auch für "GNU Compiler Collection" zu dem diverse Compiler gehören, wie der g++ für C++, der GPC für Pascal oder GNAT für Ada.
Sicherstellen, dass dein Programm in C geschrieben ist, kannst du, indem du es in C schreibst. Überprüfen ob es in einem der C-Standards geschrieben ist, kannst du über Compilerflags.
http://gcc.gnu.org/onlinedocs/gcc-4.3.3/gcc/Standards.html#Standards
-
Muss man für C bestimmte [...] Funktionen selber schreiben?
Generell musst Du das selber schreiben was noch kein anderer geschrieben und veroeffentlich hat; das ist heutzutage nicht mehr besonders viel.
Deine "Kommando-Datei" wuerde ich in XML formulieren und eine verfuegbare Lib zum parsen benutzen.
-
1. Ich arbeite unter LINUX mit gcc/g++. Wie stelle ich sicher, dass mein Programm in C und nicht in C++ geschrieben ist? Was wäre in meinem Fall sinnvoll?
gcc und nicht g++ aufrufen. Am besten mit
gcc -std=c99
.2. Muss man für C bestimmte String-Parse-Funktionen (Suchwort-Filtern, Leerzeichen eliminieren, ...) selber schreiben?
Leerzeichen elimieren: http://www.c-plusplus.net/forum/viewtopic-var-p-is-1629464.html
Suchwort-Filtern: man: string.h(7posix) könnte hilfreich seinStehen in Header-Datei ausschließlich die Definitionen einer Funktion bzw. Konstanten?
Normalweise ja (Funktions-Deklarationen), und Typ-Definitionen.
4. Ich würde das Programm sehr gerne modular gestalten. Wie sollte ich Funktionen und Datentypen in einzelnen Dateien zusammenfassen?
Wenn du globale Variablen, die keine Funktion sind, vor anderen Quelldateien verstecken willst, musst du
static
dazusagen. Das sollte imo default-mäßig so sein, ist es aber nicht.3. Kann mir jemand eine Quelle nennen/empfehlen, wo man sich solche Parser abgucken kann?
Wenn man sich den Code von NEDUG mal ansieht, ist das vielleicht keine so gute Idee. Ich würde das weiter aufstrukturieren, damit man's auch versteht. Allgemeines zu Parsern vielleicht hier:
http://en.wikipedia.org/wiki/Parsing
http://en.wikipedia.org/wiki/Parse_treeIn dem Artikel "Parsing" ist ein Bild im Abschnitt "Overview of process". Davon kann man sich alles nötige abschauen, denke ich.
-
Wenn man sich den Code von NEDUG mal ansieht, ist das vielleicht keine so gute Idee. Ich würde das weiter aufstrukturieren, damit man's auch versteht.
Hmm ich weiß was du meinst. Und das war nur der Lexer
Der Parser wäre noch mal ein Zacken schärfer. Zu meiner Verteidigung muß ich sagen, daß mein Code noch geht. Ich hab mich einige Zeit mit Compilerbau beschäftigt und muß sagen, daß Parser halt wirklich nicht einfach sind. Im Endeffekt sind sie alle sehr verwickelt. Die meisten benutzen aber eh einfach nur Lex/Yacc und fertig. Dabei sind selbstgebaute Sachen bei weitem schneller als das Duo. Ich bin mir auch noch nicht so ganz sicher, ob der Ersteller des Threads überhaupt eine solche Möglichkeit braucht. Er schreibt ja nicht weiter, was er eigentlich machen möchte. Er sagt "(...) Kommando-Datei parst und anschließend Textdaten (ASCII) einließt (...)" Was die geparste Kommando Datei im Endeffekt tun soll, wird nicht klar. Desewegen erstmal Statement abwarten und dann noch ma schaun. Für meinen unverständlichen, wenig kommentierten Code entschuldige ich mich auf jeden Fall. Hatte nicht mehr Zeit.
-
Hallo,
ich möchte noch eine Frage im Kontext Modularisierung stellen.
In dem Programm, welches ich schreiben möchte, greife ich auf mehrere Dateien zu.
Um den einzelnen Routinen nicht immer gesamte Dateinamen und Pfade zu übergeben bzw. zusammenbauen zu müssen, wollte ich eine Funktion definieren, in welcher die erforderlichen Dateinamen inkl. Pfad in erzeugt werden.
Diese Strings sollen dann in Zeichenketten gespeichert werden.Frage:
# Ist dieses Vorgehen möglich/sinnvoll?
# Kann ich die Strings in den Headerdateien wie folgt definieren:globStrings.h
char cmdFilename[1024] = "";
# Ist es möglich/sinnvoll einen char-Pointer auf das erste Zeichen von cmdFilename zeigen zu lassen und auf diese Weise in andere Routinen zu übergeben?
char *fPtr; fPtr = &cmdFilename;
Besten Dank.
-
Phil270307 schrieb:
# Kann ich die Strings in den Headerdateien wie folgt definieren:
das lass sein, sonst versucht der compiler für jedes C-file, das compiliert wird, diese variable anzulegen. erzeuge globale variablen nur in einem C-file und in die .h schreibst du ein 'extern' davor.
// global.c int my_global; ...
// global.h extern int my_global; ...
Phil270307 schrieb:
# Ist es möglich/sinnvoll einen char-Pointer auf das erste Zeichen von cmdFilename zeigen zu lassen und auf diese Weise in andere Routinen zu übergeben?
klar, das wird sogar meistens so gemacht.
-
# Ist dieses Vorgehen möglich/sinnvoll?
Möglich jedenfalls, klingt auch sinnvoll.
Ist dann sinnvoll, wenn's nicht auch einfacher geht.
(zB mit relativen Pfaden, die man nicht zusammenbauen muss, oder so -- kann ich nicht wissen)# Kann ich die Strings in den Headerdateien wie folgt definieren: (...)
Ja.
# Ist es möglich/sinnvoll einen char-Pointer auf das erste Zeichen von cmdFilename zeigen zu lassen und auf diese Weise in andere Routinen zu übergeben?
Ja, aber:
char string[] = "12345"; char *pt; // dann entweder: pt = string; // oder: pt = &string[0]; // aber eher nicht: pt = &string;
EDIT:
fricky hat recht, lass mal lieber die Strings in den Headern (hatte ich überlesen).
-
bgdnoy schrieb:
// aber eher nicht:
pt = &string;sieht irgendwie blöd aus, geht aber trotzdem.
-
Ja, geht trotzdem, aber nur für string als Array und nicht für string als Zeiger auf char. Und das hat mich in grauer Vorzeit einmal sehr verwirrt...
-
bgdnoy schrieb:
Ja, geht trotzdem, aber nur für string als Array und nicht für string als Zeiger auf char.
ersteres ergibt einen char(*)[], letzteres einen char**. deshalb geht's mit arrays, obwohl der compiler meckert.
-
char cmdFilename[1024] = "";
Kann mir mal jemand expliziet beschreiben, was hier genau passiert ...
-
knivil schrieb:
char cmdFilename[1024] = "";
Kann mir mal jemand expliziet beschreiben, was hier genau passiert ...
das ist ein char-array mit 1024 elementen und das erste wird auf 0 gesetzt.
-
Hallo,
die Länge der String-Variable möchte ich über eine Konstante definieren, die in einer anderen Header-Datei liegt.
Wie wird denn diese Header-Datei in die Header-Datei inkludiert, in welcher der String definiert wird?In einem nächsten Schritt möchte ich gerne den aktuellen Pfad auslesen und dafür einen entsprechenden String zur Verfügung stellen. Wäre es möglich mir ein kurzes Beispiel zu geben, wie man
1. die erforderliche Länge des Strings für das Working-Directory bestimmt
2. diesen String dynamisch auspanntBesten Dank
-
Phil270307 schrieb:
die Länge der String-Variable möchte ich über eine Konstante definieren, die in einer anderen Header-Datei liegt.
Wie wird denn diese Header-Datei in die Header-Datei inkludiert, in welcher der String definiert wird?in die eine .h datei machste erstmal die konstante als #define rein. dann inkludierst du diese datei in jedem .c-file bevor du die datei mit der string-definition inkludierst. du könntest du auch die konstanten-h von der string-h selber inkludieren lassen. ach ja, und verwende 'inlcude guards', also sowas ähnliches:
#ifndef MY_CONSTANTS #define MY_CONSTANTS ... #define ... ... // hier alle konstanten rein ... #endif
^^das brauchste, damit mehrfaches includen nicht den compile-vorgang abbricht.
Phil270307 schrieb:
1. die erforderliche Länge des Strings für das Working-Directory bestimmt
unter windoofs gibt's die konstante MAX_PATH, aber ich weiss nicht, ob die brauchbar ist.
Phil270307 schrieb:
2. diesen String dynamisch auspannt
ausspannt oder aufspannt? wie auch immer, was meinst du damit?
-
ok, die Definition der Konstanten mit #define ist mir klar.
Die Strings, die ich für meine Dateipfade verwenden liegen in einer anderen Header-Datei und werden durch die Konstanten begrenzt.
konst.h
#define MAXSTR1 = 512
globStrings.h
#include "konst.h" char command[MAXSTR1] = "";
Aber das funktioniert noch nicht ....
Muss ich für die Definition des Strings command noch eine *.c-Datei anlegen?P.S.:
Meine Verzeichnisstrutkur sieht wie folgt aus:src/inc/.h
src/obj/.o
src/obj/Makefile
src/*.c
-
Hallo,
ich möchte gerne noch eine Frage stellen:
Ist es möglich zur Laufzeit eine Struktur oder auch ein Feld einer Struktur zu allokieren, die selbst ein Feld beinhaltet, das dynamisch augespannt werden kann?Bsp:
struct MyDataTyp { int A; float B; .... }
An der Stelle, die ich mit "..." markiert habe sollte nun noch ein int-Feld erzeugt werden, dass N Elemente besitzt. Wie genau geht das?
-
Phil270307 schrieb:
Hallo,
ich möchte gerne noch eine Frage stellen:
Ist es möglich zur Laufzeit eine Struktur oder auch ein Feld einer Struktur zu allokieren, die selbst ein Feld beinhaltet, das dynamisch augespannt werden kann?Si! Do you gucking here:
typedef struct { int n; // speichert die anzahl der dynamic_int elemente. int* dynamic_int; }DynamicStruct; DynamicStruct* dynamic_struct_factory ( int n ) { DynamicStruct* _new = malloc (sizeof(DynamicStruct)); if ( _new != NULL ) { if ( NULL ==(_new->dynamic_int = malloc ( n*sizeof(int)))) { free (_new); _new = NULL; } else _new->n = n; } return _new; } void dynamic_struct_free ( DynamicStruct* pds ) { free (pds->dynamic_int); free (pds); } int main() { int N = 100; DynamicStruct* pds = dynamic_struct_factory (N); if ( pds == NULL ) fprintf(stderr, "Shit happens: %s\n", strerror(errno)); // do something with it ... dynamic_struct_free (pds); // free memory. return 0; }
SEEYA
B.B. ( the bigger one )