簡體   English   中英

C - 從 function 返回指針后,未設置值是結構指針

[英]C - values is struct pointer are not set after pointer is returned from function

我正在嘗試將我在 Java 中編寫的 kd 樹上的 knn(k 最近鄰搜索)移植到 C。

Java output,如預期:

Nearest to Key: 6.0,5.0,4.0
Key:6.0,5.0,4.0,min distance:0.0
Key:5.0,4.0,3.0,min distance:3.0
Key:7.0,6.0,5.0,min distance:3.0
Key:4.0,3.0,2.0,min distance:12.0
Key:3.0,2.0,1.0,min distance:27.0

Java 代碼,class(這是一個快速實現,只是為了在我開始我的端口之前讓算法工作):

class kd_tree {
  public int DEFAULT_NUMBER_OF_DIMENSIONS = 1;
  static int current_number_of_data_points = 0;
  static int current_global_median = 0;

  kd_tree left;
  kd_tree right;
  float[] data;
  private int k_dimensions;
  float distance_to_neighbor;
}

Java knn方法:

/*===========================================================================
Function        knn, knn algorithm  using kd-tree & minimumNeighbor function.
Description:    
==========================================================*/
public static kd_tree[] knn( kd_tree  root
                           , float[] data_point
                           , int     depth
                           , int     k_dimension
                           , int     number_of_nearest_neighbors) {

  kd_tree[] all_nearests = new kd_tree[current_number_of_data_points];
  kd_tree[] n_nearests = new kd_tree[current_number_of_data_points];

  if (root != null) {
    int nearests_counter = 0;

    /* now lets traverse the tree inorder & calculate distances  from the 
    query point based on Morris Traversal algorithm that does not 
    use any stacks or no recursion */
    kd_tree cur = root;
    kd_tree pre;
    while (cur != null) {
      if (cur.left == null) {
        /* debugging System.out.println(cur.data[0]);
        calculate distance */

        cur.distance_to_neighbor = n_dimensional_euclidean(data_point, cur.data);
        all_nearests[nearests_counter] = cur;
        nearests_counter++;

        cur = cur.right; // move to next right node
      } else { // has a left subtree
        pre = cur.left;
        while (pre.right != null && pre.right != cur) { // find rightmost
          pre = pre.right;
        }

        /* Make current as the right child of its inorder  
        predecessor */
        if (pre.right == null) {
            pre.right = cur;
            cur = cur.left;
        } else {
            pre.right = null;
            // debugging printf("%d ", current->data);
            // calculate distance
            cur.distance_to_neighbor = n_dimensional_euclidean( data_point, cur.data);
            all_nearests[nearests_counter] = cur;
            nearests_counter++;

            cur = cur.right;
        }
      }
    }//end while

    // base cases
    // sort from least to greatest
    insertion_sort_based_on_distance(all_nearests);

    // return on specified number_of_nearest_neighbors
    for (int i = 0; i < number_of_nearest_neighbors; i++) {
      n_nearests[i]=all_nearests[i];
    }
  }

  return n_nearests;
}

相關的 C 代碼片段:

#include <stdlib.h>
#ifndef KDTREE_H
#define KDTREE_H

/*
 * Representation of a kd tree
 */
typedef struct tree_ {
    struct tree*  left;
    struct tree*  right;
    float*        info;
    float         distance_to_neighbor;
} tree;

// pre-allocated tree nodes array
static tree tree_space[KD_TREE_HEAP_SIZE];

// the knn algorithm will require memory space upto tree size 
static tree* processing_space [KD_TREE_HEAP_SIZE];

tree* knn( tree*      root
         , float      data_point[]
         , int        depth
         , const int  k_dimensions
         , int        number_of_nearest_neighbors
         , int        total_number_of_elements
         , tree*      n_nearests);

相關knn實現,kdtree.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include "kdtree.h"
#include "sorting_utility.h"

static int current_number_of_kd_tree_nodes  = 0;
static int current_number_of_data_points    = 0;

/*===========================================================================
Function        knn, knn algorithm  using kd-tree.
Description:    
==========================================================*/
tree* knn( tree*      root
         , float      data_point[]
         , int        depth
         , const int  k_dimensions
         , int        number_of_nearest_neighbors
         , tree*      n_nearests)
{
  tree  all_nearests[current_number_of_kd_tree_nodes];
  tree* source_data_end_ptr         = NULL;
  tree* source_data_start_ptr       = NULL;
  tree* destination_data_start_ptr  = NULL;
  tree* destination_data_end_ptr    = NULL;

  if(NULL != root && NULL != n_nearests)
  {
    int nearests_counter = 0;

    // now lets traverse the tree inorder & calculate distances
    // from the query point
    tree* cur = root;

    tree* pre;
    while(NULL != cur)
    {
      if(NULL == cur->left)
      {
        cur->distance_to_neighbor = n_dimensional_euclidean(data_point, cur->info);
        processing_space[nearests_counter] = *cur;
        nearests_counter++;
        cur = cur->right; // move to next right node
      }
      else
      {
        pre = cur->left;
        while(pre->right != NULL && pre->right != cur)
        { // find rightmost
          pre = pre->right;
        }
        /* Make current as the right child of its inorder predecessor */
        if(pre->right == NULL)
        {
          pre->right = cur;
          cur = cur->left;
        }
        else
        {
          pre->right = NULL;
          // calculate distance
          cur->distance_to_neighbor = n_dimensional_euclidean(data_point, cur->info);
          processing_space[nearests_counter] = *cur;

          nearests_counter++;
          cur = cur->right;
        }
      }
    } // end while

    // JUST FOR DEBUGGING START
    printf ("***For debugging before sort:\n");

    for(int i = 0; i < current_number_of_kd_tree_nodes; i++)
    {
      printf("{");
      for(int c = 0; c < k_dimensions; c++)
      {
        if(NULL != processing_space[i].info)
        {
          printf("%f,", processing_space[i].info[c]);
        }
        else
        {
          break;
        }
      } // end for
      printf("} ");
      printf("min_distance=%f\n", processing_space[i].distance_to_neighbor);
    } // end for
    // JUST FOR DEBUGGING END

    /* copy relevant range up current_number_of_kd_tree_nodes before sort,
     * in  order to avoid sorting beyond range that does not have any data */

    source_data_start_ptr       = &processing_space[0];
    destination_data_start_ptr  = &all_nearests[0];
    destination_data_end_ptr    = &all_nearests[current_number_of_kd_tree_nodes - 1];

    while(destination_data_start_ptr <= destination_data_end_ptr)
    {
      *destination_data_start_ptr = *source_data_start_ptr;
      source_data_start_ptr++;
      destination_data_start_ptr++;
    }

    // sort based on distance from query point
    quick_sort(all_nearests, 0, current_number_of_kd_tree_nodes - 1);

    // JUST FOR DEBUGGING START
    printf("***For debugging after sort\n");

    for (int i = 0; i < current_number_of_kd_tree_nodes; i++)
    {
      printf("{");
      for (int c = 0; c < k_dimensions; c++)
      {
        if (NULL != all_nearests[i].info)
        {
          printf ("%f,", all_nearests[i].info[c]);
        }
        else
        {
          break;
        }
      } // end for
      printf("} ");
      printf("min_distance=%f\n", all_nearests[i].distance_to_neighbor);
    } // end for

    // JUST FOR DEBUGGING END

    /* copy only the n_nearest & ignore/ do NOT copy any empty tree nodes */
    // reuse pointers
    destination_data_end_ptr  = &n_nearests[number_of_nearest_neighbors - 1];
    source_data_end_ptr       = &all_nearests[current_number_of_kd_tree_nodes - 1];
    source_data_start_ptr     = all_nearests;

    int counter = 0;
    while(counter < number_of_nearest_neighbors && source_data_start_ptr < source_data_end_ptr)
    {
      // do NOT copy any empty tree nodes
      if(source_data_start_ptr != NULL && source_data_start_ptr->info != NULL)
      {
          /* ATTENTION: i checked with debugger & values for 
          (distance_to_neighbor,info,left,right were not zeroed or empty */
          float distance_to_neighbor  = source_data_start_ptr->distance_to_neighbor;
          float* info                 = source_data_start_ptr->info;
          tree* left                  = source_data_start_ptr->left;
          tree* right                 = source_data_start_ptr->right;

          n_nearests[counter].distance_to_neighbor  = distance_to_neighbor;
          n_nearests[counter].info                  = info;
          n_nearests[counter].left                  = left;
          n_nearests[counter].right                 = right;

          counter++;
      }
      source_data_start_ptr++;
    }
  }
  else
  {
    printf("Error, knn input parameter error");
  }

  return n_nearests;
} // end function

main.c 的相關片段:

#include<kdtree.h>

int main()
{
  const int query_size    = 10;
  const int k_dimensions  = 3;

  printf("knn (k nearest neighboor)\n:");
  tree* n_nearests[query_size];
  printf("%Nearest to Key: {%f,%f,%f}\n", query_size,query_point[0], query_point[1], query_point[2]); 
  knn(root, query_point, 0, k_dimensions, query_size, KD_TREE_HEAP_SIZE, n_nearests);

  // print n nearest neighbors
  tree* tree_ptr = &n_nearests[0];

  for(int i = 0; i <query_size; i++)
  {
    if(NULL != tree_ptr->info)
    {
      printf("Key={");
      for(int c=0; c < k_dimensions; c++)
      {
        printf("%d,", tree_ptr->info[c]);
      }
      printf("} ");
      printf("%f min distance\n", tree_ptr->distance_to_neighbor);
    }
  }

  return 0;
} // end main

Output 由 C 版本:

knn (k nearest  neighboor)
:5 Nearest neighbors to Key: {6.000000,5.000000,4.000000} are: 
***For debugging before sort:
{-1.000000,-2.000000,-3.000000,} min_distance=49.000000
{0.000000,-1.000000,-2.000000,} min_distance=36.000000
{1.000000,0.000000,-1.000000,} min_distance=25.000000
{2.000000,1.000000,0.000000,} min_distance=16.000000
{3.000000,2.000000,1.000000,} min_distance=9.000000
{4.000000,3.000000,2.000000,} min_distance=4.000000
{5.000000,4.000000,3.000000,} min_distance=1.000000
{6.000000,5.000000,4.000000,} min_distance=0.000000
{7.000000,6.000000,5.000000,} min_distance=1.000000
{14.000000,13.000000,12.000000,} min_distance=64.000000
{15.000000,14.000000,13.000000,} min_distance=81.000000
{16.000000,15.000000,14.000000,} min_distance=100.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
***For debugging after sort
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{6.000000,5.000000,4.000000,} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{} min_distance=0.000000
{5.000000,4.000000,3.000000,} min_distance=1.000000
{7.000000,6.000000,5.000000,} min_distance=1.000000
{4.000000,3.000000,2.000000,} min_distance=4.000000
{3.000000,2.000000,1.000000,} min_distance=9.000000
{2.000000,1.000000,0.000000,} min_distance=16.000000
{1.000000,0.000000,-1.000000,} min_distance=25.000000
{0.000000,-1.000000,-2.000000,} min_distance=36.000000
{-1.000000,-2.000000,-3.000000,} min_distance=49.000000
{14.000000,13.000000,12.000000,} min_distance=64.000000
{15.000000,14.000000,13.000000,} min_distance=81.000000
{16.000000,15.000000,14.000000,} min_distance=100.000000

**After function return:**
Key={0,0,0,} 0.000000 min distance
Key={0,0,0,} 0.000000 min distance
Key={0,0,0,} 0.000000 min distance
Key={0,0,0,} 0.000000 min distance
Key={0,0,0,} 0.000000 min distance

我已經在 Java 和 C 版本中測試了插入、中序遍歷、搜索、刪除和找到最小鄰居,它們按預期工作。

在 C 版本中,knn function 在 function 返回后返回零值?

為什么在主 function 中調用后結構中的值為零(請參閱標題為“function 返回后”的 output 區域?

希望別人能發現一些明顯的東西,真的很絕望地拔掉我的頭發。

有多個問題:

  • n_nearestmain中定義為指向tree對象的指針數組。 但是您將knn的參數定義為指向實際tree對象數組的指針,這是不兼容的。 該參數應定義為tree **n_nearesttree* n_nearest[] ,它是指向指針數組的指針。 然后,您應該只存儲對樹元素的引用,而不是復制值。 同樣, all_nearests應該定義為指針數組,就像在 java 中一樣,而不是存儲樹節點副本的對象數組。
  • 編譯器應該針對這種類型不匹配發出診斷。 這不是良性錯誤,代碼確實具有未定義的行為,這可能解釋了不正確的 output。 始終為 C 編譯器啟用額外警告,以檢測可能不正確的代碼: gcc -Wall -Werrorclang -Weverything -Werror是節省無數時間的好開始。
  • java 類型kd_tree可以在 C 中定義為typedef tree *kd_tree; 但是 C 程序員發現將指針隱藏在 typedef 后面會令人困惑,其中 java 程序員可以毫無問題地操縱除標量值之外的所有內容的指針,因為 ZA3CBC3F9D0CE2F2C1554E1B671D71D71D9CZ 不包含指向實際對象的指針。
  • 如果使用全局processing_space ,則可以避免動態分配臨時數組all_nearests ,該數組從 java 的堆和 C 的堆棧中分配。 請注意,在這種情況下,您不需要傳遞total_number_of_elements ,因為processing_space被假定大到足以容納對所有樹節點的引用。
  • 您不會返回存儲到目標數組中的元素數量。 可以少於請求的數量。 java 中也存在此問題,您將n_nearestall_nearests分配為大小為current_number_of_data_pointskd_tree對象的 arrays ,其中許多將是null
  • 實際上,您必須在您未發布的排序 function 中測試這些 null 指針,如果您傳遞正確數量的元素進行排序,則這是不必要的。
  • main中的循環不會增加tree_ptr ,因此它總是打印n_nearests的第一個元素。
  • 在 C 版本中使用指針的可讀性低於使用數組表示法,並且不能確保更好的性能。 C 編譯器在刪除由數組偏移計算產生的常見子表達式方面非常有效。 C 代碼將更接近 java 代碼。

這是使用指向結構的指針 arrays 的修改版本,更接近 java 版本:

#ifndef KDTREE_H
#define KDTREE_H
/*
 * Representation of a kd tree
 */
typedef struct tree {
    struct tree*  left;
    struct tree*  right;
    float*        info;
    float         distance_to_neighbor;
} tree;

// pre-allocated tree nodes array
extern tree tree_space[KD_TREE_HEAP_SIZE];

// the knn algorithm will require memory space upto tree size 
extern tree* processing_space[KD_TREE_HEAP_SIZE];

// return the number of closest neighbors, <= number_of_nearest_neighbors
int knn(tree*      root,
        float      data_point[],
        int        depth,
        const int  k_dimensions,
        int        number_of_nearest_neighbors,
        int        total_number_of_elements,
        tree*      n_nearests);

knn的實現:

#include <stdio.h>
#include <stdlib.h>
#include "kdtree.h"

static int current_number_of_kd_tree_nodes  = 0;
static int current_number_of_data_points    = 0;

static int tree_compare_distance(const void *a, const void *b) {
    tree* ap = *(tree* const *)a;
    tree* bp = *(tree* const *)b;
    return ((ap->distance_to_neighbor > bp->distance_to_neighbor) -
            (ap->distance_to_neighbor < bp->distance_to_neighbor));
}

static void quick_sort_based_on_distance(tree** array, size_t size) {
    qsort(array, size, sizeof(*array), tree_compare_distance);
}

/*===========================================================================
   Function        knn, knn algorithm  using kd-tree.
   Description:
   ==========================================================*/
int knn(tree*      root,
        float      data_point[],
        int        depth,
        const int  k_dimensions,
        int        number_of_nearest_neighbors,
        tree**     n_nearests)
{
    /* use the static processing space to avoid defining a potentially huge
       array on the stack */
    tree** all_nearests = processing_space;

    if (root != NULL && n_nearests != NULL) {
        int nearests_counter = 0;

        // now lets traverse the tree inorder & calculate distances
        // from the query point
        tree* cur = root;
        tree* pre;
        while (cur != NULL) {
            if (cur->left == NULL) {
                // calculate distance
                cur->distance_to_neighbor = n_dimensional_euclidean(data_point, cur->info);
                all_nearests[nearests_counter] = cur;
                nearests_counter++;
                cur = cur->right; // move to next right node
            } else { // has a left subtree
                pre = cur->left;
                while (pre->right != NULL && pre->right != cur) { // find rightmost
                    pre = pre->right;
                }
                /* Make current as the right child of its inorder predecessor */
                if (pre->right == NULL) {
                    pre->right = cur;
                    cur = cur->left;
                } else {
                    pre->right = NULL;
                    // calculate distance
                    cur->distance_to_neighbor = n_dimensional_euclidean(data_point, cur->info);
                    all_nearests[nearests_counter] = cur;
                    nearests_counter++;
                    cur = cur->right;
                }
            }
        }

        // JUST FOR DEBUGGING START
        printf ("***For debugging before sort:\n");
        for (int i = 0; i < nearests_counter; i++) {
            printf("{");
            for (int c = 0; c < k_dimensions; c++) {
                printf("%f,", all_nearests[i]->info[c]);
            }
            printf("} ");
            printf("min_distance=%f\n", all_nearests[i]->distance_to_neighbor);
        }
        // JUST FOR DEBUGGING END

        // sort based on distance from query point
        quick_sort_based_on_distance(all_nearests, nearests_counter);

        // JUST FOR DEBUGGING START
        printf("***For debugging after sort\n");
        for (int i = 0; i < nearests_counter; i++) {
            printf("{");
            for (int c = 0; c < k_dimensions; c++) {
                printf("%f,", all_nearests[i]->info[c]);
            }
            printf("} ");
            printf("min_distance=%f\n", all_nearests[i]->distance_to_neighbor);
        }
        // JUST FOR DEBUGGING END

        // return only specified number_of_nearest_neighbors
        // yet do not return elements beyond nearest_counter
        if (number_of_nearest_neighbors > nearest_counter)
            number_of_nearest_neighbors = nearest_counter;
        for (int i = 0; i < number_of_nearest_neighbors; i++) {
            n_nearests[i] = all_nearests[i];
        }
        return number_of_nearest_neighbors;
    } else {
        printf("Error, knn input parameter error");
        return -1;
    }
}

The main code is modified too:

```c
#include <stdio.h>
#include "kdtree.h"

int main() {
    const int query_size    = 10;
    const int k_dimensions  = 3;

    // ...
    // get the values and the query point
    // ...

    printf("knn (k nearest neighboor)\n:");
    tree* n_nearests[query_size];
    printf("%d nearest neighbors to Key: {%f,%f,%f}\n", query_size,
           query_point[0], query_point[1], query_point[2]);
    int result_size = knn(root, query_point, 0, k_dimensions, query_size, n_nearests);

    // print the computed nearest neighbors
    for (int i = 0; i < result_size; i++) {
        tree* tree_ptr = n_nearests[i];
        if (tree_ptr->info != NULL) {
            printf("Key={");
            for (int c = 0; c < k_dimensions; c++) {
                printf("%s%d", "," + !c, tree_ptr->info[c]);
            }
            printf("} ");
            printf("%f min distance\n", tree_ptr->distance_to_neighbor);
        }
    }
    return 0;
}

為什么在主 function 中調用后結構中的值為零(請參閱標題為“function 返回后”的 output 區域?

您的代碼中有一些省略,但可能是因為current_number_of_kd_tree_nodes設置為 0?


如果您只需要“K-最近鄰”實現,請查看現有問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM