C++ Text in Struct Speichern

#1
Liebes Forum,

Zur Übung mochte ich folgendes Beispiel lösen. Ich Programmiere noch nicht sehr lage, somit Fällt es mir schwer den "Anfang" zu finden.

Mir ist klar das ich mit "int *TextSammlung = new int(n);" den Speicher für n Texte reserviere.
Jedoch scheitere ich am Einlesen der Texte ohne des C++ Strings. Ich bin auch überfordert beim befüllen der Datenstrucktur.

Da ich bei der bevorstehenden Prüfung Beispiele dieser Art lösen muss bin ich für jede Hilfe dankbar. Für das 2 Semester Elektronik erscheint mir dieses Beispiel auch ziemlich schwierig.

Zum Beispiel:

Schreiben Sie ein Programm, das einen positiven ganzzahligen Wert n ≥ 6 einliest. Anschließend reservieren Sie Speicher für ein Array TextSammlung von n Pointern auf die folgende Datenstruktur:
struct TextBlock
{
char * Text;
size_t Laenge;
};

Lesen Sie nun n Texte beliebiger Längen ein (Leerzeichen in den Texten sollen erlaubt sein), und befüllen Sie die Datenstruktur entsprechend mit dem Text und seiner Länge (Vergessen Sie nicht, sowohl für den Text als auch für die Struktur selbst Speicher zu allokieren!). Geben Sie dann jeden Text und seine Länge mit cout übersichtlich formatiert aus. Vertauschen Sie im Anschluss die ersten 3 mit den letzten 3 Texten mit Hilfe einer geeigneten Funktion, die in einem eigenen Sourcemodul definiert ist und geben Sie das Ergebnis erneut aus. Achten Sie auf korrekte Definitionen und Deklarationen aller notwendigen Elemente.

Randbedingungen:
1) Die Verwendung der C++-Klasse string ist nicht erlaubt.
2) Verwenden Sie nur new zur Speicherallokation, nicht malloc()!
3) Vertauschen heißt nicht Kopieren...
 

cwriter

Erfahrenes Mitglied
#2
Jedoch scheitere ich am Einlesen der Texte ohne des C++ Strings. Ich bin auch überfordert beim befüllen der Datenstrucktur.
Zum Einlesen von Text bietet sich die C-Funktion scanf() an; du kannst aber auch std::cin in einer Schleife oder einfach std::getline() verwenden.

Befüllen der Datenstruktur: Wo ist das Problem?
C++:
struct TextBlock {
   char* text;
   size_t len;
}

// Einfaches Befüllen
TextBlock* tb = new TextBlock();
assert(tb); // Checks einbauen :) 
tb->text = nullptr;
tb->len = 0;
Ansonsten möchte ich dich gerne dazu ermuntern, genauer zu erläutern, was dir unklar ist. Das hilft dir gleich doppelt: Du siehst selbst, wo deine Verständnisprobleme sind, und wir können zielgenauer und besser helfen.

Gruss
cwriter
 
#3
Danke für die schnelle Antwort.

Die einzelnen Programmteile verstehe ich einigermaßen, jedoch ist das zusammensetzen zu einem ganzen funktionierenden Programm schwierig für mich.

Ich hab leider wenige Funktionierende Beispiele, sondern nur einzellne "fetzen" von Programmen.

Beim befüllen der Datenstrucktur blicke ich nicht durch bei den Pointern. Für mich ist unklar was auf was zeigt und an welcher Stelle ich derefernzieren muss.
 

cwriter

Erfahrenes Mitglied
#4
Ich hab leider wenige Funktionierende Beispiele, sondern nur einzellne "fetzen" von Programmen.
Magst du uns deine bisherige Arbeit zeigen?

Beim befüllen der Datenstrucktur blicke ich nicht durch bei den Pointern. Für mich ist unklar was auf was zeigt und an welcher Stelle ich derefernzieren muss.
Zu den Pointern: Vielleicht hilft dir dieses Beispiel:

C++:
TextBlock tb_stack; // Erstelle auf Stack
TextBlock* tb_heap = new TextBlock(); // Erstelle auf Heap

// Wir wissen, dass tb_stack auf dem Stack liegt, und dass es eigentlich so aussieht:
struct {
    char* text;
    size_t len;
} tb_stack;
// (Das folgt aus der Definition)
// Nun haben wir ganz einfach, dass das "text"-Element im tb_stack ist, daher:
tb_stack.text = x;

// tb_stack.text ist aber selbst ein Pointer. D.h. in tb_stack speicherst du einen Verweis auf einen anderen Speicherbereich.

// Bei tb_heap ist es ein bisschen anders:  Du hast einen Pointer auf einen Speicherbereich, an dem ein TextBlock-Objekt liegt.
// Also geht das nicht:
tb_heap.text; // Error, tb_heap ist ein Pointertyp und Pointer haben keine Member.

// Aber wir können Dereferenzieren, also den Speicherbereich anschauen, wo das Objekt tatsächlich liegt:
(*tb_heap).text; // Ok, sofern tb_heap gültig ist.

// Es ist aber recht mühsam, immer (*tb_heap).text zu schreiben. Daher gibt es die Kurznotation:
tb_heap->text; //Dasselbe wie (*tb_heap).text
Bei Pointern in einem Struct gilt immer zu bedenken: Die Pointer werden kopiert, aber nicht die Speicherbereiche dahinter.

C++:
TextBlock tb1;
TextBlock tb2;

tb1.text = new char[16];
strcpy(tb1.text, "Hello");
tb2 = tb1;

std::cout << tb1.text << " | " << tb2.text; // Gibt 2x "Hello" aus.
strcpy(tb1.text, "Bye");
std::cout << tb1.text << " | " << tb2.text; // Gibt 2x Bye aus
tb1.text = nulllptr;
std::cout << tb1.text << " | " << tb2.text; // Gibt null | Bye aus
Anders dazu kopieren Pointer gar nichts, man hat "Aliasing":

C++:
TextBlock* tb1 = new TextBlock();
TextBlock* tb2;

tb1->text = new char[16];
strcpy(tb1->text, "Hello");
tb2 = tb1;

std::cout << tb1->text << " | " << tb2->text; // Gibt 2x "Hello" aus.
strcpy(tb1.text, "Bye");
std::cout << tb1->text << " | " << tb2->text; // Gibt 2x Bye aus
tb1.text = nulllptr;
std::cout << tb1->text << " | " << tb2->text; // Gibt null | null aus
(Vergessen Sie nicht, sowohl für den Text als auch für die Struktur selbst Speicher zu allokieren!)
Das ist genau das im 2. Beispiel: Zuerst new für tb1, dann new für tb1->text.

Dereferenzieren musst du in diesem Beispiel nur die TextBlocks selbst, für TextBlock::text solltest du existierende Funktionen nutzen (strcpy, strdup, strlen, etc.)

Weisst du etwas darüber, wie ihr einlesen sollt? Ohne std::string gibt es da eigentlich fast nur die C-Alternativen, und std::sstream dürft ihr wohl auch nicht verwenden, oder?

Gruss
cwriter
 
Zuletzt bearbeitet:
#5
Ich glaube mich zu erinnern das wir fgets() verwenden sollen.

Danke für die Hilfe das hat auf jeden Fall zum Vertändnis beigetragen.
Wäre es zu viel verlangt dich zu bitten das gesamte Programm zu schreiben? Das würde mir wirklich helfen.
Das Beispiel beinhaltet einige funktionen und Zusammenhänge die in der Volesung nicht behandelt wurden.

Ich verstehe es besser wenn ich eine Lösung zum nachvolziehen habe.

Die vorhin erwähnten "fetzen" stammen nicht von mir, sondern sind Teil der Vorlesung

Danke für die Mühe
 

cwriter

Erfahrenes Mitglied
#6
Wäre es zu viel verlangt dich zu bitten das gesamte Programm zu schreiben? Das würde mir wirklich helfen.
Normalerweise gibt es hier Hilfe zur Selbsthilfe.

C++:
int main(int argc, char* argv[])
{
    unsigned int count = 0;
    std::cin >> count;

    TextBlock** arr = new TextBlock*[count];
    if(arr == nullptr) return -1;

    for(size_t i = 0; i < count; ++i)
    {
        arr[i] = new TextBlock();
        char* tmp = nullptr;
        scanf("%m[^\n]", &tmp); // Non-Standard Posix extension: %ms erstellt direkt einen string; intern ist das malloc, aber hier nicht exposed; das [^\n] ist billiges Regex einzelne Zeilen zu lesen. Ein TODO für dich zum Umschreiben mit fgets.
        arr[i]->text = tmp;
        arr[i]->len = strlen(tmp);
    }
    for(size_t i = 0; i < count; ++i)
    {
        std::cout << std::setw(5) << arr[i]->len << ": " << arr[i]->text << std::endl;
    }
 
    // Vertauschen überlasse ich dir. Modul heisst: Header/Source-Paar.
    for(size_t i = 0; i < count; ++i) delete arr[i]; // Leakt die Strings, ausser, man verwendet TextBlockMod.
    delete[] arr;
}

struct TextBlockMod : public TextBlock {
    TextBlockMod() { text = nullptr; len = 0; }
    ~TextBlockMod() { free(text); }
};
Das sollte dir nicht die ganze Aufgabe vorwegnehmen, aber eine gute Basis bilden. Ungestestet.

Gruss
cwriter
 
Zuletzt bearbeitet: