вторник, 9 сентября 2008 г.

Пример хэш-таблиц в C

В стандарной библиотеке языка C, как известно, нет поддержки хэш-таблиц (она есть в STL C++, но этот язык - не в моем вкусе), но зато GNU libc сожержит функции работы с db-файлами (используются, например, sendmail). Причем, как указано в man-страницк на функцию dbopen, аргумент <имя файла> может принимать значение NULL - и при этом таблица на будет сохраняться на диске. Что нам на самом деле и надо. Далее следует маленький (абсолютно практически бесполезный) пример, демонстрирующий, как такое использование функции позволяет вполне гладко работать с хэш-таблицей на гольном C.

Заголовочные файлы соответствуют FreeBSD 7.0 - в других сиситемах стОит посмотреть man dbopen(3).

/* программа создает хэш-таблицу, ключами которой являются аргументы коммандной строки, а значениеями - пара (порядковый номер аргумента, длина в символах */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#include <sys/types.h>
#include <fcntl.h>
#include <limits.h>


/* такую структуру имеют данные в нашей таблице */
struct mydata {
int num;
size_t len;
};


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

DB* db;

/* и ключ, и значение в db-файле имеют тип DBT - это структура из size_t size и void* data - как видно, реальные данные могут иметь любой размер и любой тип */
DBT ky, va;
int i;
struct mydata d;

/* первый NULL в аргументах - это как раз знак того, что таблицу не надо сохранять на диск */
if ((db = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL)) == NULL)
perror("dbopen");
exit(1);
}
for (i = 0; i < argc; i++) {

/* заполняем ключ */

ky.size = strlen(argv[i]);
ky.data = (void*)argv[i];

/* значение */
va.size = sizeof(d);
d.num = i;
d.len = ky.size;
va.data = (void*)&d;

/* и пишем в таблицу */
db->put(db, &ky, &va, 0);
}

/* а теперь, чтобы убедиться, что у нас все получилось, выведем ключи и значения в обратном порядке */
for (i = argc - 1; i >= 0; i--) {

/* опять надо заполнить ключ */
ky.size = strlen(argv[i]);
ky.data = (void*)argv[i];

/* зато значение заполнится само */
if (!db->get(db, &ky, &va, 0)) {
struct mydata* md = (struct mydata*)(va.data);
printf("key: %s value: (num: %d, len: %ld)\n", (char*)(ky.data), d->num, d->len);
}
}

/* таблица нам надоела - закроем ее */
db->close(db);
return 0;
}


Комментариев нет: