[英]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.