ubuntuusers.de

[C] Structur an Funktion übergeben

Status: Ungelöst | Ubuntu-Version: Ubuntu 12.04 (Precise Pangolin)
Antworten |

Budada_Bubladend

Avatar von Budada_Bubladend

Anmeldungsdatum:
13. Dezember 2009

Beiträge: 812

Hallo Leute,

ich bin gerade trotz langer Internetsuche am verzweifeln. Ich stehe vor folgendem Problem:

Ich habe mit einer Structur einen Datentyp erstellt:

struct bruch {
     int zaehler;
     int nenner;
};

Nun möchte ich in einer Funktion Namens eingabe b1 füllen. Ich habe nur keine Ahnung wie ich b1 übergebe! Beispiel:

//b1 soll sein
struct bruch b1={zaehler nenner};
//muss ich jetzt b1 anlegen, und das dann in die Funktion eingabe übergeben oder wie?

void eingabe (b1){
  int zaehler;
     printf ( "Zaehler:" );
     scanf ( "%d" , zaehler);
struct bruch b1 ={zaehler};   //funktioniert so leider nicht

Im Internet bin ich auf folgendes gestoßen:

//Funktionsaufruf:
eingabe( bruch);

//Funktionskopf:
void eingabe (struct bruch);

doch das geht auch nicht!

Kann mir bitte jemand helfen?

Danke schon mal im voraus

Lg BB

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Hiermit sind zaehler und nenner nur lokal im struct bekannt.

1
2
3
4
struct bruch {
     int zaehler;
     int nenner;
};
1
2
3
4
5
6
7
8
//b1 soll sein
struct bruch b1={zaehler nenner};

void eingabe (b1){
  int zaehler;
     printf ( "Zaehler:" );
     scanf ( "%d" , zaehler);
struct bruch b1 ={zaehler};   //funktioniert so leider nicht

Weder sind zaehler und nenner deklariert und initialisiert (lediglich bruch.nenner ist deklariert), noch deklarierst Du eine Funktion derart, dass Du sie mit einer konkreten Referenz aufrufst.

1
2
3
int a = 7; 
void foo (a) { // ...
void foo (7) { // ...

würde man beides nicht machen, denn es ist sinnlos. Eine Funktion erwartet Parameter, die erst zur Laufzeit gebunden werden. Wenn Du sowas wirklich bräuchtest würdest Du schreiben

1
2
void foo (void) { 
int a = 7; 

Was Du meinst ist wohl:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

struct bruch {
     int zaehler;
     int nenner;
};

struct bruch eingabe () {
  int n, z;
  printf ( "Zähler:" );
  scanf ( "%d" , &z);
  printf ( "Nenner:" );
  scanf ( "%d" , &n);
  struct bruch b1;
  b1.zaehler = z;
  b1.nenner = n;
  return b1;
}

Mein C ist sehr angestaubt - da sind bestimmt Fehler drin - jedenfalls compiliert es.

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

Entweder das oder du übergibst einen Zeiger an deine Funktion:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

struct bruch
{
	int zaehler;
	int nenner;
};

void eingabe(struct bruch *b)
{
	b->zaehler = 2;
	b->nenner = 3;
}

int main(int argc, char *argv[])
{
	struct bruch a;
	eingabe(&a);
	printf("%d, %d\n", a.zaehler, a.nenner);
	return 0;
}

Der Unterschied ist meines Wissens nach auch, dass bei user unknown in der Funktion „eingabe()“ ein struct erzeugt wird und dann nach Ende dieser Funktion in der aufrufenden Funktion das „zurückgegebene“ struct auf den lokalen Stack kopiert wird. Musst du wissen, ob sowas bei dir sinnvoll ist. Wenn du es mit einem Pointer machst, dann hast du nur in der aufrufenden Funktion ein struct und „eingabe()“ arbeitet direkt darauf.

Damit wir alle Varianten durch haben: Obiges Beispiel ohne Pointer erzeugt dir in „eingabe()“ eine Kopie des structs, die auf die aufrufende Funktion keine Auswirkung hat:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

struct bruch
{
	int zaehler;
	int nenner;
};

void eingabe(struct bruch b)
{
	b.zaehler = 2;
	b.nenner = 3;
}

int main(int argc, char *argv[])
{
	struct bruch a;
	a.zaehler = 123;
	a.nenner = 456;
	eingabe(a);
	printf("%d, %d\n", a.zaehler, a.nenner);
	return 0;
}

Darkstar999

Avatar von Darkstar999

Anmeldungsdatum:
21. September 2008

Beiträge: 324

Vain schrieb:

Hallo nur so Interesse halber, der Unterschied kommt doch erst Signifikant zur Geltung im Ansi C bei großen Strukturen wo es um Speicher spezifische bzw. Zeitliche Begrenzungen geht oder z.B auf einem 8-Bit Systeme etc. Oder liege ich da falsch.

Zumal das Direkt auf der Struktur rum rutschen wenn man weiß was man tut ideal ist da man dann im Programmverlauf keine Kopie der Struktur an die Funktion übergibst! Wie du oben schon erwähnt hast.

mfg darkstar999

Budada_Bubladend

(Themenstarter)
Avatar von Budada_Bubladend

Anmeldungsdatum:
13. Dezember 2009

Beiträge: 812

Hey Vain,

danke dir! Genau das habe ich eigentlich gesucht! Ich hatte es mit einem Pointer versucht, doch das hatte nicht ganz funktioniert.. ich weiß jetzt auch warum. Ich hatte da

void eingabe(*b)

(an dein Beispiel angepasst). Nun funktioniert es! Vielen Dank.. natürlich auch an user unknown.

Gruß BB

Vain

Avatar von Vain

Anmeldungsdatum:
12. April 2008

Beiträge: 2510

Darkstar999 schrieb:

Hallo nur so Interesse halber, der Unterschied kommt doch erst Signifikant zur Geltung im Ansi C bei großen Strukturen wo es um Speicher spezifische bzw. Zeitliche Begrenzungen geht oder z.B auf einem 8-Bit Systeme etc. Oder liege ich da falsch.

Ist anzunehmen, dass das nur relevant ist, wenn’s wirklich auf die letzte Millisekunde und das letzte Bit ankommt. Kannst dir aber vielleicht auch mal anschauen, welchen Code der Compiler so generiert, wenn du ihn optimieren lässt. So schlau, wie die Dinger mittlerweile sind, könnte ich mir vorstellen, dass sie eine unnötige Kopie dann eh wegoptimieren.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Ich kann nur raten die Frage aus einem anderen Blickwinkel zu stellen: Wie will man die Funktion nutzen?

Will ich mir ein Bruch-Objekt geben lassen, oder will ich dafür immer extra Code schreiben?

Als Programmierer will ich eine Funktion, die mir soviel Arbeit wie möglich abnimmt.

Will ich einen einmal erzeugten Bruch nochmal neu verwenden - das sieht eher ungewöhnlich und wie ein Codesmell aus, kann aber in speziellen Situationen doch sinnvoll sein.

Benötige ich eine extra Funktion, die Brüche erzeugt, die aber nicht von der Tastatur liest? Das ist ziemlich gut möglich, z.B. wenn man Funktionen hat, die da lauten

1
2
3
4
5
6
struct Bruch add (Bruch a, Bruch b);
struct Bruch mul (Bruch a, Bruch b);
struct Bruch sub (Bruch a, Bruch b);
struct Bruch div (Bruch a, Bruch b);
struct Bruch invers (Bruch a);
struct Bruch kuerzen (Bruch a);

Dann könnte sich eine eigene Funktion zum Erzeugen von Brüchen dennoch anbieten, weil man das nicht immer an Ort und Stelle machen will (DRY-Prinzip).

Darkstar999

Avatar von Darkstar999

Anmeldungsdatum:
21. September 2008

Beiträge: 324

user unknown schrieb:

Dann könnte sich eine eigene Funktion zum Erzeugen von Brüchen dennoch anbieten, weil man das nicht immer an Ort und Stelle machen will (DRY-Prinzip).

Naja dan würde ich Persönlich auf Zeiger und eine Dynamische Datenstruktur wählen!!! Plus Funktionszeiger wegen mal plus durch etc..

Besten Dank für die Antworten

mfg darkstar999

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Darkstar999 schrieb:

user unknown schrieb:

Dann könnte sich eine eigene Funktion zum Erzeugen von Brüchen dennoch anbieten, weil man das nicht immer an Ort und Stelle machen will (DRY-Prinzip).

Naja dan würde ich Persönlich auf Zeiger und eine Dynamische Datenstruktur wählen!!!

Was ist eine dynamische Datenstruktur? Wie implementiert man das? Wieso?

Plus Funktionszeiger wegen mal plus durch etc..

Verstehe ich auch nicht.

1
2
3
Bruch a = eingabe ();
Bruch b = eingabe ();
Bruch c = add (mul (a, a), div (a, b)); 

Nötig ist das ja nicht. Oder wozu?

Darkstar999

Avatar von Darkstar999

Anmeldungsdatum:
21. September 2008

Beiträge: 324

So ich habe hier mal ein kleines Beispiel zum Thema Funktionszeiger zusammengebastelt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
 ============================================================================
 Name        : Bruch.c
 Author      : darkstar999
 Version     :
 Copyright   : Your copyright notice
 Description : Bruch C, Ansi-style
 ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>

struct Bruch {
  int zaehler;
  int nenner;
  struct Bruch *next;
};

struct Bruch plus(struct Bruch *a,struct Bruch *b){
  struct Bruch *rueck =malloc(sizeof(struct Bruch));
  rueck->zaehler = (a->zaehler) + (b->zaehler);
  rueck->nenner = (a->nenner) + (b->nenner);

  return *rueck;
}


struct Bruch mal(struct Bruch *a,struct Bruch *b){
  struct Bruch *rueck = malloc(sizeof(struct Bruch));
  rueck->zaehler = (a->zaehler) * (b->nenner);
  rueck->nenner = (b->zaehler) * (a->nenner);

  return *rueck;
}

struct Bruch berechnen(struct Bruch (*modus)(struct Bruch *c, struct Bruch *d),struct Bruch *a, struct Bruch *b)
{
  struct Bruch *erg = malloc(sizeof(struct Bruch));

  *erg = (*modus)(a,b);

  return *erg;
}

int main(void) {
	struct Bruch *a = malloc(sizeof(struct Bruch));
	struct Bruch *b = (struct Bruch *)malloc(sizeof(struct Bruch));
	struct Bruch *c = (struct Bruch *)malloc(sizeof(struct Bruch));
	struct Bruch *d = (struct Bruch *)malloc(sizeof(struct Bruch));

	a->zaehler = 2;
	a->nenner = 2;
	b->zaehler = 2;
	b->nenner = 2;

	*c = berechnen(mal,a,b);
        *d = berechnen(plus,a,b);

	printf("Z%d  N%d\n",c->zaehler,c->nenner);
	printf("Z%d  N%d\n",d->zaehler,d->nenner);

        free(a);
	free(b);
	free(c);
        free(d);
         

	return 0;

}

Das mit der Dynamischen Datenstruktur oben im Beispiel schon durch den next Zeiger angedeutet macht z.b Sinn wenn du die Brüche aus einer Datei ausliest um die einzelnen Brüche zwischen zu speichern. Eine Liste (Dynamische Datenstruktur) zu verwende ist Sinnvoll da in der einzulesenden Datei ja unterschiedlich viele Brüche stehen können und du nicht immer von vornherein sagen kannst wie viele Brüche du brauchst.Ich habe das jetzt nicht mit implementiert weil ich nicht weiß ob jedem gleich klar wird was ich in dem Beispiel oben mache.

mfg darkstar999

TraumFlug

Avatar von TraumFlug

Anmeldungsdatum:
16. Juli 2009

Beiträge: 999

Wohnort: zwischen den ohren

Darkstar999 schrieb:

So ich habe hier mal ein kleines Beispiel zum Thema Funktionszeiger zusammengebastelt.

...

<...> weil ich nicht weiß ob jedem gleich klar wird was ich in dem Beispiel oben mache.

Du machst Mist mit malloc. Unter Anderem. Dein Code "leakt" wie sau:

Bei jedem Aufruf einer deiner Unterfunktionen:

  struct Bruch *rueck = malloc(sizeof(struct Bruch));
  ...
  return *rueck;

...Du alloziierst eine Struct mit malloc, und gibtst nicht den Pointer Zurück, sondern die reine Struct (auf dem Stack für die aufrufende Funktion). Die Daten werden nich in deinem Programm freigegeben. Wenn du schon, im Sinne wirklich "dynamischer" Datenstrukturen (dein Beispiel hat so gut wie nichts damit zu tun!), eine Funktion willst, die einen Pointer auf eine "neue" Struktur zurückgibt, dann musst du im Programm Sorge tragen, dass keine Leaks entstehen. Z.B. indem du einen Pointer auf das alloziierte zurückgibst, diesen nur auf einen "leeren" Pointer überträgst, und sicherstellst, dass er nach Nutzung wieder freigegeben wird.

Gut, du wolltest nur ein kleines "Beispiel" geben, aber insgesammt ist in diesem jegliche Nutzung von malloc "überflüssig". Das man mit Pointer-Parameter-Funktionen arbeitet, heisst nicht, dass man malloc nutzen muss, malloc hat den Sinn, bei Programmierung unbekannte Quantitäten an Speicher zur Verfügung zu stellen, der auch Ausserhalb von den alloziierenden Funktionen noch Bestand hat. Und nicht, dass man jeglichen Speicher, den man so braucht damit anfordert!

Ausserdem ist es eher unüblich, einen Basisdatentyp, mit dem man alles Mögliche machen kann ("struct Bruch") direkt mit einem Pointer für eine verlinkte Liste zu versehen, eher baut man eine spezialisierte Listenstruktur, und versieht diese mit einer "struct Bruch", oder wenn's ganz generisch sein soll, mit einem void* der dann auf einen extra angeforderten Bruch zeigt.

Und noch lesenswert, eine gute Konvention in der C Programmierung, die viel praktiziert wird, wenn Pointer auf irgendwas an eine Funktion übergeben werden: http://en.wikipedia.org/wiki/Const_correctness.

Darkstar999

Avatar von Darkstar999

Anmeldungsdatum:
21. September 2008

Beiträge: 324

Darkstar999 schrieb:

Das mit der Dynamischen Datenstruktur oben im Beispiel schon durch den next Zeiger angedeutet macht z.b Sinn wenn du die Brüche aus einer Datei ausliest um die einzelnen Brüche zwischen zu >speichern.Ich habe das jetzt nicht mit implementiert weil ich nicht weiß ob jedem gleich klar wird was ich in dem Beispiel oben mache.

Richtig ich habe die Dynamische Datenstruktur nicht genutzt wie ich auch geschrieben habe in diesem Beispiel!!!!!

Zum Thema jegliche Nutzung von malloc ist überflüssig nein da sonst z.b *d adresse 0x00 hat und somit das Programm abbricht und keine Ausgabe zu Stande kommt von d!

Soweit zum mindestens mein Stand der Dinge!

Sorry ich kann dir gerade nicht ganz folgen wo das leak entsteht.

mfg darkstar999

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17622

Wohnort: Berlin

Darkstar999 schrieb:

So ich habe hier mal ein kleines Beispiel zum Thema Funktionszeiger zusammengebastelt.

1
2
3
4
5
struct Bruch {
  int zaehler;
  int nenner;
  struct Bruch *next;
};

Und wenn Du Brüche in Arrays speichern willst, dann schreibst Du die Struktur neu, ohne *next? Und wenn Du eine doppelt verkettete Liste willst, dann schreibst Du es wieder neu? Und wenn Du eine Liste von Fluechen haben willst schreibst Du effektiv wieder den gleichen Listencode - nur mit Flüchen statt Brüchen - neu?

Das sieht aber nicht empfehlenswert aus.

Ein next-Zeiger hat in einem Bruch meiner Meinung nach nichts verloren (SOC: separation of concerns), und eine Liste sollte agnostisch sein gegenüber ihren Inhalten.

1
2
3
4
5
6
7
struct Bruch plus(struct Bruch *a,struct Bruch *b){
  struct Bruch *rueck =malloc(sizeof(struct Bruch));
  rueck->zaehler = (a->zaehler) + (b->zaehler);
  rueck->nenner = (a->nenner) + (b->nenner);

  return *rueck;
}
½+½=2*¼ 

Sei froh dass das Dein Mathelehrer nicht sieht! ☺

struct Bruch berechnen (struct Bruch (*modus)(struct Bruch *c, struct Bruch *d), struct Bruch *a, struct Bruch *b) { struct Bruch *erg = malloc(sizeof(struct Bruch));

  • erg = (*modus)(a,b);

return *erg; }

int main(void) { struct Bruch *a = malloc(sizeof(struct Bruch)); struct Bruch *b = (struct Bruch *) malloc (sizeof (struct Bruch)); struct Bruch *c = (struct Bruch *) malloc (sizeof (struct Bruch)); struct Bruch *d = (struct Bruch *) malloc (sizeof (struct Bruch));

a-> zaehler = 2; a-> nenner = 2; b-> zaehler = 2; b-> nenner = 2;

  • c = berechnen (mal, a,b);

  • d = berechnen (plus, a,b);

printf ("Z%d N%d\n", c-> zaehler, c-> nenner); printf ("Z%d N%d\n", d-> zaehler, d-> nenner);

free (a); free (b); free (c); free (d);

return 0; }

}}}

Wenn Du zeigen wolltest, welches Problem jetzt Dein Typ löst, welches meiner nicht löst, sehe ich nichts. Nur 4 Aufrufe von 'free', die ich spare.

Das mit der Dynamischen Datenstruktur oben im Beispiel

... überrascht mich, weil ich eine dynamische Collection nicht so bezeichnet hätte, und nicht mit dem Code des Objekts vermengen würde. Aber wenn man es tut, wie Du, ist das Bruchobjekt selbst keine dynamische Struktur: Es ist ja jeder Bruch neben seiner Bruchhaftigkeit noch ein Knoten, und weder kann er unterschiedlich viele Zeiger auf weitere Elemente haben - etwa in einem Baum könnte das praktisch sein, oder in einer Dequeue - (außer einer notdürftigen Unterscheidung zwischen 0 und 1 next-Element, implementiert als NullPointer) - noch kann das nächste Element etwas anderes als wieder ein Bruch sein. Also sehr dynamisch ist die Struktur so nicht.

1
*c = berechnen(mal,a,b);

ist ja auch nur komplizierter als

1
c = mal (a, b);

Ein Vorteil wurde nicht deutlich gemacht.

Darkstar999

Avatar von Darkstar999

Anmeldungsdatum:
21. September 2008

Beiträge: 324

user unknown schrieb:

Sei froh dass das Dein Mathelehrer nicht sieht! ☺

Ups inne Ecke stell +dumdidum+ Flüchtigkeitsfehler!

mfg darkstar999

TraumFlug

Avatar von TraumFlug

Anmeldungsdatum:
16. Juli 2009

Beiträge: 999

Wohnort: zwischen den ohren

Darkstar999 schrieb:

Zum Thema jegliche Nutzung von malloc ist überflüssig nein da sonst z.b *d adresse 0x00 hat und somit das Programm abbricht und keine Ausgabe zu Stande kommt von d!

Ich meinte ja auch nicht, dass nur das malloc überflüssig ist, sondern dass du unnötig dynamischen Speicher anforderst, wo ein Arbeiten mit lokalen Variablen sehr viel sauberer und effizienter wäre.

Soweit zum mindestens mein Stand der Dinge!

Sorry ich kann dir gerade nicht ganz folgen wo das leak entsteht.

return *rueck;

Das kopiert die Struct, auf die "rueck" zeigt als return-value auf den Stack und danach wird der "struct Bruch* rueck" quasi "vergessen" und der alloziierte Speicher, auf den er zeigte nie wieder freigegeben.

Im Ernst, aus verschiedenen Gründen ist das einer der verkifftesten c-codes seit langem, den ich gesehen habe - wenn man bedenkt, dass du das in einem Thread, der von einem Anfänger gestartet wurde, der grundsätzliche Fragen hatte, quasi als gute Lösung präsentierst, ...kann man nicht mehr schweigen... 😈

Antworten |