Zwei Fragen zu verketteten Listen

Gast #2566769
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
#2566795
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
#2566812
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'
#2566815
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.
#2566819
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;
#2566824
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++
#2566831
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.
#2566834
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.
#2566836
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.
#2566840
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.
#2566952
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren