C++ Call-by-referenz auf struct

EueRolando

Cadet 2nd Year
Registriert
Aug. 2014
Beiträge
29
Hallo,

ich bin c++-Anfänger und deshalb grade was typische Lösungen/Vorgenhensweisen betrifft nicht sicher.
Ich möchte eine (Bücher)Bibliotheksverwaltung realisieren dabei sollen Bücher im Bestand einer Bibliothek auf 3 verschiedene Weisen in eine "Datenbank" aufgenommen werde. 1. Ein Array mit fester Größe: Art A 2. Ein Array mit Variabler Größe: Art B 3. Eine einfach verkettete Liste (auch mit Variabler Größe) Art C
Ich schreibe dieses Programm mit 2 Modulen was im folgenden stark gekürzt so aussieht:

struct_ErzeugungArrays.h
Code:
//---------------------------------------------------------------------------
/*Diese Unit implementiert den zur Speicherung der Daten notwendigen struct
(alle drei Arten, A. B. und C.) nutzen diesen hier deklarierten struct*/
#include <stdio.h>
#include <iostream>	//für cout/cin
//---------------------------------------------------------------------------

struct sBuchEintrag{
    sBuchEintrag *next;
	char author[20];
	unsigned int kaufdatum;
};		  

sBuchEintrag *ArtA();
sBuchEintrag *ArtB();
sBuchEintrag *ArtC();

struct_ErzeugungArrays.cpp
Code:
#include "struct_ErzeugungArrays.h"
//---------------------------------------------------------------------------


sBuchEintrag *ArtA(){		//Methode erzeugt ein 5 Elemente Array von BuchEintrag
	sBuchEintrag arta[5];		//5 Elemente vom Typ sBuchEintrag
	return arta;
}

sBuchEintrag *ArtB(){
	sBuchEintrag *artb=NULL;	//Der Zeiger wird initialisiert, wird später auf das Array zeigen
	int anzahleinträge=0;       //die Variable vom Typ int wird initialisiert später benötigt um das Array mit variabler Größe zu erstellen

	std::cin>>anzahleinträge;
	artb=new sBuchEintrag[anzahleinträge];	//mit new wird das Array erzeugt, damit es unter dem Label artb angesprochen werden kann muss der Zeiger(artb) noch auf die erste Adresse zeigen

	return artb;
}

sBuchEintrag *ArtC(){
	sBuchEintrag *artc= NULL; //dieser Zeiger wird benötigt, um später auf den Anfang der Liste zu zeigen
	int anzahleinträge=0;	//wird wieder benötigt um das Array zu erstellen
	sBuchEintrag *anker=NULL;	//Zeiger der zu einem belibigen Element der Liste zeigt, um sich durch die Liste zu "hangeln"

	std::cin>>anzahleinträge;

	for(int i=1; i<=anzahleinträge; i++){
	if (i==1){
		artc = new sBuchEintrag;		//erzeugt das erste Element und lässt artc darauf zeigen
		anker = artc;
	}
	else{
		anker->next = new sBuchEintrag;
		anker = anker->next;
	}
	}

	return artc;
}

Datenausgabe.h
Code:
//---------------------------------------------------------------------------
#include <stdio.h>
#include <iostream>	//für cout/cin
//---------------------------------------------------------------------------

void AusgabeTabelle(struct sBuchEintrag *datensatz);

Datenausgabe.cpp
Code:
//---------------------------------------------------------------------------

#pragma hdrstop

#include "Datenausgabe.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

void AusgabeTabelle(struct sBuchEintrag *datensatz){

	std::cout<<"Alle Daten werden ausgegeben";
	for (int i=0; i < 5; i++) {
		std::cout<<datensatz[i]->kaufdatum;
	}
}

Hier meckert er in der .cpp. Der Compiler meldet:

"[BCC32 Fehler] Datenausgabe.cpp(9): E2450 Undefinierte Struktur 'sBuchEintrag'
[BCC32 Fehler] Datenausgabe.cpp(9): E2453 Größe des Typs 'sBuchEintrag' ist unbekannt oder Null
[BCC32 Fehler] Datenausgabe.cpp(9): E2288 Zeiger auf Struktur auf linker Seite von -> oder von ->* erforderlich".

So nun habe ich mir schon ein wenig Gedanken gemacht. Ich nehme mal an da der struct in einem anderen Modul definiert und deklariert ist kennt diese Modul den sBuchEintrag nicht. Nur wenn ich die Datei "struct_ErzeugungArrays.h" includiere meldet er immer noch:

"[BCC32 Fehler] Datenausgabe.cpp(13): E2288 Zeiger auf Struktur auf linker Seite von -> oder von ->* erforderlich"

Nun habe ich folgende Fragen: ist es richtig, dass ich die Module untereinander mit
Code:
#include
bekannt mache wenn es erforderlich ist? Oder: Besteht die Notwendigkeit oder Möglichkeit dieses "Bekanntmachen" zentral an einer bestimmten Stelle vorzunehmen (vielleicht in main)? 2.Frage Wie löse ich mein Problem bzw. worin besteht genau der Fehler?

main.cpp
Code:
//---------------------------------------------------------------------------

#pragma hdrstop

#include "struct_ErzeugungArrays.h"
#include "Datenausgabe.h"
#include <tchar.h>
#include <iostream>
//---------------------------------------------------------------------------
using namespace std;
#pragma argsused

sBuchEintrag *datensatza=NULL;
sBuchEintrag *datensatzb=NULL;
sBuchEintrag *datensatzc=NULL;

int _tmain(int argc, _TCHAR* argv[])
{
	cout<<"Programm gestartet\n";
	datensatza=ArtA();
	datensatzb=ArtB();
	datensatzc=ArtC();

	AusgabeTabelle(datensatza);
	AusgabeTabelle(datensatzb);
	AusgabeTabelle(datensatzc);

	return 0;
}
//---------------------------------------------------------------------------


Grüße
EueRolando
 
Zuletzt bearbeitet:
Deine erste Vermutung ist richtig, d.h. wenn in der Datei Datenausgabe.h die Struktur sBuchEintrag verwendet werden soll, musst du diese mit #include "struct_ErzeugungArrays.h" zunächst bekannt machen (sofern dies nicht schon indirekt durch Einbinden einer anderen Headerdatei geschehen ist).

Zum dann resultierenden Fehler: Ändere mal das

Code:
std::cout<<datensatz[i]->kaufdatum;

wie folgt ab:

Code:
std::cout<<datensatz[i].kaufdatum;

Durch den []-Operator wird dein Zeiger bereits dereferenziert und du kannst (bzw. musst) ganz normal über . auf die einzelnen Elemente zugreifen.
 
EueRolando schrieb:
Hier meckert er in der .cpp. Der Compiler meldet:

"[BCC32 Fehler] Datenausgabe.cpp(9): E2450 Undefinierte Struktur 'sBuchEintrag'
[BCC32 Fehler] Datenausgabe.cpp(9): E2453 Größe des Typs 'sBuchEintrag' ist unbekannt oder Null
[BCC32 Fehler] Datenausgabe.cpp(9): E2288 Zeiger auf Struktur auf linker Seite von -> oder von ->* erforderlich".

So nun habe ich mir schon ein wenig Gedanken gemacht. Ich nehme mal an da der struct in einem anderen Modul definiert und deklariert ist kennt diese Modul den sBuchEintrag nicht.
Genau, er kennt sBuchEintrag nicht. Entweder du inkludierst also den Header (wie getan und richtig) oder du machst eine Forward Declaration.
Code:
//---------------------------------------------------------------------------
#include <stdio.h>
#include <iostream>	//für cout/cin

#include "struct_ErzeugungArrays.h" // <-- entweder, immer wenn möglich
//---------------------------------------------------------------------------

struct sBuchEintrag; // <-- oder

void AusgabeTabelle(struct sBuchEintrag *datensatz);
EueRolando schrieb:
Nur wenn ich die Datei "struct_ErzeugungArrays.h" includiere meldet er immer noch:

"[BCC32 Fehler] Datenausgabe.cpp(13): E2288 Zeiger auf Struktur auf linker Seite von -> oder von ->* erforderlich"
Das kommt daher, da du bereits einen Zeiger auf ein Objekt übergibst. Was du machen willst, ist aber ein Array auf Zeiger von Objekten zu übergeben. Ergo muss da also ein
Code:
//---------------------------------------------------------------------------

#pragma hdrstop

#include "Datenausgabe.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

void AusgabeTabelle(struct sBuchEintrag **datensatz, size_t size){

	std::cout<<"Alle Daten werden ausgegeben";
	for (size_t i=0; i < size; i++) {
		std::cout<<datensatz[i]->kaufdatum;
	}
}
hin.
 
EueRolando schrieb:
struct_ErzeugungArrays.cpp
Code:
#include "struct_ErzeugungArrays.h"
//---------------------------------------------------------------------------


sBuchEintrag *ArtA(){		//Methode erzeugt ein 5 Elemente Array von BuchEintrag
	sBuchEintrag arta[5];		//5 Elemente vom Typ sBuchEintrag
	return arta;
}
Das kann übrigens so nicht funktionieren. Das Array arta ist eine lokale Variable in der Funktion ArtA(), d.h. seine Lebensdauer ist auf diese Funktion beschränkt. Sobald die Funktion verlassen wird, wird die Variable ungültig, der zurückgegebene Pointer zeigt dann auf einen Speicherbereich mit undefiniertem Inhalt.

Wenn du unbedingt ein automatisch erzeugtes Array verwenden willst, so kannst du das nur dadurch realisieren, dass du das direkt in der main()-Funktion anlegst, du musst also die Zeile
Code:
datensatza=ArtA();
durch
Code:
sBuchEintrag arta[5];
datensatza = arta;
ersetzen.
 
Danke euch für die Hilfe. Jetzt habe ich aber nochmal 'ne Rückfrage an Sculletto: Gilt dies nur für das erste Array, oder auch für die anderen beiden Methoden?

Grüße
EueRolando
 
EueRolando schrieb:
Danke euch für die Hilfe. Jetzt habe ich aber nochmal 'ne Rückfrage an Sculletto: Gilt dies nur für das erste Array, oder auch für die anderen beiden Methoden?

Grüße
EueRolando
Deine zweite Methode, also die aus ArtB() würde so tatsächlich funktionieren, hier erzeugst du das Array ja dynamisch, und das existiert dann so lange, bis du es entweder wieder mit
Code:
delete [] artb;
freigibst oder das Programm zu Ende ist. Der elegantere Weg ist allerdings, das Array wieder freizugeben. Das ist ein Stil, den du dir angewöhnen solltest für den Fall, dass du später einmal komplexere Programme mit längerer Laufzeit schreibst. Ein nicht freigegebenes Array belegt, wenn es nicht mehr gebraucht wird, nur unnötig Speicher.

Deine dritte Methode, ArtC(), kann so auch nicht funktionieren. Das Problem liegt hier anders als bei ArtA() allerdings nicht bei der Erzeugung des Arrays, die kann man durchaus so machen, sondern beim Zugriff auf die Elemente. Du machst ja eine verkettete Liste, und da kannst du nicht einfach mit dem Indexoperator [] arbeiten. Die Elemente der Liste liegen ja nicht i.d.R. hintereinander im Speicher, sondern sind u.U. kreuz und quer über den Speicher verteilt, deswegen muss ja auch jedes Elemenent einen Pointer auf das nächste Element enthalten. Willst du nun auf das i-te Element zugreifen, so geht das nur dadurch, dass du dich vom ersten Element beginnend über die next-Einträge durch die einzelnen Elemente bis zum i-ten Element durchhangelst.
 
Ah, ok das mit dem freigeben hab ich inzwischen schon eingebaut. So im laufe der Zeit habe ich bemerkt dass die ganze Modularisierung, wie ich sie vornehme etwas unsinnig ist. Learning by doing halt. :D Auf jeden Fall vielen Dank für eure Beiträge !

Viele Grüße
EueRolando
 
Schau mal, daß du deinen Headern auch anständige include guards verpaßt, sonst wird es problematisch, wenn die selbe Übersetzungseinheit (also eine .cpp-Datei) einen Header über Umwege mehrmals inkludiert.

Außerdem heißt der C++-standardkonforme Header nicht <stdio.h> sondern <cstdio> (wobei ich jetzt aber bei flüchtigem Überfliegen deines Codes auch nichts gesehen habe, wofür du den Header überhaupt brauchen würdest). Gleiches gilt übrigens auch für andere Standard-Header, die C++ von C geerbt hat:

<math.h> -> <cmath>
<limits.h> -> <climits>
<string.h> -> <cstring> (nicht zu verwechseln mit dem <string> C++ Header, in welchem std::string definiert ist)
<stdlib.h> -> <cstdlib>

und so weiter ... (komplette Liste: http://en.cppreference.com/w/cpp/header)

Obendrein ist es in C++ untypisch, das struct in void AusgabeTabelle(struct sBuchEintrag *datensatz) zu erwähnen. Einfach void AusgabeTabelle(sBuchEintrag *datensatz) ist völlig ausreichend.

Gewöhn dir auch ganz schnell wieder das Verwenden von globalen Variablen (bei dir datensatza, datensatzb und datensatzc) ab, außer es gibt einen zwingenden Grund dafür (und davon gibt es nur verschwindend wenig und verdammt selten). Diese 3 Variablen hättest du auch genau so gut lokal in main() anlegen können.
 

Ähnliche Themen

  • Artikel
Antworten
5
Aufrufe
2.368
Antworten
5
Aufrufe
2.245
Antworten
3
Aufrufe
1.604
Zurück
Oben