[英]How to use dynamic 2d array inside a struct array in C?
我正在做一個關於動態矩陣乘法的項目。 我想從用戶那里輸入他/她想要執行乘法的矩陣數量,並基於此我想創建一個如下所示的結構:
typedef struct
{
size_t rows, columns;
int table[];
} Matrix;
之后,檢查要相乘的矩陣的有效性(使用非常簡單的數學) 。
然后創建一個Matrix
類型的數組,根據用戶要相乘的矩陣個數分配memory給它。
+---------------------------------------------------------------------------------------------------+
| +------------------------------+ +---------------------------------------------------------+ |
| | Matrix struct type array | | ..... More arrays depending on the number of matrices. | |
| +------------------------------+ +---------------------------------------------------------+ |
+---------------------------------------------------------------------------------------------------+
Eg. 2 Matrices [2][2] & [2][1]
+----------------> rows <-------------+
| |
| +------------> columns <-----------|--+
| | | |
| | +------------------------+ | | +------------------+
{ { 2, 2, | { { 1, 2 }, { 2, 1 } } | }, { 2, 1, | { { 1 }, { 3 } } | } }
+------------------------+ +------------------+
| |
| |
| |
v v
| 1 2 | | 1 |
| | | |
| 2 1 | | 3 |
對於這個問題的一些讀者來說,有一件事可能看起來很奇怪,那就是,為什么我想制作一個類型為struct Matrix
的數組,而不是創建 2 個不同類型的對象並在"user_defined_matrix"->table
上執行乘法"user_defined_matrix"->table
。 那么,原因如下:
struct Matrix
的對象。言歸正傳,分配完memory后,我想讓用戶填滿矩陣的每個槽位,然后對它們進行乘法運算,得到結果。
注意:我知道你不能在結構中聲明一個真正的二維 VLA,你必須首先聲明一個一維數組,它后來變成一個二維數組的損壞版本,然后在使用它之前將它強制轉換為一個二維數組,如這個答案所示。
我制作了一個真正的二維數組來存儲用戶輸入的行和列。
int dimensions[NUMBER_OF_MATRIX][2];
然后使用這些dimensions
,我計算了我必須分配多少 memory。
int total_matrix_size = 0;
for (uint i = 0; i < NUMBER_OF_MATRIX; i++)
{
total_matrix_size += (dimensions[i][0] * dimensions[i][1]);
}
然后使用total_matrix_size
將 memory 分配給類型數組struct Martix
。
Matrix *matrix = malloc(((sizeof *matrix) * NUMBER_OF_MATRIX) + sizeof(int[total_matrix_size]));
之后,我要求用戶填充矩陣,在填充矩陣之前,我借助下面代碼中的宏將數組轉換為二維數組。
注意:我將多次引用下面的代碼塊,為了簡單起見,我們將此命名為輸入塊。
#define get_array(arr) \
_Generic((arr), \
Matrix \
: (int(*)[(arr).columns])(arr).table)
for (uint i = 0; i < NUMBER_OF_MATRIX; i++)
{
matrix[i].rows = dimensions[i][0];
matrix[i].columns = dimensions[i][1];
for (uint x = 0; x < matrix[i].rows; x++)
{
for (uint y = 0; y < matrix[i].columns; y++)
{
printf("Enter values of matrix %d a[%dx%d] : ", i + 1, x + 1, y + 1);
scanf("%d", &get_array(matrix[i])[x][y]);
printf("%d\n", get_array(matrix[i])[x][y]); // print statement 1
}
}
}
然后出於測試目的,我正在打印所有矩陣。
注意:我將多次引用下面的代碼塊,為了簡單起見,我們將此塊命名為output 塊。
for (uint i = 0; i < NUMBER_OF_MATRIX; i++)
{
printf("Matrix %d\n", i+1);
for (uint x = 0; x < matrix[i].rows; x++)
{
for (uint y = 0; y < matrix[i].columns; y++)
{
printf("%d ", get_array(matrix[i])[x][y]); // print statement 2
}
printf("\n");
}
}
如您所見,我在上面的 2 個代碼塊input和output塊中編寫了 2 個完全相同的打印語句。
printf("%d ", get_array(matrix[i])[x][y]);
但它們都產生不同的輸出。 Output 由輸入塊生成。
Enter the rows of matrix 1 : 2 2
Enter the rows of matrix 2 : 2 2
Enter values of matrix 1 a[1x1] : 1
1
Enter values of matrix 1 a[1x2] : 0
0
Enter values of matrix 1 a[2x1] : 1
1
Enter values of matrix 1 a[2x2] : 0
0
Enter values of matrix 2 a[1x1] : 2
2
Enter values of matrix 2 a[1x2] : 3
3
Enter values of matrix 2 a[2x1] : 2
2
Enter values of matrix 2 a[2x2] : 3
3
按預期打印所有內容。
但output 塊不一樣。
Matrix 1
2 2
2 3
Matrix 2
2 3
2 3
只不過是對以下內容的深入解釋:
請保持格式有點像這個答案。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// TODO Preprocessors
#define get_array(arr) \
_Generic((arr), \
Matrix \
: (int(*)[(arr).columns])(arr).table)
// TODO Custom types
typedef unsigned int uint;
// TODO Structs
typedef struct
{
uint rows, columns;
int table[];
} Matrix;
// TODO Function Declarations
void flushBuffer(void);
int main(void)
{
int NUMBER_OF_MATRIX = 2;
int dimensions[NUMBER_OF_MATRIX][2];
for (uint i = 0; i < NUMBER_OF_MATRIX; i++)
{
printf("Enter the rows of matrix %d : ", i + 1);
scanf("%d %d", &dimensions[i][0], &dimensions[i][1]);
flushBuffer();
}
if (dimensions[0][1] != dimensions[1][0])
{
printf("Matrix multiplication not possible.");
}
else
{
int total_matrix_size = 0;
for (uint i = 0; i < NUMBER_OF_MATRIX; i++)
{
total_matrix_size += (dimensions[i][0] * dimensions[i][1]);
}
Matrix *matrix = malloc(((sizeof *matrix) * NUMBER_OF_MATRIX) + sizeof(int[total_matrix_size]));
for (uint i = 0; i < NUMBER_OF_MATRIX; i++)
{
matrix[i].rows = dimensions[i][0];
matrix[i].columns = dimensions[i][1];
for (uint x = 0; x < matrix[i].rows; x++)
{
for (uint y = 0; y < matrix[i].columns; y++)
{
printf("Enter values of matrix %d a[%dx%d] : ", i + 1, x + 1, y + 1);
scanf("%d", &get_array(matrix[i])[x][y]);
printf("%d\n", get_array(matrix[i])[x][y]);
}
}
}
for (uint i = 0; i < NUMBER_OF_MATRIX; i++)
{
printf("Matrix divider\n");
for (uint x = 0; x < matrix[i].rows; x++)
{
for (uint y = 0; y < matrix[i].columns; y++)
{
printf("%d ", get_array(matrix[i])[x][y]);
}
printf("\n");
}
}
}
return 0;
}
// TODO Function Definitions
void flushBuffer(void)
{
int c;
while ((c = getchar()) != '\n' && c != EOF)
;
}
我將從開門見山開始。 我將詳細介紹您之后要求的特定主題。
簡而言之,您對問題的理論方法是合理的 IMO。 但是 memory 管理沒有正確實施。
您想要做的是擁有Matrix
結構的多個實例。 這不是您當前設置的結果。 具體來說:
Matrix *matrix = malloc(((sizeof *matrix) * NUMBER_OF_MATRIX) + sizeof(int[total_matrix_size]));
當然,您在這里為NUMBER_OF_MATRIX
Matrix
實例分配了足夠的 memory。 但是你這樣做是為了Matrix
類型。 給定matrix
是pointer to Matrix
矩陣的指針,並且sizeof *matrix
(或sizeof(Matrix)
)等於sizeof(int[2])
(1) ,當您索引該數組(即進行指針算術)時,memory 偏移量增量就是那個尺寸。
假設sizeof(int) = 4
、 NUMBER_OF_MATRIX = 2
和total_matrix_size = 8
(2 個2x2
矩陣)。 如果給matrix
memory 地址0x0010
, &(matrix[1])
將是0x0018
,而不是你期望的0x0028
(24 字節偏移量)。
有關 C 如何處理指針運算的更多參考,您可以參考此 SO 答案和這篇文章,其中對該主題有更詳細的概述。
IMO 實現你想要的最干凈的方法是動態分配matrix[i].table
單獨而不是預先分配你需要的所有 memory 一次。 這是出於以下幾個原因:
(1):我正在考慮給定的結構示例,其中只有int
s。 這意味着不需要填充。 如果有不同類型的成員,由於添加了填充,結構的總大小可能會略大於成員大小的總和。
(2):可以證明單次分配比多次分配更快。 這通常是正確的,因為分配 memory 具有固有的開銷。 同樣,這是您需要針對您的應用程序單獨考慮的事情,而不是將一般性陳述視為事實
我能想到的讓你繼續遵循這個策略的唯一原因是,如果你出於性能原因需要這樣做,並且你想最小malloc
調用(也許你工作的平台對內存分配有嚴重的懲罰)。 鑒於我可以建議修復您當前方法的一件事是將get_array
宏修改為如下內容:
#define get_array(arr, idx) \
_Generic((arr), \
Matrix * \
: (int(*)[(arr)[offset_for_index((arr), (idx))].columns])(arr)[offset_for_index((arr), (idx))].table)
並定義一個offset_for_index
function(或者作為一個宏,如果你願意的話),例如:
static inline size_t offset_for_index(struct x *arr, size_t idx) {
size_t offset = 0;
for (size_t i = 0; i < idx; ++i) {
offset += (sizeof *arr) + sizeof(int[arr[offset].a * arr[offset].b]);
}
// No need to safeguard because all our struct members are `int`, so
// `offset` will, necessarily, be a multiple of `sizeof *arr`
return offset / sizeof *arr;
}
但在我看來這絕對是麻煩的。 我已將其聲明為inline
以嘗試遵循避免 function 調用的模式(假設您選擇將get_array
定義為宏)。 並且 function 甚至可能不會被內聯,除非您嚴格強制編譯器這樣做。 有關內聯函數的更多信息,請參見此處和此處。
這個新設置的示例用法如下:
/* ... */
#define NUMBER_OF_MATRIX 2
#define TOTAL_TABLE_ELEMENTS 8
#define SIZE ((sizeof *a) * NUMBER_OF_MATRIX + sizeof(int[TOTAL_TABLE_ELEMENTS]))
int main() {
Matrix *a = calloc(1, SIZE);
a[offset_for_index(a, 0)].a = 2;
a[offset_for_index(a, 0)].b = 2;
a[offset_for_index(a, 1)].a = 2;
a[offset_for_index(a, 1)].b = 2;
for (size_t i = 0; i < 2; ++i) {
for (size_t j = 0; j < 2; ++j) {
get_array(a, 0)[i][j] = 10 * (i + 1);
get_array(a, 1)[i][j] = -10 * (i + 1);
}
}
// Your output block
for (uint i = 0; i < NUMBER_OF_MATRIX; i++) {
printf("Matrix %d\n", i+1);
size_t idx = offset_for_index(a, i);
for (uint x = 0; x < a[idx].rows; x++) {
for (uint y = 0; y < a[idx].columns; y++) {
printf("%d ", get_array(a, i)[x][y]);
}
printf("\n");
}
}
free(a);
return 0;
}
這並不是說他們確實在工作。 您只是在打印您剛剛設置的內容。 您所做的與以下內容幾乎相同:
/* ... */
int n;
scanf("%d", &n);
pritnf("%d\n", n);
“只有西斯才做絕對的交易” 。 克諾比,歐比萬。
我不是特別喜歡通過說某事是正確的方式或某事永遠不應該使用/完成來限制方法(甚至goto
在 IMO 中也有它的位置)。 也就是說,我不會告訴您分配 memory 的正確方法是什么。但是,正如我之前提到的,根據我目前掌握的信息,我認為最好的方法是為table
成員動態分配 memory . 您的結構定義將更新為:
typedef struct {
uint rows, columns;
int *table;
} Matrix;
在設置新矩陣后,您需要為table
分配 memory :
Matrix *matrix = malloc((sizeof *matrix) * NUMBER_OF_MATRIX);
for (uint i = 0; i < NUMBER_OF_MATRIX; i++) {
matrix[i].rows = dimensions[i][0];
matrix[i].columns = dimensions[i][1];
matrix[i].table = malloc(sizeof(int) * matrix[i].rows * matrix[i].columns);
for (uint x = 0; x < matrix[i].rows; x++) {
for (uint y = 0; y < matrix[i].columns; y++)
{
printf("Enter values of matrix %d a[%dx%d] : ", i + 1, x + 1, y + 1);
scanf("%d", &get_array(matrix[i])[x][y]);
printf("%d\n", get_array(matrix[i])[x][y]); // print statement 1
// Mind `get_array` here is your original macro!
}
}
}
顯然,您需要在不再需要時free
memory。 假設只有當您的程序完成時才會出現這種情況,以下方法可以解決問題:
for (uint i = 0; i < NUMBER_OF_MATRIX; i++) {
free(matrix[i].table);
}
free(matrix);
我不會回答最后一個問題,因為我相信我已經通過上面的部分一遍又一遍地回答了。 簡而言之,您應該考慮您的特定需求和您認為更重要的東西(可能是更清晰的代碼,或者可能是一點——或更多——更高的性能)。
我希望這能澄清問題,您將能夠思考並為您的項目選擇最佳的前進方式。 最好的祝福!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.