【问题标题】:Casting int to void pointer将 int 转换为 void 指针
【发布时间】:2013-11-18 01:59:01
【问题描述】:

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

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

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

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 来做空指针?

【问题讨论】:

  • 您没有将指向 void 的指针转换为 int。您正在将其转换为指向 int 的 指针
  • ((int *)(root-&gt;data)) = num 无法编译。必须是((int *)(root-&gt;data)) = &amp;num*((int *)(root-&gt;data)) = num。是哪一个?
  • 感谢您的回答。他们非常有帮助。我使用了“new int(num)”-它有效。关于模板 - 肯定会更有效,但我现在是一名学生,我的作业清楚地表明我们需要使用 void ,正如我所说的那样,我不是很合理)

标签: c++ casting void-pointers


【解决方案1】:

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

时,您的整个问题很快就会消失
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* 的系统。

【讨论】:

    【解决方案2】:

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

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

    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;
      ..
    }
    

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

    如果你真的想在void* 中存储int,那么你应该使用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-&gt;data=&amp;num 是错误的,因为您将自动分配变量的地址分配给 data,该变量在退出其范围时将变为无效。

    【讨论】:

      【解决方案3】:

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

      void Tree::addToTree(int num)
      {
      
         if(root==NULL){
             root= new TreeNode();
             root->data=&num;
      

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

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

      1. 如果您只想存储整数或任何宽度小于 sizeof(void *) 的数据类型,您可以使用 root-&gt;data = (void *)num(注意我将变量的 转换为 @ 987654325@ 而不是变量的地址)。或者你也可以,正如我看到的 Zac 也建议的那样,
      2. 创建变量的副本并存储副本地址。 root-&gt;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-&gt;data此时只是一个指针......它还没有指向任何地方(有意义)。因此,当您尝试取消引用它时,您正在尝试访问一些确实“存在”的内存(指针为 NULL 或具有无效地址),因此您出现了段错误。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-12
        • 2014-02-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-22
        • 2017-02-12
        • 2012-03-02
        相关资源
        最近更新 更多