Читаем Linux программирование в примерах полностью

ЗАМЕЧАНИЕ. Поскольку функции деревьев хранят указатели, тщательно позаботьтесь о том, чтобы не использовать realloc() для значений, которые были использованы в качестве ключей! realloc() может переместить данные, вернув новый указатель, но процедуры деревьев все равно сохранят висящие (dangling) указатели на старые данные.

<p>14.4.4. Поиск по дереву и использование возвращенного указателя: <code>tfind()</code> и <code>tsearch()</code></p>

Функции tfind() и tsearch() осуществляют поиск в двоичном дереве по данному ключу. Они принимают тот же самый набор аргументов: ключ для поиска key. указатель на корень дерева, rootp; и compare, указатель на функцию сравнения. Обе функции возвращают указатель на вершину, которая соответствует key.

Как именно использовать указатель, возвращенный tfind() и tsearch()? Во всяком случае, на что именно он указывает? Ответ заключается в том, что он указывает на вершину в дереве. Это внутренний тип; вы не можете увидеть, как он определен. Однако, POSIX гарантирует, что этот указатель может быть приведен к указателю на указатель на что бы то ни было, что вы используете в качестве ключа. Вот обрывочный код для демонстрации, а затем мы покажем, как это работает:

struct employee { /* Из главы 6 */

 char lastname[30];

 char firstname[30];

 long emp_id;

 time_t start_date;

};

/* emp_name_id_compare --- сравнение по имени, затем no ID */

int emp_name_id_compare(const void *e1p, const void *e2p) {

 /* ...также из главы 6, полностью представлено позже... */

}

struct employee key = { ... };

void *vp, *root;

struct employee *e;

/* ...заполнение данными... */

vp = tfind(&key, root, emp_name_id_compare);

if (vp != NULL) { /* it's there, use it */

 e = *((struct employee**)vp); /* Получить хранящиеся в дереве данные */

 /* использование данных в *е ... */

}

Как можно указатель на вершину использовать как указатель на указатель данных? Рассмотрим, как была бы реализована вершина двоичного дерева. В каждой вершине хранится по крайней мере указатель на элемент данных пользователя и указатели на потенциальные порожденные вершины справа и слева. Поэтому она должна выглядеть примерно так.

struct binary_tree {

 void *user_data; /* Указатель на данные пользователя */

 struct binary_tree *left; /* Порожденная вершина слева или NULL */

 struct binary_tree *right; /* Порожденная вершина справа или NULL */

/* ...здесь возможны другие поля... */

} node;

С и C++ гарантируют, что поля внутри структуры располагаются в порядке возрастания адресов. Таким образом, выражение '&node.left < &node.right' истинно. Более того, адрес структуры является также адресом ее первого поля (другими словами, игнорируя проблемы типов, '&node == &node.user_data').

Следовательно, концептуально 'е = *((struct employee**)vp);' означает:

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

2. '(struct employee**)vp' приводит адрес внутреннего указателя к нужному типу; он остается указателем на указатель, но в этот раз на struct employee. Помните, что приведение одного типа указателя к другому не изменяют значения (паттерна битов); оно меняет лишь способ интерпретации компилятором значения для анализа типов.

3. '*((struct employee**)vp)' разыменовывает вновь созданный struct employee**, возвращая годный к употреблению указатель struct employee*.

4. 'е = *((struct employee**)vp)' сохраняет это значение в е для непосредственного использования позже.

Идея проиллюстрирована на рис. 14.2.

Перейти на страницу:

Похожие книги

C++ Primer Plus
C++ Primer Plus

C++ Primer Plus is a carefully crafted, complete tutorial on one of the most significant and widely used programming languages today. An accessible and easy-to-use self-study guide, this book is appropriate for both serious students of programming as well as developers already proficient in other languages.The sixth edition of C++ Primer Plus has been updated and expanded to cover the latest developments in C++, including a detailed look at the new C++11 standard.Author and educator Stephen Prata has created an introduction to C++ that is instructive, clear, and insightful. Fundamental programming concepts are explained along with details of the C++ language. Many short, practical examples illustrate just one or two concepts at a time, encouraging readers to master new topics by immediately putting them to use.Review questions and programming exercises at the end of each chapter help readers zero in on the most critical information and digest the most difficult concepts.In C++ Primer Plus, you'll find depth, breadth, and a variety of teaching techniques and tools to enhance your learning:• A new detailed chapter on the changes and additional capabilities introduced in the C++11 standard• Complete, integrated discussion of both basic C language and additional C++ features• Clear guidance about when and why to use a feature• Hands-on learning with concise and simple examples that develop your understanding a concept or two at a time• Hundreds of practical sample programs• Review questions and programming exercises at the end of each chapter to test your understanding• Coverage of generic C++ gives you the greatest possible flexibility• Teaches the ISO standard, including discussions of templates, the Standard Template Library, the string class, exceptions, RTTI, and namespaces

Стивен Прата

Программирование, программы, базы данных