What do I want to do?

我在做一个关于动态矩阵乘法的项目.我想从用户那里输入他/她想要执行乘法的矩阵数,并基于此创建一个 struct ,如下所示:

typedef struct
{
    size_t rows, columns;
    int table[];
} Matrix;

然后,判断矩阵的有效性,乘以(using very simple maths).

然后创建一个包含Matrix种类型的数组,并根据用户想要乘法的矩阵数量为其分配内存.

+---------------------------------------------------------------------------------------------------+
|    +------------------------------+  +---------------------------------------------------------+  |
|    |   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 |

Reasons for creating an array of struct type

有一件事对我的一些读者来说可能很奇怪,那就是,为什么我要创建一个类型为struct Matrix的数组,而不是创建两个不同的类型对象并在"user_defined_matrix"->table上执行乘法.原因如下:

  • Scalability.由于每个约束都是用户定义的,我希望事情尽可能灵活.考虑这一点的一种方法是,假设用户想要15个矩阵之间的乘法,那么您不想声明15个类型为struct Matrix的对象.
  • Accessibility,在for-loop的帮助下,访问每个矩阵将变得非常容易.

现在回到主题,在分配内存之后,我希望用户填充矩阵的每个插槽,然后对它们执行乘法并生成结果.


What I have done so far

101 I know that you cannot declare a true 2d VLA inside a struct, you have to first declare a 1d array which later becomes a mangled version of a 2d array and before using it typecast it to a 2d array as shown in this 100.

我制作了一个真正的2d数组来存储用户输入的行和列.

int dimensions[NUMBER_OF_MATRIX][2];

然后用这dimensions个,我计算了我需要分配多少内存.

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_sizestruct Martix类型的数组分配内存.

Matrix *matrix = malloc(((sizeof *matrix) * NUMBER_OF_MATRIX) + sizeof(int[total_matrix_size]));

在那之后,我要求用户填充矩阵,在填充矩阵之前,我在下面代码中的宏的帮助下,将数组转换为2darray.

100 I'll be referring to the below code block many times so for the sake of simplicity, let's name this 101.

#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
                }
            }
        }

为了测试,我打印了所有的矩阵.

100 I'll be referring to the below code block many times so for the sake of simplicity, let's name this 101.

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

So, where is the problem?

正如你所见,我在上面的两个代码块inputoutput中写了两个完全相同的print语句.

printf("%d ", get_array(matrix[i])[x][y]);

但它们都产生了不同的输出.

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 block人的情况不一样.

Matrix 1
2 2
2 3
Matrix 2
2 3
2 3

What do I expect from the answers?

只不过是一个深入的解释:

  • 为什么打印报表在input block&amp;为什么不进来
  • 如果这不是分配内存的正确方法,那么是什么?
  • 如果我的方法完全错误,那应该是什么?

请保持answer的格式.


这是全部代码

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

推荐答案

我先直截了当地说.之后我会详细介绍你要求的具体话题.

Solution

问题

简而言之,在我看来,你对这个问题的理论方法是合理的,但内存管理没有得到正确的实施.

您要做的是拥有Matrix struct 的多个实例.这不是当前设置的结果.明确地:

Matrix *matrix = malloc(((sizeof *matrix) * NUMBER_OF_MATRIX) + sizeof(int[total_matrix_size]));

在这里,您为NUMBER_OF_MATRIX Matrix个实例分配了足够的内存,当然.但你这样做是为了Matrix型.给定matrixpointer to Matrixsizeof *matrix(或sizeof(Matrix))等于sizeof(int[2]) (1),当你索引该数组(即执行指针算术)时,内存偏移增量就是这个大小.

让我们假设sizeof(int) = 4NUMBER_OF_MATRIX = 2total_matrix_size = 8(2 2x2个矩阵).如果给matrix指定了内存地址0x0010,那么&(matrix[1])将是0x0018,而不是预期的0x0028(偏移量为24字节).

有关C如何处理指针算法的更多参考资料,请参阅this SO answerthis article,其中有关于该主题的更详细概述.

在我看来,实现你想要的最干净的方法是动态地单独分配matrix[i].table个内存,而不是一次预先分配所有你需要的内存.这有几个原因:

  1. 这对其他读者和你自己来说都会更干净(这样的问题发生的可能性更小)
  2. 随着所需的总内存越来越大,操作系统越来越难以处理单个分配,因为它试图找到一个连续的内存部分.这可能是应用程序的一个问题,也可能不是,但无论如何都要注意这一点.请参阅第this article series页,以了解关于该事项的更深入概述(2)

(1) :我正在考虑给定的only int struct 示例.这意味着不需要padding美元.如果有不同类型的成员,由于添加了填充, struct 的总大小可能略大于成员大小之和.

(2) :一次分配比多次分配更快.这通常是正确的,因为分配内存有一个固有的开销.同样,这是你在申请时需要单独考虑的事情,而不是把一个笼统的陈述当作事实

可能的解决方案

我能想到的唯一一个让你继续遵循这一策略的原因是,如果出于性能原因,你需要这样做,并且你想最小化malloc个调用(可能你工作的平台对mem分配有很重的惩罚).考虑到我可以建议修复当前方法的一件事是将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函数(或宏,如果您愿意),例如:

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,以try 并遵循避免函数调用的模式(假设您 Select 将get_array定义为宏).函数甚至可能不会内联,除非您严格强制编译器这样做.更多关于内联函数herehere的信息.

此新设置的示例用法如下:

/* ... */

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

Why were the print statements working in the input block?

并不是说他们正好working岁.你只是在打印你刚刚设置的内容.你所做的几乎和:

  /* ... */
  
  int n;
  scanf("%d", &n);
  pritnf("%d\n", n);

Correct way of allocating memory

only a sith deals in absolutes

"Only a sith deals in absolutes"克诺比,欧比万.

我并不特别喜欢通过说某事是the right way或某事应该是never来限制方法(在我看来,即使是goto也有它的位置).也就是说,我不会告诉你如何分配内存.但是,正如我之前提到的,鉴于我目前掌握的信息,我认为最好的方法是为table成员动态分配内存.您的 struct 定义将更新为:

typedef struct {
    uint rows, columns;
    int *table;
} Matrix;

在设置新矩阵时,需要为table分配内存:

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!
        }
    }
}

显然,当不再需要的时候,你需要记忆.假设只有当你的程序完成时才会出现这种情况,下面的方法就可以了:

for (uint i = 0; i < NUMBER_OF_MATRIX; i++) {
    free(matrix[i].table);
}

free(matrix);

我不会回答最后一个问题,因为我相信我已经一遍又一遍地回答了上面的部分.简而言之,您应该考虑您的具体需求和您认为更重要的内容(可能是更清晰的代码,或者可能是更高的性能).

我希望这是澄清,你将能够思考,并为你的项目 Select 最佳的前进道路.顺致敬意,

C++相关问答推荐

strftime函数中%s的历史意义是什么?为什么没有记录?

球体—立方体重叠:无、部分或全部?

如何将已分配的数组(运行时已知的大小)放入 struct 中?

为什么在4.9.37版的内核中,kfio还需要smp_wmb呢?

为什么C语言允许你使用var =(struct NAME){

C中的指针增量和减量(*--*++p)

为什么我不能只在内存地址中添加一个int来寻址任何数组?

N的值设置为0或1(未定义的行为),而我正在try 学习realloc和Malloc的用法

struct -未知大小

进程在写入管道时挂起

为什么中断函数会以这种方式影响数组?

S的这种管道实施有什么问题吗?

<;unistd.h>;和<;sys/unistd.h>;之间有什么区别?

致命错误:ASM/rwan ce.h:没有这样的文件或目录.符号链接还不够

将变量或参数打包到 struct /联合中是否会带来意想不到的性能损失?

通过对一个大的Malloc内存进行切片来使用Malloc的内存片

如何使用calloc和snprintf

将指针的地址加载到寄存器内联拇指组件中

将十六进制值或十进制值分配给 uint16_t 有什么区别?

inline 关键字导致 Clion 中的链接器错误