Generisk liste manipulasjon funksjon i C?

stemmer
5

Hva er en generisk liste manipulasjon funksjon i C? (Jeg så dette da jeg gikk gjennom noen materialer.)

Hva er forskjellen mellom denne funksjonen og en funksjon som kan akseptere elementer av noe slag?

Er de samme ...? Hvordan kan vi implementere dem individuelt om de ikke er like?

Publisert på 28/11/2008 klokken 16:49
kilden bruker
På andre språk...                            


7 svar

stemmer
5

En generell liste er sannsynlig å være alene bundet, og sannsynligvis forutsetter at elementene i listen har en struktur som dette:

typedef struct list_item list_item;

struct list_item
{
    list_item *next;
    ...data for node...
};

Ved hjelp av dette oppsettet kan du skrive funksjoner for å manipulere lister ved hjelp av bare de neste pekere.

Noen ganger ' ...data for node...' vil være en enkel ' void *'; som er, vil listeelementene inneholder pekere til neste node i listen (eller NULL hvis det ikke er neste node) og pekere til dataene.

typedef struct list list;

struct list
{
    list *next;
    void *data;
};

Siden du kan kaste noen peker til ' void *', kan du ha noen blanding av datatyper i listen - men koden må vite hvordan man skal håndtere dem.

Du spør om 'a' generisk liste funksjon, men det er trolig ikke en eneste en-funksjon-gjør-alt design, og absolutt ikke en enkel en. Det finnes en rekke mulige sett av funksjoner som kan gjøre generiske liste funksjoner. Ett sett, inspirert av Lisp, vil bestå av:

void *car(list *lp);    // Return the data for the first item on the list
list *cdr(list *lp);    // Return the tail of the list
list *cons(list *lp1, list *lp2);   // Construct a list from lists lp1 and lp2

list *cond(list *lp, void *data);  // Append data item to list

Du ønsker sannsynligvis å gi muligheten til å teste om listen er tom, og et par andre elementer.

En god utredning, riktignok i C ++, er funnet i Koenig er " drøvtygging på C ++ ". Ideene kan tilpasses til C ganske enkelt - det er ikke fryktelig vanskelig (selv om lagringsadministrasjon i C er vanskeligere enn i C ++).

Svarte 28/11/2008 kl. 16:53
kilden bruker

stemmer
6

C har ikke begrepet "generiske" tips eller gjenstander - det nærmeste du kan få er å bruke en void*peker. Hvis du ønsker en kodebit å være i stand til å håndtere alle datatype, du har ganske mye å bruke void*pekere. For datatyper med størrelser større enn en peker, kan du kaste mellom type og void*; for større datatyper, må du bruke dynamisk minne og har void*medlem peker på det dynamiske minnet. Bare se opp for minnelekkasjer!

typedef struct list_node {
  struct list_node *next;
  void *data;
} list_node;

void list_insert(list_node *node, void *data) {
  // ...
}

På den annen side, hvis du ønsker å generere kode for hver mulige datatype, må du gjøre det med makroer, og deretter instantiate makroer for hver datatype du kan bruke. For eksempel:

#define DEFINE_LIST(type) \
  typedef struct list_node_##type { \
    struct list_node_##type *next; \
    type data; \
  }

#define IMPLEMENT_LIST_INSERT(type) \
  void list_##type##_insert(list_node_##type *node, type data) { \
    ... \
  }

DEFINE_LIST(int);     // defines struct list_node_int
DEFINE_LIST(double);  // defines struct list_node_double
IMPLEMENT_LIST_INSERT(int);    // defines list_int_insert
IMPLEMENT_LIST_INSERT(double); // defines list_double_insert
Svarte 28/11/2008 kl. 17:06
kilden bruker

stemmer
2

Linux-kjernen har en interessant implementering av en generisk lenket liste i C på sin linux / list.h spissen. Det er en dobbelt-lenket liste med et hode node, brukes slik:

struct mystruct {
    ...
    /* Contains the next and prev pointers */
    struct list_head mylist;
    ...
    /* A single struct can be in several lists */
    struct list_head another_list;
    ...
};

struct list_head mylist_head;
struct list_head another_list_head;

Noen interessante ting i denne lite eksempel:

  • Listen noden er integrert i målet struct, trenger ingen datapeker.
  • Listen node trenger ikke å komme i begynnelsen av målet struct.
  • En enkelt struct kan være i flere lenkede lister samtidig.
  • De neste og prev pekere innenfor listen peker til struct list_head, ikke å målstrukturen (i eksemplet ovenfor, peker de &(foo->mylist)for den første listen, og &(foo->another_list)for den andre listen).

Alle liste manipulasjon funksjoner ta pekere til struct list_head(og de fleste ikke bryr seg i det hele tatt om det er separat hodet node eller en av de innebygde noder). For å komme fra den struct list_headtil målet struct, bruker man den list_entrymakro (som er den samme som den containter_ofmakro fra Linux / kernel.h header), som ekspanderer inn i en enkel peker subtraksjon.

Siden det er en dobbelt-lenket liste med et hode node, kan du i O(1):

  • Sjekk om listen er tom, eller hvis en node er ikke på noen liste.
  • Få noden etter eller før en annen node (hvis noden er hodet, får du den første eller siste elementet på listen).
  • Sett inn en node etter eller før en annen node (hvis noden er hodet, setter deg i begynnelsen eller slutten av listen).
  • Fjerne en node fra listen (du trenger bare pekeren til noden selv å gjøre dette).
  • Flere andre operasjoner.
Svarte 29/11/2008 kl. 16:12
kilden bruker

stemmer
1

C og dens standard bibliotek tilbyr ikke noen liste spesifikke funksjoner.

Men det er biblioteker med mange nyttige funksjoner for C som støtter datatyper som er kjent fra andre programmeringsspråk: http://library.gnome.org/devel/glib/2.18/glib-data-types.html

Svarte 30/11/2008 kl. 03:32
kilden bruker

stemmer
0

Som nevnt ovenfor, jeg prøvde å bruke MAKROER tilnærming til å lage liste manipulasjon funksjoner. Det er lett å lage SETT drift rutine, men vanskelig å lage Slett og travers operasjoner. Etter det listen struktur og INSERT rutine signatur:

#define LIST_DEFINE(type) \
    struct list_node_##type \
    { \
        type *data; \`
        struct list_node_##type *next; \
   };

LIST_INSERT(&ListHead,&Data, DataType);

Hvor:
ListHead- Leder for kjedet liste
Data- Data hvor en ny node vil bli opprettet og data blir innført i noden DataType- Er datatype av de data som sendes

Til info, jeg tildele minne i funksjonen og kopiere alle data som sendes i den nyopprettede noden og dem legge noden i lenket liste.

Nå, når en LIST_DELETErutine er opprettet, må noden som skal slettes vil bli identifisert ved hjelp av en unik identifikator innenfor data. Denne identifikator føres også i den MACROrutine som nøkkel som vil bli erstattet i MACROekspansjon. Rutinen signatur kan være:

LIST_DELETE(&ListHead, DataType, myvar->data->str, char*);

Hvor:
ListHead- Leder av lenket liste
DataType- Er datatypen data
myvar->data->str- Unik nøkkel
char*- Nøkkeltype

Nå, når nøkkelen er utvidet, at samme nøkkel kan ikke brukes til sammenligning som om vi skriver

if((keytype)ListHead->data->key == (keytype)key)

Det utvides til

ListHead->data->myvar->data->str == myvar->data->str

Og her er det ingen variabel som: ListHead->data->myvar->data->str

Så kan denne tilnærmingen ikke arbeide for å skrive slette rutiner og som traversering og søkerutiner også bruke unik nøkkel, vil samme problemet bli møtt på dem også.

Og på en urelatert notat, hvordan du finner den matchende logikk for unik nøkkel, som den unike nøkkelen kunne være noe.

Svarte 18/06/2012 kl. 05:30
kilden bruker

stemmer
2

For min lære kom jeg til å utvikle denne "generiske" -listen modul, trolig forenkle versjon av Linux-kjernen en, med ytterligere om uoppdagede bugs inkludert, og som bruker gcc extensions ... Eventuelle kommentarer er velkommen!

#ifndef _LISTE
#define _LISTE
#include <stdlib.h>
typedef struct liste_s {
  struct liste_s * suivant ;
} * liste ;


#define newl(t) ( (liste) malloc ( sizeof ( struct liste_s ) + sizeof ( t ) ) )
#define elt(l,t) ( * ( ( t * ) ( l + 1 ) ) )

#define liste_vide NULL
#define videp(l) ( l == liste_vide )
#define lvide() liste_vide
#define cons(e,l) \
  ({ liste res = newl(typeof(e)) ;      \
     res->suivant = l ; \
     elt(res,typeof(e)) = e ;           \
    res ; }) 

#define hd(l,t) ({ liste res = l ; if ( videp(res) ) exit ( EXIT_FAILURE ) ; elt(res,t) ; })
#define tl(l)   ({ liste res = l ; if ( videp(res) ) exit ( EXIT_FAILURE ) ; res->suivant ;})


#endif
Svarte 06/12/2012 kl. 11:02
kilden bruker

stemmer
0

Jeg har prøvd noe annet. Dette er et annet perspektiv hvordan å gå ombord problemet

Hvis vi har å følge struktur:

typedef struct token {
    int id;
    char *name;
    struct token *next;
} Token;

og vi trenger for å lage en funksjon som returnerer halen av en lenket liste, men funksjonen skal være generisk for noen lenket liste, så:

void* tail(void* list, void* (*f)(void *)) {
    void *head = list;

    while(f(head) != NULL) {
        head = f(head);
    }

    return head;
}

Nå vil det være nødvendig å lage en funksjon ansvarlig for å gjøre bro mellom vår skikk struct til en generisk brukervennlighet i halen funksjonen. På den måten har vi:

void* nextToken(void *a) {
    Token *t = (Token *) t;
    return (void *) (a->next);
}

Endelig kan vi bare bruke:

Token *listTokens;
(...)
Token *lastToken = tail(listTokens, nextToken);
Svarte 28/10/2015 kl. 04:00
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more