Можно ли иметь связанный список разных типов данных? - PullRequest
6 голосов
/ 15 июля 2009

Это просто еще один вопрос для интервью.

Можем ли мы иметь связанный список разных типов данных, то есть каждый элемент в связанном списке может иметь различную структуру или элементы объединения? Если это возможно, не могли бы вы объяснить на примере?

Ответы [ 10 ]

15 голосов
/ 15 июля 2009

Используйте объединение для создания типа данных

union u_tag{
    char ch;
    int d;
    double dl;
};

struct node {
    char type;
    union u_tag u;
    struct node *next;
};

Используйте нод struct для создания связанного списка. Тип решает, что тип данных данных.

Харша Т, Бангалор

14 голосов
/ 15 июля 2009

Ну, в связанном списке вы не должны связывать как для одинаковых структур вместе. Пока у них есть соответствующие указатели вперед и / или назад, у вас все в порядке. Например:

struct BaseLink
{
   BaseLink* pNext;
   BaseLink* pPrev;
   int       typeId;
};

struct StringLink
{
    BaseLink baseLink;
    char* pString;
};

struct IntLink
{
    BaseLink baseLink;
    int   nInt;
};

Таким образом, у вас будет связанный список, который идет от BaseLink к BaseLink. Дополнительные данные не проблема. Вы хотите видеть это как StringLink? Затем приведите BaseLink к StringLink.

Просто помните, что вам нужна какая-то форма typeid, чтобы вы знали, на что их наложить, когда придете.

7 голосов
/ 15 июля 2009

Вы можете использовать тип объединения:

enum type_tag {INT_TYPE, DOUBLE_TYPE, STRING_TYPE, R1_TYPE, R2_TYPE, ...};
struct node {
  union {
    int ival;
    double dval;
    char *sval;
    struct recordType1 r1val;
    struct recordType2 r2val;
    ...
  } data;
  enum type_tag dataType;
  struct node *prev;
  struct node *next;
};

Другой метод, который я исследовал, - это использовать void * для данных и прикреплять указатели на функции, которые обрабатывают информацию о типах:

/**
 * Define a key type for indexing and searching
 */
typedef ... key_t;                 

/**
 * Define the list node type
 */
struct node {
  void *data;
  struct node *prev;
  struct node *next;
  void *(*cpy)(void *);            // make a deep copy of the data
  void (*del)(void *);             // delete the data
  char *(*dpy)(void *);            // format the data for display as a string
  int (*match)(void *, key_t);     // match against a key value
};

/**
 * Define functions for handling a specific data type
 */
void *copyARecordType(void *data)
{
  struct aRecordType v = *(struct aRecordType *) data;
  struct aRecordType *new = malloc(sizeof *new);
  if (new)
  {
    // copy elements of v to new
  }
  return new;
}

void deleteARecordType(void *data) {...}
char *displayARecordType(void *data) {...}
int matchARecordType(void *data, key_t key) {...}

/**
 * Define functions for handling a different type
 */
void *copyADifferentRecordType(void *data) {...}
void deleteADifferentRecordType(void *data) {...}
char *displayADifferentRecordType(void *data) {...}
int matchADifferentRecordType(void *data, key_t key) {...}

/**
 * Function for creating new list nodes
 */
struct node *createNode(void *data, void *(*cpy)(void *), void (*del)(void *), 
    char *(*dpy)(void *), int (*match)(void *, key_t))
{
  struct node *new = malloc(sizeof *new);
  if (new)
  {
    new->cpy = cpy;
    new->del = del;
    new->dpy = dpy;
    new->match = match;
    new->data = new->cpy(data);
    new->prev = new->next = NULL;
  }
  return new;
}

/**
 * Function for deleting list nodes
 */
void deleteNode(struct node *p)
{
  if (p)
    p->del(p->data);
  free(p);
}

/**
 * Add new node to the list; for this example, we just add to the end
 * as in a FIFO queue.  
 */
void addNode(struct node *head, void *data, void *(*cpy)(void*), 
  void (*del)(void *), char *(*dpy)(void *), int (*match)(void*, key_t))
{
  struct node *new = createNode(data, cpy, del, dpy, match);
  if (!head->next)
    head->next = new;
  else
  {
    struct node *cur = head->next;
    while (cur->next != NULL)
      cur = cur->next;
    cur->next = new;
    new->prev = cur;
  }
}

/**
 * Examples of how all of this would be used.
 */
int main(void)
{
  struct aRecordType r1 = {...};
  struct aDifferentRecordType r2 = {...};

  struct node list, *p;
  addNode(&list, &r1, copyARecordType, deleteARecordType, displayARecordType,
    matchARecordType);
  addNode(&list, &r2, copyADifferentRecordType, deleteADifferentRecordType,
    displayADifferentRecordType, matchADifferentRecordType);
  p = list.next;
  while (p)
  {
    printf("Data at node %p: %s\n", (void*) p, p->dpy(p->data));
    p = p->next;
  }
  return 0;
}

Очевидно, я пропустил некоторые проверки и обработку ошибок из этого примера, и я не сомневаюсь, что есть множество проблем с ним, но это должно быть иллюстративно.

1 голос
/ 05 апреля 2015

Да, я делаю это, определяя значение элемента списка как пустой указатель void*. Чтобы узнать тип, хранящийся в каждом элементе списка, у меня также есть поле .type, поэтому я знаю, как разыменовать, на что указывает указатель для каждого элемента.

struct node {
    struct node* next;
    int type;
    void* value;
};

Вот полный пример этого:

//                                                                                                                                                                                          
// An exercise to play with a struct that stores anything using a void* field.                                                                                                              
//                                                                                                                                                                                          

#include <stdio.h>

#define TRUE 1

int TYPE_INT = 0;
int TYPE_STRING = 1;
int TYPE_BOOLEAN = 2;
int TYPE_PERSON = 3;

struct node {
  struct node* next;
  int type;
  void* value;
};

struct person {
  char* name;
  int age;
};

int main(int args, char **argv) {

  struct person aPerson;
  aPerson.name = "Angel";
  aPerson.age = 35;

  // Define a linked list of objects.                                                                                                                                                       
  // We use that .type field to know what we're dealing                                                                                                                                     
  // with on every iteration. On .value we store our values.                                                                                                                                
  struct node nodes[] = {
    { .next = &nodes[1], .type = TYPE_INT    , .value=1                   },
    { .next = &nodes[2], .type = TYPE_STRING , .value="anyfing, anyfing!" },
    { .next = &nodes[3], .type = TYPE_PERSON , .value=&aPerson            },
    { .next = NULL     , .type = TYPE_BOOLEAN, .value=TRUE                }
  };

  // We iterate through the list                                                                                                                                                            
  for ( struct node *currentNode = &nodes[0]; currentNode;  currentNode = currentNode->next) {
    int currentType = (*currentNode).type;
    if (currentType == TYPE_INT) {
      printf("%s: %d\n", "- INTEGER", (*currentNode).value); // just playing with syntax, same as currentNode->value                                                                        
    } else if (currentType == TYPE_STRING) {
      printf("%s: %s\n", "- STRING", currentNode->value);
    } else if (currentType == TYPE_BOOLEAN) {
      printf("%s: %d\n", "- BOOLEAN (true:1, false:0)", currentNode->value);
    } else if (currentType == TYPE_PERSON) {
        // since we're using void*, we end up with a pointer to struct person, which we *dereference                                                                                        
        // into a struct in the stack.                                                                                                                                                      
        struct person currentPerson = *(struct person*) currentNode->value;
        printf("%s: %s (%d)\n","- TYPE_PERSON", currentPerson.name, currentPerson.age);
      }
  }

    return 0;
}

Ожидаемый результат:

- INTEGER: 1
- STRING: anyfing, anyfing!
- TYPE_PERSON: Angel (35)
- BOOLEAN (true:1, false:0): 1
1 голос
/ 31 мая 2014

Если вы не хотите указывать тип каждого узла в списке с помощью решения объединения, вы всегда можете просто сохранить данные в символе * и использовать указатели функций для конкретных типов в качестве параметров для чувствительных к типу операций. такие как печать или сортировка списка. Таким образом, вам не нужно беспокоиться о том, какой узел какого типа, и вы можете просто приводить данные так, как вам нравится.

/* data types */

typedef struct list_node list_node;
struct list_node {
    char *data;
    list_node *next;
    list_node *prev;
};

typedef struct list list;
struct list {
    list_node *head;
    list_node *tail;
    size_t size;
};

/* type sensitive functions */

int list_sort(list *l, int (*compar)(const void*, const void*));
int list_print(list *l, void (*print)(char *data));
1 голос
/ 15 июля 2009

У каждого узла в связанном списке может быть пустота *, указывающая на ваши данные. Вам решать, как определить тип данных, на который указывает указатель.

0 голосов
/ 16 июля 2009

Я использую эти макросы, которые я написал, для создания общих связанных списков. Вы просто создаете свою собственную структуру и используете макрос list_link где-то в качестве члена структуры. Дайте этому макросу один аргумент с именем struct (без ключевого слова struct). Это реализует двусвязный список без фиктивного узла (например, последний узел связывается с первым узлом). Якорь - это указатель на первый узел, который вначале инициализируется list_init(anchor) путем присвоения ему lvalue (разыменованный указатель на него - lvalue). Затем вы можете использовать другие макросы в заголовке. Прочитайте источник для комментариев о каждой доступной функции макроса. Это реализовано на 100% в макросах.

http://phil.ipal.org/pre-release/list-0.0.5.tar.bz2

0 голосов
/ 15 июля 2009

Просто к сведению, в C # вы можете использовать Object в качестве члена данных.

class Node
{
     Node next;
     Object Data;
}

Пользователь может затем использовать что-то вроде этого, чтобы узнать, какие Object Node хранит:

if (obj.GetType() == this.GetType()) //
{

}
0 голосов
/ 15 июля 2009

На самом деле вам не нужно помещать указатель первым в структуре, вы можете поместить его где угодно, а затем найти начало структуры с помощью контейнера containerof (). Ядро linux делает это с помощью связанных списков.

http://isis.poly.edu/kulesh/stuff/src/klist/

0 голосов
/ 15 июля 2009

Как уже говорилось, у вас может быть узел с этим вопросом в пустоте *. Я предлагаю использовать что-то, чтобы знать о вашем типе:

typedef struct
{
    /* linked list stuff here */    

    char m_type;
    void* m_data;
} 
Node;

См. этот вопрос .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...