Forum: PC-Programmierung Zwei Fragen zu verketteten Listen


von Kanone (Gast)


Lesenswert?

Hallo,

ich spiele gerade mit verschiedenen Datenstrukturen in C, um was zu 
lernen. Dabei kommen gerade zwei Fragen auf, die ich mit 
Suchmaschinen-Hilfe nicht klären konnte.

1. Frage: Wenn ich eine verkettete Liste als struct deklarieren möchte, 
wie bekomme ich dann den Verweis auf das nächste Elemente innerhalb der 
struct definiert? Zu dem Zeitpunkt kennt der Compiler das ja noch nicht 
und spuckt eine Fehlermeldung aus:
1
typedef struct{
2
  int variable_1;
3
  int variable_2:
4
  liste_t *next;
5
} liste_t;

Ich könnte einfach eine Variable definieren, so wie es in den meisten 
Beispielen im Netz gemacht wird, dann geht es:
1
struct liste
2
{
3
  int variable_1;
4
  int variable_2;
5
  liste *next;
6
}

Ich brauche aber eine saubere Typdefinition, möchte die Algorithmen 
auslagern.

2. Frage: Wenn ich neue Elemente zufüge, mache ich dann jedes Mal ein 
malloc() für ein paar Bytes? Wie ist das mit der Speicherverwaltung, ist 
das nicht furchtbar ineffizient? Ich habe gelesen, dass bei vielen 
Betriebssystemen die kleinst allozierbare Einheit schon ein paar kb groß 
ist, das heißt bei vielen Elementen in der Liste verbrauche ich doch ein 
vielfaches des eigentlich nötigen Speicherplatzes, oder?

Wie lösen Profis das Problem?

Danke und Gruß,
Martin

: Verschoben durch User
von Franz H. (dl7avf)


Lesenswert?

Martin,
die übliche Deklaration einer einfach verketteten Liste in Standard-C 
ist wie folgt:
1
// in Header-Datei list.h
2
3
// Struktur eines Listenelements.
4
struct listen_element
5
  {
6
  int idata; // oder was auch immer als Datenobjekt benötigt wird.
7
  struct listen_element *pnext;  
8
  };
9
10
// Typ eines Listenelements. 
11
typedef struct listen_element listen_t;  
12
13
// in main.c z.B.
14
15
// Beispiel für statische Listenelemente:
16
listen_t listen_element1;
17
listen_t listen_element2;

zu 2.
Die Speicherverwaltung bei malloc() usw. macht üblicherweise die 
C-Laufzeitbibliothek und ist dabei auf den im Heap angelegten 
Speicherbereich beschränkt. Was darüber hinausgeht ist 
betriebssystemabhängig (falls bei Microcontrollern überhaupt ein 
Betriebssystem vorhanden ist).

Schöne Grüsse,
Franz

von Klaus W. (mfgkw)


Lesenswert?

Das ist richtig, aber vielleicht etwas näher an der ersten Frage:
1
// der Trick ist, der struct hier bereits einen Namen zu geben:
2
typedef struct s_liste{
3
// dadurch kann man die struct entweder als 'struct s_liste'
4
// oder als 'liste_t' verwenden, beides ist eigentlich gleichwertig.
5
// Der einzige Unterschied ist, daß 'struct s_liste' bereits
6
// hier bekannt ist, 'liste_t' erst später.
7
  int variable_1;
8
  int variable_2:
9
  struct s_liste *next; // das geht
10
} liste_t; // ab hier erst gibt es 'liste_t'

von (prx) A. K. (prx)


Lesenswert?

Man kann auch den gleichen Namen verwenden:
1
typedef struct liste_t {
2
  ...
3
  struct liste_t *next;
4
} liste_t;

von Klaus W. (mfgkw)


Lesenswert?

Zur 2. Frage:
"effizient" und "ineffizient" sind dehnbare Begriffe.
Ein Allokieren und wieder Freigeben vieler Elemente auf dem Stack wird 
sicher langsamer sein, als dieselben Elemente nebeneinander auf dem 
Stack zu haben.

Aber so schlecht ist es dann in der Regel auch nicht implementiert, und 
wenn doch muß man die C-Lib wechseln.

Im allgemeinen Fall ohne Nebenbedingungen wird es nicht viel effizienter 
gehen.
Weiß man dagegen Randbedingungen, kann man evtl. etwas Schnelleres 
finden.

Beispielsweise müssen Obstacks (GNU-libc, kein Standard: 
http://www.gnu.org/software/libc/manual/html_node/Obstacks.html#Obstacks 
) in der umgekehrten Reihenfolge freigegeben wie allokiert werden, sind 
dann aber effizienter.
Oder man hat eine (nicht zu hohe) Obergrenze der Anzahl allokierter 
Elemente, dann kann man die Elemente in einem Feld halten.

von Klaus W. (mfgkw)


Lesenswert?

A. K. schrieb:
> Man kann auch den gleichen Namen verwenden:

ja, aber es ist eine Stilfrage, ob man das möchte :-)

Wenn du zwei Kinder hast, kann das Standesamt wahrscheinlich nichts 
dagegen machen, wenn du beiden denselben Namen gibst.
Empfehlen würde ich es trotzdem nicht, auch nicht bei Zwillingen.

von (prx) A. K. (prx)


Lesenswert?

Klaus Wachtler schrieb:

> ja, aber es ist eine Stilfrage, ob man das möchte :-)

Selbst bei deinen Zwillingen handelt es trotz aller Ähnlichkeit um 
getrennte Individuen. Hier aber bezeichnet es exakt die selbe Sache. 
Daher habe ich damit kein Problem.

Etwas ungünstig wäre das jedoch bei
1
typedef struct listeA {
2
   ...
3
} listeB;
4
5
typedef struct listeB {
6
   ...
7
} listeA;

von Klaus W. (mfgkw)


Lesenswert?

naja, ist halt Geschmackssache.

Wenn das irgendwas in struct irgendwas Teil von struct irgendwas ist und 
nur damit zusammen zu einem Typ wird, dann ist das irgendwas, das ohne 
struct ein Typname ist, für mich nicht dasselbe.
Wenn man z.B. jeden Typ mit angehängtem _t kennzeichnet, was macht man 
dann mit dem Ding nach struct?
Es ist kein eigenständiger Typ, trotzdem ein _t anhängen?
Aber egal...

In C++ hat es sich eh erledigt, weil man das struct bei der Verwendung 
des Typs weglassen kann und damit auch das typedef in diesem Fall 
obsolet wird:
1
struct liste_t
2
{
3
  int      i;
4
  liste_t *next;  // geht ohne struct davor nicht in C, nur C++
5
};
6
7
liste_t  *knurzel = NULL; // geht ohne struct davor nicht in C, nur C++

von Klaus W. (mfgkw)


Lesenswert?

PS: ich weiß natürlich, daß das Korinthenkackerei ist ist und bestehe 
jetzt nicht auf eine endgültigen Lösung der Frage :-)

von (prx) A. K. (prx)


Lesenswert?

Aber selbst in C++ ist es zulässig, was bei eine C/C++ Mix eine gewisse 
Rolle spielt.

von Klaus W. (mfgkw)


Lesenswert?

Zulässig ja, was aber die Frage nach dem Stil nicht beantwortet :-)

von (prx) A. K. (prx)


Lesenswert?

Klaus Wachtler schrieb:

> PS: ich weiß natürlich, daß das Korinthenkackerei ist ist und bestehe
> jetzt nicht auf eine endgültigen Lösung der Frage :-)

Hehe... Es ist ausser uns eh noch niemand wach, wir stören also 
niemanden mit der Korinthenkackerei. ;-)

von Klaus W. (mfgkw)


Lesenswert?

A. K. schrieb:
> typedef struct listeA {
>    ...
> } listeB;
>
> typedef struct listeB {
>    ...
> } listeA;

Das geht übrigens bei C noch durch, bei C++ nicht mehr.

von (prx) A. K. (prx)


Lesenswert?

Klaus Wachtler schrieb:

> Zulässig ja, was aber die Frage nach dem Stil nicht beantwortet :-)

Gewissermassen schon. In C++ musste man nämlich für genau diese 
Möglichkeit eigens eine Extrawurst braten, da es sich anders als in C 
formal um eine doppelte Definition des gleichen Namens im gleichen 
Namespace handelt. Das hätte Stroustrup nicht gemacht, wenn bloss ein 
einzelner Spassvogel in mikrocontroller.net das elegant gefunden hätte.

von Klaus W. (mfgkw)


Lesenswert?

A. K. schrieb:
> Es ist ausser uns eh noch niemand wach,

Vielleicht mal ein neues Feature bei den Betreibern einfordern:
Eine Option, tagsüber den Unsinn ausblenden zu können, der nachts so 
entsteht?

von Klaus W. (mfgkw)


Lesenswert?

A. K. schrieb:
> einzelner Spassvogel

Wer?

von Klaus W. (mfgkw)


Lesenswert?

Aber im Ernst: daß man in C++ das Geeier abgeschafft hat mit dem 
unvollständigen Typnamen und deshalb immer struct irgendwas... schreiben 
muß, ist doch nett. Dadurch entfällt ja auch gleich das typedef und es 
wird schon etwas übersichtlicher.
Es stören sich doch nur die Leute daran, die unbedingt seit '45 alles 
mit typedef machen wollen.

von (prx) A. K. (prx)


Lesenswert?

Klaus Wachtler schrieb:

> A. K. schrieb:
>> einzelner Spassvogel
>
> Wer?

Uns Uhu erkennt Spass meist nichtmal dann, wenn er mit dem Holzhammer 
daherkommt, also dieser Vogel kanns nicht sein.

von (prx) A. K. (prx)


Lesenswert?

Klaus Wachtler schrieb:

> Aber im Ernst: daß man in C++ das Geeier abgeschafft hat mit dem
> unvollständigen Typnamen und deshalb immer struct irgendwas... schreiben
> muß, ist doch nett.

Keine Frage. Das war sinnvoll.

Allerdings hatte Stroustrup an anderer Stelle dafür eigens ein paar neue 
faule Eier ins Nest gelegt. Meine Begeisterung über seinen Sinn für 
Syntax hält sich daher in Grenzen.

von Klaus W. (mfgkw)


Lesenswert?

Da wiederum gebe ich dir Recht.

Wenn gar nichts mehr hilft, muß man irgendwo ein typename hinschreiben, 
weil der Compiler selber nicht mehr weiter weiß. In solchen Fällen 
zweifle ich auch manchmal etwas.

Demnächst kommt für den entgegengesetzten Fall vielleicht ein 
nottypename (bzw. um wie gewohnt Schlüsselwörter einzusparen ein 
¨typename oder !typename oder mal wieder auto) zur Klärung, wenn etwas 
eben kein Typ ist.

von Nico S. (nico22)


Lesenswert?

Klaus Wachtler schrieb:
> Wenn gar nichts mehr hilft, muß man irgendwo ein typename hinschreiben,
> weil der Compiler selber nicht mehr weiter weiß.

Das hat nicht unbedingt damit etwas zu tun, dass der Compiler das nicht 
mehr weiß, sondern dass in diesen Fällen das "typename" vom Standard 
gefordert ist, selbst wenn der Compiler es besser wüsste:

http://pages.cs.wisc.edu/~driscoll/typename.html

> This rule even holds if it doesn't make sense even if it doesn't make sense to
> refer to a non-type.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.