通过 POSIX tdelete() 访问节点数据

发布于 2024-09-18 22:00:06 字数 641 浏览 7 评论 0原文

POSIX 二叉树函数 的联机帮助页包括以下语句:

tdelete() 返回指向已删除项目的父级的指针,如果未找到该项目,则返回 NULL

tdelete() 释放树中节点所需的内存。用户 负责释放相应数据的内存。

这意味着无法通过 tdelete() 调用访问给定键的节点数据。需要调用 tfind() (而不是 tsearch() 以免添加给定的键),销毁节点数据,然后调用 < code>tdelete() 使用相同的键从二叉树中删除节点。

我的解释正确吗?有什么办法可以解决我认为这种方法的局限性吗?

  1. 如果键是堆分配的,则在删除节点之前无法释放它(或使其对正在使用的比较函数无用)。这需要调用 tfind() 获取指向数据的指针,调用 tdelete() 删除节点,然后销毁从 tfind() 检索到的数据调用。
  2. 需要两次查找才能删除节点并销毁其包含的数据。

The manpage for the POSIX binary tree functions includes the following statements:

tdelete() returns a pointer to the parent of the item deleted, or NULL if the item was not found.

tdelete() frees the memory required for the node in the tree. The user
is responsible for freeing the memory for the corresponding data.

This means that there is no way to get access to a node's data for a given key from a tdelete() call. One would be required to call tfind() (rather than tsearch() so as not to add the given key), perform destruction of the node's data, and then call tdelete() with the same key to remove the node from the binary tree.

Have I interpreted this correctly? Is there some way around what I perceive to be limitations with this approach?

  1. If the key is heap-allocated, it can't be freed (or made useless to the comparison function in use) before the node is deleted. This requires calling tfind() to obtain a pointer to the data, tdelete() to remove the node, and then destroying the data retrieved from the tfind() call.
  2. Two lookups are required to delete a node and destroy it's enclosed data.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

暗地喜欢 2024-09-25 22:00:06

马特,我认为你的解释是正确的。对于单独删除元素来说,这似乎是不可避免的。 (但无论如何,对于 \Omega \log N 成本的操作来说,它只是 2 的一个因素;)

我很长一段时间没有使用这些函数,但通过旧代码,看起来你可以避免部分开销,如果你通过使用两次 twalk 调用一次性销毁树(如果这是您的情况):

  1. 计算其中元素的数量 N
    通过适当调用 twalk 为您的树
  2. 分配一个大小为 N 的数组
    指向树项的指针
  3. 通过第二次 twalk 填充此数组
    穿过树
  4. 穿过这个
    数组并首先对每个树节点
    删除其数据,然后删除节点
    如果您确实需要这样的东西,

您可以在我们的库中找到这些调用的线程安全 C++ 封装 parXXL 在目录 par/sys 中。

Matt, I think that your interpretation is correct. For individual deletion of elements this seems unavoidable. (But anyhow it is only a factor of 2 for an operation of \Omega \log N cost ;)

Long time that I didn't use these function, but looking through old code it looks that you can avoid part of the overhead if you destroy the tree all at once (if this is your case) by using two calls of twalk:

  1. count the number N of elements in
    your tree with an appropriate call to twalk
  2. allocate an array of size N of
    pointers to tree items
  3. fill this array by a second twalk
    through the tree
  4. run through this
    array and for each tree node first
    delete its data and then the node
    itself

If your really need such a thing you can find a thread safe C++ encapsulation of these calls in our library parXXL in the directory par/sys.

め可乐爱微笑 2024-09-25 22:00:06

我以前从未使用过这组功能,但你似乎是正确的。但是,我会尝试使用从 tfind() 返回的指针作为 tdelete() 的 rootp 参数。这将使第二次搜索基本上成为无操作。 AFAICT,数据结构允许将任何节点视为根,因此您可以找到该节点,然后从以您找到的节点为根的子树中删除它。

I've never used this set of functions before, but you appear to be correct. However, I would try using the pointer returned from tfind() as the rootp argument to tdelete(). This should make the second search essentially a no-op. AFAICT, the data structure allows any node to be treated as a root so you would find the node and then delete it from the subtree rooted at the node that you found.

因为看清所以看轻 2024-09-25 22:00:06

和 D.Shawley 一样,我以前从未使用过这些函数,所以我编写了一个泄漏内存的小测试程序。

#include <search.h>
#include <stdio.h>
#include <stdlib.h>

int fx(const void *a, const void *b) {
  if (*(int*)a < *(int*)b) return -1;
  return (*(int*)a > *(int*)b);
}

int main(void) {
  int i, *elem;
  void *root = NULL, *val;

  for (i = 0; i < 10; i++) {
    elem = malloc(sizeof *elem); /* LEAK, leak, leak */
    *elem = i/2;
    val = tsearch(elem, &root, fx); /* assume all OK */
    printf("i: %d; elem: %p (%d); val: %p (%x)\n",
           i, (void*)elem, *elem, val, *(int*)val);
  }

  for (i = -1; i < 6; i++) {
    val = tfind(&i, &root, fx);
    printf("i: %d; ", i);
    if (val) {
      printf("@%p (%x)\n", val, *(int*)val);
    } else {
      printf("NOT FOUND\n");
    }
  }

  return 0;
}

运行它输出

i: 0; elem: 0xcb8010 (0); val: 0xcb8030 (cb8010)
i: 1; elem: 0xcb8060 (0); val: 0xcb8030 (cb8010)
i: 2; elem: 0xcb8080 (1); val: 0xcb80a0 (cb8080)
i: 3; elem: 0xcb80d0 (1); val: 0xcb80a0 (cb8080)
i: 4; elem: 0xcb80f0 (2); val: 0xcb8110 (cb80f0)
i: 5; elem: 0xcb8140 (2); val: 0xcb8110 (cb80f0)
i: 6; elem: 0xcb8160 (3); val: 0xcb8180 (cb8160)
i: 7; elem: 0xcb81b0 (3); val: 0xcb8180 (cb8160)
i: 8; elem: 0xcb81d0 (4); val: 0xcb81f0 (cb81d0)
i: 9; elem: 0xcb8220 (4); val: 0xcb81f0 (cb81d0)
i: -1; NOT FOUND
i: 0; @0xcb8030 (cb8010)
i: 1; @0xcb80a0 (cb8080)
i: 2; @0xcb8110 (cb80f0)
i: 3; @0xcb8180 (cb8160)
i: 4; @0xcb81f0 (cb81d0)
i: 5; NOT FOUND

显然“二叉树内部”的值是指向数据的指针的副本。副本由 函数管理,数据应由程序管理。

内存0xcb8030应该通过调用tdelete()来释放,相应的0xcb8010(elem值为0)应该由程序释放(以及之前丢失的0xcb8060)。

Like D.Shawley, I have never used these function before, so I wrote a little test program that leaks memory.

#include <search.h>
#include <stdio.h>
#include <stdlib.h>

int fx(const void *a, const void *b) {
  if (*(int*)a < *(int*)b) return -1;
  return (*(int*)a > *(int*)b);
}

int main(void) {
  int i, *elem;
  void *root = NULL, *val;

  for (i = 0; i < 10; i++) {
    elem = malloc(sizeof *elem); /* LEAK, leak, leak */
    *elem = i/2;
    val = tsearch(elem, &root, fx); /* assume all OK */
    printf("i: %d; elem: %p (%d); val: %p (%x)\n",
           i, (void*)elem, *elem, val, *(int*)val);
  }

  for (i = -1; i < 6; i++) {
    val = tfind(&i, &root, fx);
    printf("i: %d; ", i);
    if (val) {
      printf("@%p (%x)\n", val, *(int*)val);
    } else {
      printf("NOT FOUND\n");
    }
  }

  return 0;
}

Running it outputs

i: 0; elem: 0xcb8010 (0); val: 0xcb8030 (cb8010)
i: 1; elem: 0xcb8060 (0); val: 0xcb8030 (cb8010)
i: 2; elem: 0xcb8080 (1); val: 0xcb80a0 (cb8080)
i: 3; elem: 0xcb80d0 (1); val: 0xcb80a0 (cb8080)
i: 4; elem: 0xcb80f0 (2); val: 0xcb8110 (cb80f0)
i: 5; elem: 0xcb8140 (2); val: 0xcb8110 (cb80f0)
i: 6; elem: 0xcb8160 (3); val: 0xcb8180 (cb8160)
i: 7; elem: 0xcb81b0 (3); val: 0xcb8180 (cb8160)
i: 8; elem: 0xcb81d0 (4); val: 0xcb81f0 (cb81d0)
i: 9; elem: 0xcb8220 (4); val: 0xcb81f0 (cb81d0)
i: -1; NOT FOUND
i: 0; @0xcb8030 (cb8010)
i: 1; @0xcb80a0 (cb8080)
i: 2; @0xcb8110 (cb80f0)
i: 3; @0xcb8180 (cb8160)
i: 4; @0xcb81f0 (cb81d0)
i: 5; NOT FOUND

Apparently the values "inside the binary tree" are copies of pointers to the data. The copies are managed by the <search.h> functions and the data should be managed by the program.

The memory 0xcb8030 should be freed by a call to tdelete(), the corresponding 0xcb8010 (elem with value 0) should be freed by the program (as well as the previously lost 0xcb8060).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文