【问题标题】:How to access recursive structure with pointers如何使用指针访问递归结构
【发布时间】:2016-03-13 18:14:12
【问题描述】:

我在我的 C 程序中遇到了一个非常奇怪的错误,因此我需要你们的帮助!所以我有一个称为路径的递归结构,有时我将“父”路径的地址存储在结构字段母亲中:

 typedef struct path{

  struct path* mother;
  struct path** children;
  int length;
  uint8_t* inf;
 } path;

所以在我的示例中,我只生成一条这样的路径:

  int child_num=2;
  int bytes=10;
  path* my_path=malloc(sizeof(path));
  if (path==NULL) throw error...

  my_path->inf=malloc(sizeof(uint8_t)*bytes);
  memset(my_path->inf, 4, bytes);

  my_path->children=malloc(sizeof(path*)*child_num);

  for(int i=0; i<child_num; i++){
      my_path->children[i]->mother=my_path;
      my_path->children[i]->inf=malloc(sizeof(uint8_t)*bytes);
      memset(my_path->children[i]->inf, 5, bytes);
  }

所以现在因为我存储了父结构的链接,我想使用另一个帮助指针来访问它的信息:

  path* my_pointer=my_path->children[0]->mother;  //this is just for the example

所以我检查了地址,一切似乎都很好,但如果我知道在另一个方法中使用指针,指向字段“inf”,那么如果我使用变量“path”,它就可以工作:

     method(path->inf, bytes);

没关系,但只要我这样做:

    method(my_pointer->inf, bytes);

方法在标记的行崩溃:

 void method(uint8_t* element, int bytes) {

     if (element==NULL) ... //<=== here it crashes
     //do something

}

我真的不明白我做错了什么,我打印了地址,一切似乎都很好,即使我通过变量“my_pointer”访问某个字节,就像

      my_pointer->inf[1]

它会返回相应的值,但在单独的方法中它不起作用。

【问题讨论】:

  • 你是如何确定它在if (element == NULL) 崩溃的?我怀疑它并没有真正崩溃,因为那里没有什么会导致错误的内存引用。问题可能发生在此之前,这是您未显示的代码。可能是对method 的调用之一。您对method 的第一个参数是path-&gt;infmy_pointer-&gt;infmy_path-&gt;...。所以检查pathmy_pointermy_path是否有效。
  • 如果pathmy_pointer 确实指向同一个内存位置,那么method(path-&gt;inf, bytes)method(my_pointer-&gt;inf, bytes) 应该产生完全相同的结果。所以我的猜测是你把问题简化得太多了,不小心漏掉了一些重要的细节。你能写一个简短、完整、最小的程序来一致地重现问题吗?
  • sizeof(uint8_t) 是多余的,因为它始终是1。 (如果平台上完全存在uint8_t,它的大小将与unsigned char 相同,并且sizeof(unsigned char) 始终是1。)
  • 是的,确实我试图用尽可能少的行来简化我遇到的问题,我会尝试让它更复杂地重现它,但现在我不知道如何。我确信它会在该行崩溃,因为每个崩溃原因我都有其他“错误消息”

标签: c pointers recursion


【解决方案1】:

就像 cmets 表明我们无法用所提供的信息准确回答您的问题,但我们可以为您指明正确的方向。

首先,我注意到在您的示例中,您使用path 作为类型定义的path 结构的变量名。您需要更详细地使用变量名称或实际复制粘贴一些代码以确保我们可以查看实际问题,因为这可能只是命名问题。

总而言之,我认为使用一点代码卫生对您有好处。在文件范围内组织一些用于数据结构开销的函数:

static int path_alloc(path* p);
static int path_alloc_kids(path* p, int num);

static int path_alloc(path* p) {
  if(p == NULL) { return -1; }

  p = (path*)malloc(sizeof(path));
  if(p == NULL) { return -2; }

  return 0;
}

static int path_alloc_kids(path* p, int num) {
  if(p == NULL || num <= 0) { return -1; }

  if(!path_alloc(p)) { /* Easier to read and understand, no error handling here to muddle things up */

    /* You don't actually need a path**, do you? Think of char *argv[] a.k.a. char **argv, is that what you're actually going for? */
    p->children = (path*)malloc(sizeof(path) * num);
    if(p->children == NULL) { return -2; }
    p->length = num;
  } else { return -1; } /* Simple */

  return 0;
}

很多更容易理解您的代码,这是指针的主要问题。添加一些方法来释放分配的子节点和根节点,您就可以以相对抽象的方式使用此路径结构。您可能需要考虑以链表方式使用pathpath_node,这样您只分配您需要的。

struct spath_node; /* So it knows of itself */
typedef struct spath_node {
  struct spath_node *parent;
  struct spath_node *next;
  uint8_t *data;
  int data_size;
} path_node;

然后通过传入数据大小和父节点进行分配,NULL 父节点可能意味着它是根节点。

static int path_alloc_node(path_node *parent, int data_size, uint8_t *data);

这使得插入/遍历相对较慢,但更容易理解您出错的地方。

编辑:明确地说,这是我们将子级添加到链表示例的方式:

static int path_alloc_node(path_node *parent, int data_size, uint8_t *data) {
  path_node *tmp;

  if(parent == NULL || data_size <= 0) { return -1; }
  if(parent->next != NULL) { return -3; }

  tmp = (path_node*)malloc(sizeof(path_node));
  if(tmp == NULL) { return -2; }
  else parent->next = tmp;

  if(data == NULL) { /* Assume the caller is requesting a new data block of the given size */
    data = (uint8_t*)malloc((size_t)data_size);
    if(data == NULL) { return -2; }
  }

  parent->next->data = data;
  parent->next->data_size = data_size;
  parent->next->next = NULL;
  parent->next->parent = parent;

  return 0;
}

【讨论】:

  • @Ian Abbott:非常正确,这可能会有所不同,因为 sizeof() 不再在编译时完成。
  • 非常感谢您清理代码。但是我对您的代码有疑问,在这里我并没有真正得到这个链表的东西,所以您将 struct spath_node 声明为 path_node?然后可能的“孩子”将存储在指针 *next 中?但这怎么还有递归性质呢?我看不出我怎么能从“下一个”分配更多的孩子
  • 我刚刚注意到的另一个问题:我认为不再需要将指针转换为类型?
  • 我说的是链表,因为它是线性的并且更容易理解。如果您对此感到满意,那么代码中的多树结构会更有意义。 See here 专门用于链接列表。至于将指针转换为类型,这是习惯。有点像检查NULL。大声笑
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-16
  • 1970-01-01
  • 2023-02-16
  • 2012-01-26
相关资源
最近更新 更多