![](/img/trans.png)
[英]Pass pointer of an array of multi-dimensional arrays to a function, specifying bounds
[英]Can you pass multi-dimensional arrays into a C function as pointers and then cast them back into arrays inside the function?
問題:假設您正在嘗試在C中編寫一個函數,該函數將使用文件中的值填充2D數組。 該文件包含按行(記錄)排列的值,其中每行包含許多字段。 該函數應該接收指向2D數組的指針和文件的地址並填充數組。 重要的是,該功能應獨立於每條記錄有多少字段。 例如,在一個程序中,您可以調用該函數從文件讀取值,其中每個記錄有四個字段:
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]
這樣的單個記錄)。
這是我在程序中達到的要點。 有兩種解決方案可供選擇:
MAX_NUMBER_OF_FIELDS
並將未使用的字段留空。 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
不再是顯式數組,而是指針。 通常,如果指定雙指針指向2D數組,則結果毫無意義。 我的想法是可以使用number_of_fields
參數,以便函數知道如何處理像array_of_values[i][j]
這樣的表達式。
原則上這應該是相當容易的:實際上,如果a
是2D數組,那么a[i][j]
被定義為
*(a + (i * n) + j)
其中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)
。 但是,這段代碼很難閱讀。 有沒有辦法告訴編譯器數組的寬度是number_of_fields
以便我可以使用索引表示法來訪問數組的元素?
不,沒有這樣的方式。
一旦需要進行通用地址計算,就需要自己實現。
祝賀您獲得添加顯式參數的解決方案,該參數描述了每條記錄的字段數,這當然是應該如何完成的。
您可以使用函數內部的宏來使地址計算更容易管理。
有一些解決方案。
使用結構:
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
:)
我相信你在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);
}
然后,您可以定義附加功能以進行操作。
除非您僅限於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.