1

美好的一天,我刚开始学习 C++ 中的 void 指针,现在我正在编写二叉树,其中存储在每个节点中的值是指向值的 void 指针。

struct TreeNode
{
    int count;
    void* data;
    TreeNode *left;
    TreeNode *right;
    };

问题出现在第一个方法添加方法中。我的方法现在采用 int aa 参数并且不返回任何内容一开始我创建了新节点。为此,我需要将整数转换为 void。程序编译并且第一个元素正确添加到根目录 - 但是当我向方法发送另一个数字时,它再次存储在根目录中。所以如果我主要有类似 tree.addToTree(12); 树.addToTree(13); 比它首先存储 12 和在 else 语句(下面的代码)之后存储 13 的 root-> 数据如何。

void Tree::addToTree(int num)
{
    if(root==NULL){
        root= new TreeNode();
        root->data=#
        //((int *)(root->data)) = num;//i tried to convert to void* in this way but it give me segmentation fault
        root->left=NULL;
        root->right=NULL;
    }
    else{
        //here root value is already changed
        int *intPtr = static_cast<int*>(root->data);
        cout << "key2" << *intPtrT << endl;
        //TreeNode* current= insert(num,root);
    }
}

据我了解,那是因为我使用 &num 所以我的参数总是在一个地方撕裂,并且根“连接”到 &num 它也会改变。

我试图找到解决方案但没有成功。有没有办法 cat int 使指针无效?

4

3 回答 3

1

首先,您应该决定是按值存储数据还是按指向它的指针存储数据。

在第一种情况下,指针是没用的,您可以使用如下模板:

template <typename T>
struct TreeNode
{
  T data;
  ..
}

TreeNode<int> node;

这甚至适用于指针(如T *data ... data = new int())。

如果您想存储指向数据的指针,您还可以使用带有类型参数的模板或使用公共祖先类,然后使用所需类型对其进行子类化,例如:

class TreeData {

}

class TreeDataInt {
  int value;
}    

struct TreeNode
{
  TreeData *data;
  ..
}

最后,不鼓励使用int内部void*指针void*来实现,通常不鼓励在 C++ 中使用来实现多态性,因为您有许多其他更安全、更可靠的工具。

如果您真的想int在 a中存储 a ,void*那么您应该使用intptr_t可转换为指针的整数类型。例如:

#include <cstdint>

intptr_t value = 50;
node->data = static_cast<void*>(value);
intptr_t value2 = static_cast<intptr_t>(node->data);

这会将整数的值直接保存为void*. 这意味着您不能取消引用指针本身。

顺便说一句,这root->data=&num是错误的,因为您正在分配data自动分配变量的地址,该变量在退出其范围时将变为无效。

于 2013-11-06T17:40:02.263 回答
1

我看到的一个问题是这个(不是答案,只是一个问题)......在函数中

void Tree::addToTree(int num)
{

   if(root==NULL){
       root= new TreeNode();
       root->data=&num;

您将自动变量的地址分配给data. 问题是一旦函数退出,这个变量将不再存在(据说超出范围),所以你有一个所谓的悬空指针,即指向一个内存区域的指针不再使用或不再用于指针预期的原始目的。

您可以通过三种方式解决此问题。

  1. 如果您只想存储整数或任何宽度为 sizeof(void *) 的数据类型,您可以这样做root->data = (void *)num(注意我将变量的转换为 avoid*而不是变量的地址)。或者你可以,正如我看到Zac也建议的那样,
  2. 创建变量的副本并存储副本地址。root->data = new int(num);. 在这种情况下,您必须确保delete销毁树时的内存
  3. 使用其他人建议的模板(这是更好的方法) - 我将保留这一点,因为其他人已经介绍过它。

你们中的另一位问题是您的评论在哪里

//((int *)(root->data)) = num;//i tried to convert to void* in this way but it give me segmentation fault

失败的原因是因为root->data此时只是一个指针......它还没有指向任何地方(有意义)。因此,当您尝试取消引用它时,您正在尝试访问一些确实“存在”的内存(指针为 NULL 或具有无效地址),因此您出现了段错误。

以这种方式使用指针时,您需要创建一些内存,然后使指针指向该内存,例如root->data = new int;. 完成此操作后,您可以为内存中的该位置分配一个值,例如,*(root->data) = 1234;

于 2013-11-06T17:35:29.290 回答
1

首先,很少有理由将某些东西存储为 avoid*而不是使用强类型方法(例如模板)。当您将代码更改为

template<typename T>
TreeNode
{
    TreeNode<T>* left;
    TreeNode<T>* right;
    T data;
};

也就是说,您遇到的问题是您正在存储一个副本的地址,一旦函数超出范围,该地址就会消失:

if(root==NULL)
{
    root= new TreeNode();
    root->data=&num; // PROBLEM!!!
    root->left=NULL;
    root->right=NULL;
}

问题线应该是:

root->data = new int(num);

当你完成它时,你必须正确地删除内存(例如当你的树被破坏时)。

或者,如果你碰巧在一个系统上sizeof(void*) == sizeof(int),你可以这样做

root->data = (void*)num;

它将简单地将void*成员视为整数。这不适用于int大于的系统void*

于 2013-11-06T17:36:54.267 回答