# [C++] Array einer Struktur als Zeiger einer Funktion Übergeben - Problem



## Dentho (15. April 2009)

Hallo, 
ich möchte in einem Programm die Daten von Mitarbeitern eingeben und anzeigen können. 
Dafür benutze ich ein Menü im Haupprogramm über welches ich die Funktionen zur Eingabe und Anzeige Aufrufe und die Struktur zum Anzeigen als Referenz und zum Eingeben der Daten als Zeiger übergebe.
Das klappt soweit auch, das Problem ist  das ich bei der Eingabe nur die Werte für das erste Feld verändern kann die restlichen Felder aber unberührt bleiben und das Programm nach dem beenden den Fehler: *Run-Time Check Failure #2 - Stack around the variable 'Mitarbeiter_ID' was corrupted.* ausgibt.
(Ich benutzen Visual C++ 2008)
Der Fehler ist warscheinlich der, das ich das Array falsch übergeben haben (wenn ich die Werte in der Funktion "daten_eingeben" ausgeben lasse existiert nur ein Feld, keine drei, in daten_anzeigen wo sie als Referenz übergebe sind alle 3 Werte vorhanden) bzw der syntax falsch ist aber ich weiß leider nicht wie ich den fehler beheben kann.

Hier ist der Code:

*menue.cpp*

```
#include <iostream>
#include <conio.h>
#include "main.hpp"

using namespace std;
void main(){
	
	int Auswahl;
	S_Mitarbeiter Mitarbeiter_ID[3];

	for (int i=0; i<=2; i++)
	{
	Mitarbeiter_ID[i].fBruttoeinkommen = NULL;
	Mitarbeiter_ID[i].fSozialabgaben   = NULL;
	Mitarbeiter_ID[i].chName[0]        = '\0';
	}
 
do
	{
		cout << "(1) Daten eingeben" << endl;
		cout << "(2) Daten eines Mitarbeiters anzeigen" << endl;
		cout << "Auswahl: ";
		cin >> Auswahl;

                          switch (Auswahl)
		{
		case (1):
			{
				cout << "----------" << endl;
				daten_eingabe(&Mitarbeiter_ID);
				system ("cls");
			} break;

		case (2):
			{
				cout << "----------" << endl;
				daten_anzeigen(Mitarbeiter_ID);
				system ("cls");
			} break;

		case (6):
			{
				cout << "----------" << endl;
				cout << "Das Programm wird beendet." << endl;
				cout << "Bis bald." << endl;
			} break;

                           default: 
			{
				cout << "----------" << endl;
				cout << "Falsche Eingabe!" << endl;
				_getch();
				system ("cls");
			}
		}
	}while (Auswahl != 6);

	_getch();
}
```

*main.hpp*

```
struct S_Mitarbeiter
{
	float fBruttoeinkommen;
	float fSozialabgaben;
	char  chName[30];
};

void daten_eingabe(S_Mitarbeiter (*pMitarbeiter_ID)[3]);
void daten_anzeigen(const S_Mitarbeiter (&Mitarbeiter_ID)[3]);
int personal_pruefen();
```

*daten_eingabe.cpp*

```
void daten_eingabe(S_Mitarbeiter (*pMitarbeiter_ID)[3]){

	int mnr = personal_pruefen();

		cout << "--------------" << endl;
		cout << "Bitte geben sie die Daten fuer Mitarbeiter Nr." << mnr << " ein:" << endl;
		
		cout << "Name: ";
		cin.ignore();
		cin.get (pMitarbeiter_ID[mnr-1]->chName, 29);
		cout << "Bruttoeinkommen: ";
		cin >> pMitarbeiter_ID[mnr-1]->fBruttoeinkommen;
		cout << "Sozialabgaben: ";
		cin >> pMitarbeiter_ID[mnr-1]->fSozialabgaben;
		cout << "--------------" << endl;
		cout << "Vielen Dank fuer ihre Eingabe." << endl;
	
	_getch();
}
```

*daten_anzeigen.cpp*

```
void daten_anzeigen(const S_Mitarbeiter (&Mitarbeiter_ID)[3]){
	
	int mnr = personal_pruefen();
	
		cout << "--------------" << endl;
		cout << "Mitarbeiter: " << mnr << endl;
		cout << "Name: " << Mitarbeiter_ID[mnr-1].chName << endl;
		cout << "Bruttoeinkommen: " << Mitarbeiter_ID[mnr-1].fBruttoeinkommen << " Euro" << endl;
		cout << "Sozialabgaben: " << Mitarbeiter_ID[mnr-1].fSozialabgaben << " Euro" << endl;

	_getch();
}
```

*personal_pruefen.cpp*

```
int personal_pruefen(){
	
	int mnr;

	do
	{
		cout << "Bitte waehlen Sie eine Personalnummer (1-3): ";
		cin >> mnr;
		if (mnr<1 || mnr>3)
		{
			cout << "Falsche Eingabe!" << endl;
			_getch();
			cout << "----------" << endl;
		}
	}while (mnr<1 || mnr>3);

	return mnr;
}
```


----------



## deepthroat (15. April 2009)

Hi.





Dentho hat gesagt.:


> *main.hpp*
> 
> ```
> void daten_eingabe(S_Mitarbeiter (*pMitarbeiter_ID)[3]);
> ```


Hier deklarierst du den Parameter der Funktion als Zeiger auf ein Array der Größe 3 vom Typ S_Mitarbeiter.


Dentho hat gesagt.:


> ```
> void daten_anzeigen(const S_Mitarbeiter (&Mitarbeiter_ID)[3]);
> ```


Hier wiederum deklarierst du den Parameter als Referenz zu einem Array der Größe 3 vom Typ konstante S_Mitarbeiter.

Man kann von einer Arrayvariablen nicht die Adresse mit dem &-Operator bestimmen. Wenn du das machst erhälst du nur die Adresse des ersten Elementes des Arrays. D.h. Array == &Array

Eigentlich machst du dir auch zuviel Gedanken. Ein Array wird immer per Referenz übergeben, da ein Array nur durch einen Zeiger auf den Anfang des Arrays, also auf das erste Element, repräsentiert wird.


```
void daten_eingabe(S_Mitarbeiiter m[3]);

S_Mitarbeiter xyz[3];
daten_eingabe(xzy);
```
Evtl. solltest du die Strukturierung des Programmes nochmal überdenken. Es wäre sinnvoller die Mitarbeiternr. vorher abzufragen und dann die Funktion nur noch mit dem konkreten Mitarbeiter *by-reference* aufzurufen, anstatt der Funktion das komplette Mitarbeiterarray zu übergeben. Außerdem mußt du ja wenn du die Anzahl der Mitarbeiter änderst jede Funktion anpassen...


Dentho hat gesagt.:


> ```
> Mitarbeiter_ID[i].fBruttoeinkommen = NULL;
> Mitarbeiter_ID[i].fSozialabgaben   = NULL;
> ```


Hast du mal die Compilerwarnungen gelesen? Dieser Code ist nicht korrekt - du weist einem float einen Zeigerwert zu. Initialisiere die Werte einfach mit 0.0.

Gruß

PS: Laut C und C++ Standard muss die main Funktion int zurückgeben.


----------



## Dentho (15. April 2009)

Vielen Dank für deine hilfe, nun klappt es ,
allerdings hätte ich noch ein paar kleine Fragen dazu.

1.
Was genau ist der Unterschied zwischen

```
void daten_eingabe(S_Mitarbeiiter Mitarbeiter_ID[3]);
```
und

```
void daten_eingabe(S_Mitarbeiter (&Mitarbeiter_ID)[3]);
```
Beides funktioniert in diesem Beispiel.

2. 

```
int mnr = personal_pruefen();
```
Dabei wird die Funktion

```
int personal_pruefen(){
	
	int mnr;

	do
	{
		cout << "Bitte waehlen Sie eine Personalnummer (1-3): ";
		cin >> mnr;
		if (mnr<1 || mnr>3)
		{
			cout << "Falsche Eingabe!" << endl;
			_getch();
			cout << "----------" << endl;
		}
	}while (mnr<1 || mnr>3);

	return mnr;
}
```
aufgerufen, das ist in meinem Beispiel jetzt zwar kein problem aber eigentlich bin ich davon ausgegangen das in "mnr" nur der Wert gespeichert wird den diese Funktion per return zurückgibt, doch stattdessen wird die gesamte Funktion aufgerufen - warum das?

3.
"Evtl. solltest du die Strukturierung des Programmes nochmal überdenken. Es wäre sinnvoller die Mitarbeiternr. vorher abzufragen und dann die Funktion nur noch mit dem konkreten Mitarbeiter by-reference aufzurufen, anstatt der Funktion das komplette Mitarbeiterarray zu übergeben. Außerdem mußt du ja wenn du die Anzahl der Mitarbeiter änderst jede Funktion anpassen..."
-
Was genau meinst du damit? Bin noch recht neu was Programmieren angeht 
Ich hätte jetzt vlt noch die Anzahl der Mitarbeiter also die größe des arrays mit einer Variable ersetzt die ich dann in der main.cpp einfach ändern kann.
Dann brauch ich, wie du sagstest, nicht jede Funktion ändern wenn ich mal die Anzahl der Mitarbeiter verändern möchte.


----------



## sheel (15. April 2009)

Zu 2: Natürlich wird der Wert in mnr gespeichert - aber welcher?
Bei jedem Funktionsaufruf kann ja eigentlich ein anderer Returnwert rauskommen, und um den "aktuellen" zu bekommen wird die Funktion eben ausgeführt

Zu dem Absatz unten: Bin mir nicht ganz sicher, was du vorhast, aber sowas:

```
int i=68;
S_Mitarbeiter meine_mitarbeiter[i];
```

wird nicht funktionieren-vor allem nicht, wenn du vorhast, die Arraygröße per i zu ändern.

Siehe dafür malloc/free/realloc bzw. new/delete


----------



## Dentho (15. April 2009)

Ich habe das eben mal mit einer Globalen Variable in der Header Datei versucht, das Programm Funktioniert dann mit 

```
const int i=68; <- Global
S_Mitarbeiter meine_mitarbeiter[i];
```

nur wwie löse ich das ohne Globale Variable? :/


----------



## sheel (15. April 2009)

Wo die Variable steht ist hier ziemlich egal...du kannst sie aufgrund const ja trotzdem nicht ändern!
Und ohne const gehts eben nicht


----------



## Dentho (15. April 2009)

Das hatte ich ja nicht vor, jedenfalls nicht in der Laufzeit, die Variable war dafür gedacht das wenn ich das Programm mal erweitern möchte um mehr mitarbeiter anzeigen lassen zu können ich einfach nur eine Variable ändern muss und nicht alle die zahlen in allen funktionen ändern muss.
-
Zu dem Absatz unten: Bin mir nicht ganz sicher, was du vorhast, aber sowas:

```
int i=68;
S_Mitarbeiter meine_mitarbeiter[i];
```

wird nicht funktionieren-vor allem nicht, wenn du vorhast, die Arraygröße per i zu ändern.

Siehe dafür malloc/free/realloc bzw. new/delete
-
Also meintest du damit das das so nicht geht wenn ich die größe des Arrays während das Programm läuft ändern möchte?


----------



## sheel (15. April 2009)

Ganz genau, dafür braucht man malloc etc

Ganz kurz erklärt einmal: 

Definieren müsstest du dein Array dann so:
S_Mitarbeiter *meine_mitarbeiter;

Sobald du weisst, wie groß das Array werden soll (hier zB 48 S_Mitarbeiter), kommt sowas:
meine_mitarbeiter=(S_Mitarbeiter *)malloc(sizeof(S_Mitarbeiter)*48);

Wenn du beim bestehenden Array die Größe nachträglich ändern möchtest,
zb auf 60 (Gesamtanzahl, also 12 mehr):
meine_mitarbeiter=(S_Mitarbeiter *)realloc(meine_mitarbeiter,sizeof(S_Mitarbeiter)*60);

Und vorm Programmende, wenn du das ganze nicht mehr brauchst, ein
free(meine_mitarbeiter);
nicht vergessen!


----------



## Dentho (15. April 2009)

Ah, verstanden!
vielen dank für deine Hilfe.


----------



## deepthroat (15. April 2009)

Dentho hat gesagt.:


> Vielen Dank für deine hilfe, nun klappt es ,
> allerdings hätte ich noch ein paar kleine Fragen dazu.
> 
> 1.
> ...


Es gibt keinen Unterschied. Wie bereits gesagt wird ein Array immer per Referenz übergeben.


			
				Dentho hat gesagt.:
			
		

> ```
> int mnr = personal_pruefen();
> ```
> Dabei wird die Funktion
> ...


Was meinst du damit? Natürlich wird die Funktion aufgerufen, das hast du ja auch so instruiert. Also ich verstehe deine Frage irgendwie nicht.


Dentho hat gesagt.:


> 3.
> "Evtl. solltest du die Strukturierung des Programmes nochmal überdenken. Es wäre sinnvoller die Mitarbeiternr. vorher abzufragen und dann die Funktion nur noch mit dem konkreten Mitarbeiter by-reference aufzurufen, anstatt der Funktion das komplette Mitarbeiterarray zu übergeben. Außerdem mußt du ja wenn du die Anzahl der Mitarbeiter änderst jede Funktion anpassen..."
> -
> Was genau meinst du damit? Bin noch recht neu was Programmieren angeht
> ...


Prinzipiell ist es schlechter Stil verschiedene Angelegenheiten in einer Funktion zu bündeln. 

Um *einen* bestimmten Mitarbeiter anzuzeigen, sollte man eine Funktion haben um *einen* Mitarbeiter anzuzeigen. Der anzuzeigende Mitarbeiter sollte als Argument der Funktion übergeben werden. 

Die Auswahl des anzuzeigenden Mitarbeiters hat mit dieser Funktion nichts zu tun. Eingabe, also das Abfragen von Benutzereingaben, hat mit dieser Funktion nichts zu tun und auch die Fehlerbehandlung bei erfolglosem Einlesen haben in der Funktion nichts verloren. 

Auch das die Funktion Zugriff auf alle Mitarbeiter durch Übergabe des gesamten Mitarbeiter Array bekommt ist nicht sehr günstig. Die Funktion arbeitet doch gar nicht auf einer Menge von Mitarbeitern, sondern nur auf einem einzelnen.

Man separiert diese verschiedenen Angelegenheiten und kombiniert sie auf einer höheren Ebene anstatt diese alle auf einer Ebene zu verwursteln.

Ein weiterer Vorteil ist, das man die Funktion eben in jedem beliebigen Zusammenhang aufrufen kann: man kann in einer Schleife alle Mitarbeiter ausgeben (ohne den Benutzer nach einer Mitarbeiter-Nr. zu fragen), man kann bestimmte Mitarbeiter anhand eines Suchkriteriums ausgeben (nachdem man den Benutzer nach einem Suchwort gefragt hat) und man kann die Funktion verwenden um einen bestimmten Mitarbeiter anhand der Mitarbeiter-Nr. auszugeben usw.

Gruß


----------



## Dentho (16. April 2009)

Also ich habe das nun so geändert:


```
case (1):
			{
				cout << "----------" << endl;
				personal_pruefen(&mnr);
				daten_eingabe(Mitarbeiter_ID, &mnr);
				system ("cls");
			} break;

		case (2):
			{
				cout << "----------" << endl;
				personal_pruefen(&mnr);
				daten_anzeigen(Mitarbeiter_ID, &mnr);
				system ("cls");
			} break;
```

Er ruft also bevor ich die Daten eingeben oder Anzeigen kann die Funktion zur Prüfung der Nummer auf.
und dann:


```
void daten_eingabe(S_Mitarbeiter Mitarbeiter_ID[mza], int *mnr){
	
		cout << "--------------" << endl;
		cout << "Bitte geben sie die Daten fuer Mitarbeiter Nr." << *mnr << " ein:" << endl;
		cout << "Name: ";
		cin.ignore();
		cin.get (Mitarbeiter_ID[*mnr-1].chName, 29);
		cout << "Bruttoeinkommen: ";
		cin >> Mitarbeiter_ID[*mnr-1].fBruttoeinkommen;
		cout << "Sozialabgaben: ";
		cin >> Mitarbeiter_ID[*mnr-1].fSozialabgaben;
		cout << "--------------" << endl;
		cout << "Vielen Dank fuer ihre Eingabe." << endl;
	
	_getch();
}
```
-

```
void daten_anzeigen(const S_Mitarbeiter Mitarbeiter_ID[mza], int *mnr){
	
		cout << "--------------" << endl;
		cout << "Mitarbeiter: " << *mnr << endl;
		cout << "Name: " << Mitarbeiter_ID[*mnr-1].chName << endl;
		cout << "Bruttoeinkommen: " << Mitarbeiter_ID[*mnr-1].fBruttoeinkommen << " Euro" << endl;
		cout << "Sozialabgaben: " << Mitarbeiter_ID[*mnr-1].fSozialabgaben << " Euro" << endl;

	_getch();
}
```

ruft er je nach auswahl eine der Funktionen auf und liest die daten ein bzw zeigt sie an. Und zwar nur für die jweilige mitarebiter nummer deren adresse ich mit einem zeiger mit übergeben habe. Richtig so?


----------



## deepthroat (16. April 2009)

Dentho hat gesagt.:


> ruft er je nach auswahl eine der Funktionen auf und liest die daten ein bzw zeigt sie an. Und zwar nur für die jweilige mitarebiter nummer deren adresse ich mit einem zeiger mit übergeben habe. Richtig so?


Nicht ganz. Du übergibst immer noch das gesamte Mitarbeiter Array. Wozu?

```
void daten_anzeigen(const S_Mitarbeiter& m);

daten_anzeigen(Mitarbeiter_ID[mnr]);
```
Auf diese Weise ist auch noch die Datenhaltung von dieser Funktion separiert - d.h. man kann auch das Array durch eine andere Datenstruktur ersetzen ohne die Funktionen ändern zu müssen, die nur auf einem einzelnen Mitarbeiter agieren.

Gruß


----------



## Dentho (16. April 2009)

Ah, verstanden  nun klappt es.
Vielen dank aber eine letze Frage hätte ich noch.
Du sagtest ja das man die einzelnen Aufgaben alle separieren sollte.
Auf welche Art sollte ich diese If-Bedingung dann schreiben?


```
case (2):
			{
				cout << "----------" << endl;
				personal_pruefen(&mnr);
				if (Mitarbeiter_ID[mnr-1].bDaten_vorhanden == false)
				{
					cout << "--------------" << endl;
					cout << "Es sind keine Daten fuer Mitarbeiternummer. "
					<< Mitarbeiter_ID[mnr-1].ident << " vorhanden." << endl;
					_getch();
				}
				else
				{
				daten_anzeigen(Mitarbeiter_ID[mnr-1]);
				}
				system ("cls");
			} break;
```

oder in die Funktion *daten_anzeigen*
oder dafür eine Extra Funktion schreiben?
Mein Gedanke dabei wäre halt das ich, wenn ich die Funktion eh nicht aufrufe da keine Daten vorhanden sind , ich nicht extra das Array für eine Fehlermeldung übergeben müsste da das ja unnötig Speicher kosten würde, auf der anderen Seite, wenn ich das so sehe könnte ich ja auch die gesamte Funktion in den case 2 schreiben.....? :/


----------



## deepthroat (16. April 2009)

Dentho hat gesagt.:


> Du sagtest ja das man die einzelnen Aufgaben alle separieren sollte.
> Auf welche Art sollte ich diese If-Bedingung dann schreiben?
> 
> 
> ...


Nein, eher nicht. Es gibt ein Konzept das nennt sich Programming-by-Contract. Wie bereits gesagt sollte die daten_anzeigen Funktion für einen einzelnen Mitarbeiter funktionieren. D.h. aber auch, das der Mitarbeiter, der als Parameter übergeben wurde, existieren muss, also an sich valide sein muss. Das stellt die sog. Vorbedingung und den Vertrag der Funktion dar: rufe mich mit einem existierenden, korrekt initialisiertem Mitarbeiter auf (Vorbedingung), und ich zeige die Daten dieses Mitarbeiters entsprechend an. Ist die Vorbedingung nicht erfüllt, kann die Funktion ihre Aufgabe nicht erfüllen.

Das Problem liegt aber eigentlich auch noch woanders. Und zwar hast du (gezwungenermaßen) dieses leidliche *bDaten_vorhanden* Attribut zu jedem Mitarbeiter hinzugefügt. Das ist sehr unschön und es vermischt schon wieder die Datenhaltung im Array mit den eigentlichen Daten eines Mitarbeiters. Das hat beides miteinander nichts zu tun.

Eigentlich sollte man an dieser Stelle eine geeignete Datenstruktur zur Verwaltung der Mitarbeiter verwenden. Ein statisches Array ist relativ schlecht geeignet.

Aber um es nicht gleich alles umzustellen und nicht allzu kompliziert zu machen, würde ich dir empfehlen ein zweites Array anzulegen, in dem du an Position i vermerkst ob der Mitarbeiter mit dem Index i bereits initialisiert wurde. Diese beiden Arrays könntest du dann wiederum in einer Struktur zusammenfassen.



Dentho hat gesagt.:


> oder dafür eine Extra Funktion schreiben?


Ich denke ich würde dafür die personal_pruefen Funktion erweitern. (wobei der Name der Funktion evlt. auch nicht so gut ist. Was prüft diese Funktion denn? Besser wäre evlt. frage_nach_mitarbeiter_nr o.ä.)

Dann solltest du gleich die Möglichkeit vorsehen, das man evlt. keine Mitarbeiternr. eingeben möchte so dass die Funktion einen boolschen Wert zurückgibt wenn das Einlesen einer validen Mitarbeiter-Nr. erfolgreich war oder nicht.

```
if (frage_nach_mitarbeiter_nr(&mnr)) {
  daten_anzeigen(Mitarbeiter[mnr]);
}
```
Gruß


----------

