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

Dentho

Grünschnabel
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
Code:
#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
Code:
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
Code:
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
Code:
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
Code:
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;
}
 
Zuletzt bearbeitet:

deepthroat

Erfahrenes Mitglied
Hi.
main.hpp
Code:
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.
Code:
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.

C++:
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...
Code:
	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.
 
Zuletzt bearbeitet:

Dentho

Grünschnabel
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
Code:
void daten_eingabe(S_Mitarbeiiter Mitarbeiter_ID[3]);
und
Code:
void daten_eingabe(S_Mitarbeiter (&Mitarbeiter_ID)[3]);
Beides funktioniert in diesem Beispiel.

2.
Code:
int mnr = personal_pruefen();
Dabei wird die Funktion
Code:
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

I love Asm
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:
Code:
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

Grünschnabel
Ich habe das eben mal mit einer Globalen Variable in der Header Datei versucht, das Programm Funktioniert dann mit
Code:
const int i=68; <- Global
S_Mitarbeiter meine_mitarbeiter[i];

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

sheel

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

Dentho

Grünschnabel
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:
Code:
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

I love Asm
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!
 

deepthroat

Erfahrenes Mitglied
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
Code:
void daten_eingabe(S_Mitarbeiiter Mitarbeiter_ID[3]);
und
Code:
void daten_eingabe(S_Mitarbeiter (&Mitarbeiter_ID)[3]);
Es gibt keinen Unterschied. Wie bereits gesagt wird ein Array immer per Referenz übergeben.
Dentho hat gesagt.:
Code:
int mnr = personal_pruefen();
Dabei wird die Funktion
Code:
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?
Was meinst du damit? Natürlich wird die Funktion aufgerufen, das hast du ja auch so instruiert. Also ich verstehe deine Frage irgendwie nicht.
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.
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ß