1

问题:假设您正在尝试用 C 语言编写一个函数,该函数将使用文件中的值填充二维数组。该文件包含按行(记录)排列的值,其中每行包含多个字段。该函数应接收指向二维数组的指针和文件地址并填充数组。重要的是,该函数应该独立于每条记录有多少字段而工作。例如,在一个程序中,您可能会调用该函数从每个记录有四个字段的文件中读取值:

int array_of_values[MAX_NUMBER_OF_RECORDS][4];
fill_in_array(array_of_values, "spacetime.csv");

在另一个程序中,您可能希望在每条记录有 11 个字段时填写值:

int array_of_values[MAX_NUMBER_OF_RECORDS][11];
fill_in_array(array_of_values, "M-theory.csv");

不幸的是,如果你尝试这样做,你就会违反 C 处理多维数组的方式。多维数组在 C 中没有实现为指向数组的指针数组,而是作为一个长的一维数组。这意味着函数需要知道数组的宽度才能从中读取数据。

所以下面的函数定义会给你一个错误:

void fill_in_array(int array_of_values[MAX_NUMBER_OF_RECORDS][], char *path)

[请注意,以下内容很好:

void fill_in_array(int array_of_values[][MAX_NUMBER_OF_RECORDS], char *path)

因为编译器不需要知道第一维的索引,但假设这是不允许的(例如,如果函数需要处理单个记录,例如array_of_values[1])。]

这是我在我的程序中达到的点。有两种解决方案:

  1. 强制函数使用固定数量的字段。我显然不想这样做,但我可以,例如,声明一个常量MAX_NUMBER_OF_FIELDS并将未使用的字段留空。
  2. 使fill_in_array函数接受指针而不是数组,并动态分配包含字段的 Iliffe 向量。这是一个很有吸引力的想法(因为它会阻止我们必须声明最大数量的记录/字段,但这也意味着我们必须创建(并记住使用!)一个函数来释放字段数组。

我还有另一个想法。也就是将函数的声明修改为如下:

void fill_in_array(int **array_of_values, int number_of_fields, char *path)

(这里,number_of_fields指的是每条记录的字段数,所以我们可以称它为fill_in_array(array_of_values, 4, "spacetime.csv");.

请注意,参数array_of_values不再是显式数组,而是指针。通常,如果你分配一个双指针指向一个二维数组,结果是没有意义的。我的想法是可以使用number_of_fields参数,以便函数知道如何处理表达式,如array_of_values[i][j].

原则上这应该是相当容易的:事实上,如果a是一个二维数组,那么a[i][j]定义为

*(a + (i * n) + j)

其中n是数组的长度,所以我们可以替换每次出现的array_of_values[i][j]with*(array_of_values + (i * number_of_fields) + j)和每次出现的array_of_values[i]with array_of_values + (i * number_of_fields)。但是,此代码将很难阅读。有没有办法告诉编译器数组的宽度是number_of_fields这样我可以使用索引符号来访问数组的元素?

4

4 回答 4

3

不,没有这种方法。

一旦您需要进行通用地址计算,您就需要自己实现它。

恭喜您获得了添加描述每条记录的字段数的显式参数的解决方案,这当然应该这样做。

您可以在函数内部使用宏来使地址计算更易于管理,也许。

于 2013-03-25T15:39:04.963 回答
1

有几个解决方案。

使用结构:

typedef struct {
  // whatever appears in a record
} record_t

void fill_in_array(record_t records[MAX_NUMBER_OF_RECORDS], const char* path);

请注意,这仅在编译时已知记录的大小时才有意义,对于您的示例,它可能不是。

使用大步:

void fill_in_array(int *array_of_values, int stride, const char *path)
{
  #define IDX(x, y) (x + (y * stride))

  // get the val at i,j
  int val = array_of_values[IDX(i,j)];

  #undef IDX
}

您已经在您的函数中建议了这种方法,number_of_fields它是一个跨步,但是跨步是其他开发人员查看您的代码更有可能识别的术语。

一点无关紧要,如果你不改变的内容path,你应该做到const:)

于 2013-03-25T15:43:10.237 回答
0

除非您仅限于 C89(即 MSVC 编译器),否则您可以像这样传递多维数组:

#include <stdio.h>

void fill_in_array(size_t m, size_t n, int array_of_values[m][n])
{
  for (size_t i = 0; i < m; ++i) {
    for (size_t j = 0; j < n; ++j) {
      array_of_values[i][j] = ((i == j) ? 1 : 0);
    }
  }
}

void print_array(size_t m, size_t n, int array_of_values[m][n])
{
  for (size_t i = 0; i < m; ++i) {
    for (size_t j = 0; j < n; ++j) {
      printf(" %d", array_of_values[i][j]);
    }
    printf("\n");
  }
}

int main()
{
  {
    int array_of_values[2][4];
    fill_in_array(2, 4, array_of_values);
    print_array(2, 4, array_of_values);
  }
  {
    size_t h = 6, w = 5;
    int array_of_values[h][w];
    fill_in_array(h, w, array_of_values);
    print_array(h, w, array_of_values);
  }
}
于 2013-03-25T17:12:26.987 回答
0

我相信您正在寻找的内容存在于 C++ 中,但不存在于 C 中。在 C++ 中,您可以定义模板函数来处理大小在编译时已知的数组,然后编译器负责其余的工作。在 C 中,有两种方法:

  • 显式定义大小
    这是类似函数的情况memcpy,您可以在其中指定元素的数量

    void process_array(int *data[], size_t max_x, size_t max_y)
    ....
    
  • 使用无效数字定义大小
    这是函数的情况,例如strlen数据以某个值终止('\0'此处)
    因此,如果您想要一个带有矩阵但元素数量可变的函数,您必须定义一种方法如何在数据。

    #define ARRAY_TERM -1
    
    void process_array(int *data[])
    {
        size_t i, j;
        for (i = 0; data[i]; i++)
        {
            for (j = 0; data[i][j] != ARRAY_TERM; j++)
            {
               ...
            }
        }
    }
    ...
    

希望你有这个想法。使用起来不是很方便。

还有另一种方法:定义自己的类型。是的,在许多情况下这是一个可行的选择:

typedef struct array *array_t;
struct array
{
    size_t max_x, max_y;
    int *data;
};

使用它的基本功能集:

int array_init(array_t *a; size_t max_x, size_t max_y)
{
    array_t res;
    res = malloc(sizeof(*res));
    res->max_x = max_x;
    res->max_y = max_y;
    res->data = calloc(max_x * max_y, sizeof(int));
    *a = res;
    return 0;
}

void array_destroy(array_t *a)
{
    free((*a)->data);
    free(*a);
}

然后您可以定义您的附加功能进行操作。

于 2013-03-25T16:06:34.223 回答