简体   繁体   English

你可以将多维数组作为指针传递给C函数,然后将它们转换回函数内的数组吗?

[英]Can you pass multi-dimensional arrays into a C function as pointers and then cast them back into arrays inside the function?

Problem: suppose you are trying to write a function in C that will fill a 2D array with values from a file. 问题:假设您正在尝试在C中编写一个函数,该函数将使用文件中的值填充2D数组。 The file contains the values arranged in rows (records), where each row contains a number of fields. 该文件包含按行(记录)排列的值,其中每行包含许多字段。 The function should take in a pointer to the 2D array and the address of the file and fill in the array. 该函数应该接收指向2D数组的指针和文件的地址并填充数组。 Importantly, the function should work independently of how many fields there are per record. 重要的是,该功能应独立于每条记录有多少字段。 For example, in one program you might call the function to read values from a file where there are four fields per record: 例如,在一个程序中,您可以调用该函数从文件读取值,其中每个记录有四个字段:

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

In another program, you might want to fill in values when there are eleven fields per record: 在另一个程序中,您可能希望在每个记录有11个字段时填写值:

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

Unfortunately, if you try to do this you fall foul of the way C handles multidimensional arrays. 不幸的是,如果你试图这样做,你会违反C处理多维数组的方式。 Multidimensional arrays are not implemented in C as arrays of pointers to arrays, but instead as one long one-dimensional array. 多维数组在C中不是作为指向数组的指针数组实现的,而是作为一个长的一维数组实现的。 This means that the function needs to know the width of the array in order to read data from it. 这意味着函数需要知道数组的宽度才能从中读取数据。

So the following function definition will give you an error: 所以下面的函数定义会给你一个错误:

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

[Note that the following would be fine: [请注意,以下情况可以:

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

since the compiler does not need to know the index for the first dimension, but suppose that this is not allowed (eg, if the function needs to mess around with individual records like array_of_values[1] ).] 因为编译器不需要知道第一个维度的索引,但是假设这是不允许的(例如,如果函数需要使用像array_of_values[1]这样的单个记录)。

This is the point that I have reached in my program. 这是我在程序中达到的要点。 There are two solutions that present themselves: 有两种解决方案可供选择:

  1. Force the function to work with a fixed number of fields. 强制该函数使用固定数量的字段。 I'd obviously rather not do this, but I could, for example, declare a constant MAX_NUMBER_OF_FIELDS and leave unused fields empty. 我显然不会这样做,但我可以,例如,声明一个常量MAX_NUMBER_OF_FIELDS并将未使用的字段留空。
  2. Make the fill_in_array function take in a pointer rather than an array and dynamically allocate an Iliffe vector containing the fields. 使fill_in_array函数接受指针而不是数组,并动态分配包含字段的Iliffe向量。 This is an attractive idea (since it would stop us having to declare a maximum number of records/fields, but it would also mean we would have to create (and remember to use!) a function to free the array of fields. 这是一个很有吸引力的想法(因为它会阻止我们声明最大数量的记录/字段,但这也意味着我们必须创建(并记住使用!)一个函数来释放字段数组。

I've got one other idea. 我有另外一个想法。 That is to modify the declaration of the function to the following: 那就是将函数的声明修改为以下内容:

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

(Here, number_of_fields refers to the number of fields per record, so we might call it as fill_in_array(array_of_values, 4, "spacetime.csv"); . (这里, number_of_fields指的是每条记录的字段数,因此我们可以将其称为fill_in_array(array_of_values, 4, "spacetime.csv");

Notice that the parameter array_of_values is no longer an explicit array, but is a pointer. 请注意,参数array_of_values不再是显式数组,而是指针。 Normally, if you assign a double-pointer to point to a 2D array, the result is meaningless. 通常,如果指定双指针指向2D数组,则结果毫无意义。 My idea is that it might be possible to use the number_of_fields parameter so that the function knows how to deal with expressions like array_of_values[i][j] . 我的想法是可以使用number_of_fields参数,以便函数知道如何处理像array_of_values[i][j]这样的表达式。

In principle this should be fairly easy: indeed, if a is a 2D array, then a[i][j] is defined to be 原则上这应该是相当容易的:实际上,如果a是2D数组,那么a[i][j]被定义为

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

where n is the length of the array, so we could replace every occurrence of array_of_values[i][j] with *(array_of_values + (i * number_of_fields) + j) , and every occurrence of array_of_values[i] with array_of_values + (i * number_of_fields) . 其中n是数组的长度,所以我们可以用*(array_of_values + (i * number_of_fields) + j)替换每个出现的array_of_values[i][j] ,并且每次出现的array_of_values[i]array_of_values + (i * number_of_fields) This code would be very hard to read, however. 但是,这段代码很难阅读。 Is there some way of telling the compiler that the width of the array is number_of_fields so that I can use index notation to access elements of the array? 有没有办法告诉编译器数组的宽度是number_of_fields以便我可以使用索引表示法来访问数组的元素?

No, there's no such way. 不,没有这样的方式。

Once you need to have generic address computation, you need to implement it yourself. 一旦需要进行通用地址计算,就需要自己实现。

Congratualations for arriving at the solution of adding an explicit parameter that describes the number of fields per record, that's certainly how it should be done. 祝贺您获得添加显式参数的解决方案,该参数描述了每条记录的字段数,这当然是应该如何完成的。

You can use a macro inside the function to make the address calculation more easily managable, perhaps. 您可以使用函数内部的宏来使地址计算更容易管理。

There are a few solutions. 有一些解决方案。

Use a struct: 使用结构:

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

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

Note that this only makes sense if the size of a record is known at compile time, which given your example, it may not be. 请注意,这只有在编译时知道记录的大小才有意义,在给出您的示例时,它可能不是。

Use a stride: 使用步幅:

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
}

You've suggested this approach in your function with number_of_fields which is a stride, however stride is a term which other developers looking at your code are more likely to recognize. 您已经在函数中使用number_of_fields建议了这种方法,这是一个步幅,但是大步是一个术语,其他开发人员在查看您的代码时更容易识别。

One minor unrelated point, if you don't change the contents of path , you should make it const :) 一个小的无关点,如果你不改变path的内容,你应该使它成为const :)

What you are looking exists in C++, but not in C, I believe. 我相信你在C ++中看到的是什么,但在C中却没有。 In C++ you can define template functions for working with arrays of size, known at a compile time, and compiler takes care of the rest. 在C ++中,您可以定义模板函数,以便在编译时使用大小数组,编译器负责其余部分。 In C, there are two approaches: 在C中,有两种方法:

  • Define the size explicitly 明确定义大小
    This is the case of functions like memcpy , where you specify number of elements 这是memcpy等函数的情况,您可以在其中指定元素的数量

     void process_array(int *data[], size_t max_x, size_t max_y) .... 
  • Define the size using invalid number 使用无效数字定义大小
    This is the case of functions like strlen where data is terminated by certain value ( '\\0' here) 这是像strlen这样的函数的情况,其中数据以某个值终止(此处为'\\0'
    So if you want to have a function with matrix, but variable number of elements, you must define a way how to indicate that in the data. 因此,如果您想要一个具有矩阵但可变数量的元素的函数,则必须定义一种如何在数据中指示的方法。

     #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++) { ... } } } ... 

Hope you've got the idea. 希望你有这个想法。 Not very convenient to use. 使用起来不太方便。

There is another approach: define your own type. 还有另一种方法:定义自己的类型。 Yes, it is a viable option in many cases: 是的,在许多情况下,这是一个可行的选择:

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

Basic set of functions to work with it: 使用它的基本功能集:

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

And then you can define your additional functions for operation. 然后,您可以定义附加功能以进行操作。

Unless you are limited to C89 (ie the MSVC compiler), you can pass multi-dimensional arrays around like this: 除非您仅限于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);
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM