'Многоцелевая' реализация связанного списка в чистом C

Ничего не зная об API GoDaddy, единственное, что я могу предложить, это, возможно, проверить правильность порта. Это может потребовать защиты транспортного уровня, в этом случае порт 25 будет закрыт.

Например, я думаю, что SMTP-сервер office365 (smtp.office365.com) требует защищенного SMTP и использует порт 587.

28
задан Bill the Lizard 15 September 2012 в 02:55
поделиться

9 ответов

void * - это небольшая боль в связанном списке, так как вам приходится управлять это выделение отдельно для самого списка. Один из подходов, который я использовал в прошлом, - это иметь структуру «переменного размера», например:

typedef struct _tNode {
    struct _tNode *prev;
    struct _tNode *next;
    int payloadType;
    char payload[1];  // or use different type for alignment.
} tNode;

Теперь я понимаю, что не выглядит переменного размера, но давайте выделим структуру таким образом:

typedef struct {
    char Name[30];
    char Addr[50];
} tPerson;
tNode *node = malloc (sizeof (tNode) - 1 + sizeof (tPerson));

Теперь у вас есть узел, который для всех намерений и целей выглядит следующим образом:

typedef struct _tNode {
    struct _tNode *prev;
    struct _tNode *next;
    int payloadType;
    char Name[30];
    char Addr[50];
} tNode;

или в графической форме (где [n] означает n байт):

+----------------+
|    prev[4]     |
+----------------+
|    next[4]     |
+----------------+
| payloadType[4] |                
+----------------+                +----------+
|   payload[1]   | <- overlap ->  | Name[30] |
+----------------+                +----------+
                                  | Addr[50] |
                                  +----------+

, что если вы знаете, как правильно обращаться с полезной нагрузкой. Это можно сделать следующим образом:

node->prev = NULL;
node->next = NULL;
node->payloadType = PLTYP_PERSON;
tPerson *person = &(node->payload); // cast for easy changes to payload.
strcpy (person->Name, "Bob Smith");
strcpy (person->Addr, "7 Station St");

Эта строка приведения просто преобразует адрес символа полезной нагрузки (в типе tNode ) в адрес фактического tPerson Тип полезной нагрузки.

Используя этот метод, вы можете переносить любой тип полезной нагрузки в узле, даже разные типы полезной нагрузки в каждом узле , без потери пространства объединения. Эта потеря может быть замечена в следующем:

union {
    int x;
    char y[100];
} u;

где 96 байтов тратятся впустую каждый раз, когда вы сохраняете целочисленный тип в списке (для 4-байтового целого).

Тип полезной нагрузки в tNode позволяет легко определить, какой тип полезной нагрузки несет этот узел, поэтому ваш код может решить, как его обработать. Вы можете использовать что-то вроде:

#define PAYLOAD_UNKNOWN     0
#define PAYLOAD_MANAGER     1
#define PAYLOAD_EMPLOYEE    2
#define PAYLOAD_CONTRACTOR  3

или (возможно, лучше):

typedef enum {
    PAYLOAD_UNKNOWN,
    PAYLOAD_MANAGER,
    PAYLOAD_EMPLOYEE,
    PAYLOAD_CONTRACTOR
} tPayLoad;
33
ответ дан paxdiablo 28 November 2019 в 03:00
поделиться

Мои $ .002:

  • Составление списка пустых указателей (вроде нелегального; сложнее для отладки)

Это не такой уж плохой выбор, IMHO, если вы должны писать на C. Вы можете добавить методы API, чтобы приложение могло предоставлять метод print () для простоты отладки. Подобные методы могут быть вызваны, когда (например) элементы добавляются или удаляются из списка. (Для связанных списков это обычно не требуется, но для более сложных структур данных - например, хеш-таблиц) - иногда это может быть спасением.)

  • Создание только одного списка, но с объединением в качестве элемента type ', содержащий все типы элементов, которые я буду использовать в программе (проще для отладки; тратит пространство, если элементы не одного размера)

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

  • Использование макроса препроцессора для регенерации кода для каждого типа в стиле SGLIB (sglib.sourceforge.net), «имитирующего» STL (творческое решение C ++); не тратит пространство; элементы имеют явный тип, которым они на самом деле являются, когда они возвращаются; любое изменение в коде списка может быть действительно драматичным)

Интригующая идея, но так как я не знаю SGLIB, я не могу сказать намного больше.

  • Ваша идея / решение

Я бы выбрал первый вариант.

т пустое пространство; элементы имеют явный тип, которым они на самом деле являются, когда они возвращаются; любое изменение в коде списка может быть действительно драматичным)

Интригующая идея, но так как я не знаю SGLIB, я не могу сказать намного больше.

  • Ваша идея / решение

Я бы пошел с первый выбор.

т пустое пространство; элементы имеют явный тип, которым они на самом деле являются, когда они возвращаются; любое изменение в коде списка может быть действительно драматичным)

Интригующая идея, но так как я не знаю SGLIB, я не могу сказать намного больше.

  • Ваша идея / решение

Я бы пошел с первый выбор.

8
ответ дан Dan Breslau 28 November 2019 в 03:00
поделиться

I Мы делали это в прошлом, в нашем коде (который с тех пор был преобразован в C ++), и в то время решили использовать подход void *. Я просто сделал это для гибкости - мы почти всегда сохраняли указатель в списке, а простота решения и удобство его использования перевешивали (для меня) недостатки других подходов. был один раз, когда он вызвал какую-то неприятную ошибку, которую было трудно отладить, так что это определенно не идеальное решение. Я думаю, что это все еще тот, который я бы взял, если бы я делал это снова сейчас.

6
ответ дан Reed Copsey 28 November 2019 в 03:00
поделиться

I haven't coded C in years but GLib claims to provide "a large set of utility functions for strings and common data structures", among which are linked lists.

4
ответ дан Sean McSomething 28 November 2019 в 03:00
поделиться

Хотя заманчиво подумать о решении такого рода проблем с использованием методов другого языка, скажем, дженериков, на практике это редко победа. Вероятно, есть некоторые консервативные решения, которые делают это правильно большую часть времени (и сообщают вам в своей документации, когда они ошибаются), использование которых может упустить смысл задания, поэтому я бы дважды подумал об этом. Для очень небольшого числа случаев может быть целесообразным выполнить собственный, но для проекта любого разумного размера его вряд ли стоит отлаживать.

Скорее, при программировании на языке x вы должны использовать идиомы языка x. Не пишите Java, когда вы используете Python. Не пишите C, когда вы используете схему. Не пишите C ++, когда вы используете C99.

Я, я Возможно, в конечном итоге я буду использовать что-то вроде предложения Пакса, но на самом деле использую объединение char [1] и void * и int, чтобы сделать общие случаи удобными (и перечислимый флаг типа)

(я также, вероятно, в конечном итоге реализация дерева Фибоначчи, просто потому что это звучит аккуратно, и вы можете реализовать Деревья RB столько раз, пока он не потерял свой вкус, даже если это лучше для общих случаев, для которых он будет использоваться.)

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

Также возможно, что в конечном итоге будет реализовано дерево Фибоначчи, просто потому что это звучит аккуратно, и вы можете реализовать Деревья RB столько раз, пока он не потерял свой вкус, даже если это лучше для общих случаев, для которых оно будет использоваться.)

edit: Судя по вашему комментарию, похоже, у вас есть довольно хороший пример использования консервированного решения. Если ваш инструктор это допускает, а синтаксис, который он предлагает, вам удобнее, поверните его.

Также возможно, что в конечном итоге будет реализовано дерево Фибоначчи, просто потому что это звучит аккуратно, и вы можете реализовать Деревья RB столько раз, пока он не потерял свой вкус, даже если это лучше для общих случаев, для которых оно будет использоваться.)

edit: Судя по вашему комментарию, похоже, у вас есть довольно хороший пример использования консервированного решения. Если ваш инструктор это допускает, а синтаксис, который он предлагает, вам удобнее, поверните его.

1
ответ дан SingleNegationElimination 28 November 2019 в 03:00
поделиться

This is a good problem. There are two solutions I like:

  • Dave Hanson's C Interfaces and Implementations uses a list of void * pointers, which is good enough for me.

  • For my students, I wrote an awk script to generate type-specific list functions. Compared to preprocessor macros, it requires an extra build step, but the operation of the system is much more transparent to programmers without a lot of experience. And it really helps make the case for parametric polymorphism, which they see later in their curriculum.

    Here's what one set of functions looks like:

    int lengthEL (Explist *l);
    Exp* nthEL (Explist *l, unsigned n);
    Explist *mkEL (Exp *hd, Explist *tl);
    

    Скрипт awk - это ужас из 150 строк; он ищет код C для typedef s и генерирует набор функций списка для каждой из них. Это очень старый; Я мог бы, вероятно, сделать лучше сейчас: -)

Я бы не дал список союзов времени суток (или места на моем жестком диске). Это небезопасно и не расширяемо, поэтому вы можете просто использовать void * и покончить с этим.

1
ответ дан Norman Ramsey 28 November 2019 в 03:00
поделиться

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

Другие идеи: встроить интерпретатор Perl или Lisp.

Или пойти на полпути: связать с библиотекой Perl и сделать ее списком Perl SV или что-то.

0
ответ дан skiphoppy 28 November 2019 в 03:00
поделиться

Я, наверное, сам пойду с подходом к пустоте *, но мне пришло в голову, что ты можешь сохранить ваши данные в виде XML. Тогда в списке может быть только char * для данных (который вы будете анализировать по требованию для любых необходимых вам подэлементов) ...

0
ответ дан dicroce 28 November 2019 в 03:00
поделиться

Использование макроса препроцессора - лучший вариант. Связанный список ядра Linux - это отличная эффективная реализация списка с циклическими связями на языке C. Он очень портативен и прост в использовании. Здесь автономная версия заголовка list.h ядра Linux 2.6.29.

FreeBSD / OpenBSD sys / queue - еще один хороший вариант для связанного списка на основе общих макросов

6
ответ дан 28 November 2019 в 03:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: