C:在另一个函数内初始化动态类型的2D矩阵

时间:2017-09-22 01:54:32

标签: c matrix dynamic matrix-multiplication

我正在尝试创建3个矩阵,这些矩阵是动态类型化的(int,float,double),用于矩阵乘法。我创造了一个虚空**"打字" main中每个矩阵的容器,然后根据用户对类型的选择将它们(作为void ***)传递到init函数中以进行malloc。下面的代码编译,但是我在j循环的一次迭代后得到了一个分段错误,我无法弄清楚为什么会发生这种情况。类似地,如果我在一个单独的2-deep循环中进行初始化(从malloc循环中取出j-loop),那么在i-loop的一次迭代之后仍然会发生分段错误。

这是实现动态类型矩阵乘法目标的好方法吗?非常感谢您的帮助。

void initMat(int type, int matSize, void ***matA, void ***matB, void ***matC)
{
    int i, j, k;

    switch(type) {
        case 0 :
        *matA = malloc(matSize * sizeof(int*));
        *matB = malloc(matSize * sizeof(int*));
        *matC = malloc(matSize * sizeof(int*));
        for (i = 0; i < matSize; i++) {
            *matA[i] = malloc(matSize * sizeof(int));
            *matB[i] = malloc(matSize * sizeof(int));
            *matC[i] = malloc(matSize * sizeof(int));
            for (j = 0; j < matSize; j++) {
                *(int*)matA[i][j] = rand()/RAND_MAX * 10;
                *(int*)matB[i][j] = rand()/RAND_MAX * 10;
                *(int*)matC[i][j] = 0;
            }
        }
        break;

        case 1 :
        // with float, double, etc.
        break;

        default :
        printf("Invalid case.\n" );
    }
}

int main()
{
    int type = 0;
    int size = 0;
    void **matA, **matB, **matC;

    int sizes[6] = {3, 4, 5};
    int matSize = sizes[size];
    printf("The selected matrix size is: %d. \n", matSize); //allows user to select matrix size

    initMat(type, matSize, &matA, &matB, &matC);

//  displayMat(matSize, matA);

}

4 个答案:

答案 0 :(得分:1)

我自己一直在做一些完全相同的事情。我对如何做到这一点的建议是实际上放弃你做这件事的方式。从它的外观来看,你有一个指针数组(比如一个int指针数组),这些指针中的每一个都有一个自己的数组,基本上是一个声明为int **exampleMatrix的变量。实际上这是一个很大的问题,你这样做就是缓存未命中。更好的方法是:

#include <stdlib.h>

void *initMat(size_t rows, size_t cols, int type);

int main(void){
    int *matrix = initMat();
    free(matrix);
    return 0;
}

void *initMat(size_t rows, size_t cols, int type){
    //check for what type to allocate here
    void *matrix = malloc(sizeof(int)*rows*cols);
    //check for errors here
    return matrix;
}

你当然必须决定你的矩阵是行还是列。我希望这是有道理的,英语不是我的第一语言,而且我有时并不是最好的解释。如果您需要我解释一下,请告诉我:)

答案 1 :(得分:1)

要使用动态分配的2d数组,应使用正确的指针类型。 (int **)是指向指针的指针,指向指针数组的第一个元素,这些指针本身指向不同的分配。这种代码的结果是锯齿状数组,但不是2d数组。分配的内存不保证是连续的(因为数组分配必须是):

size_t num_rows = 3;
size_t num_cols = 5;

int **jagged_arr = malloc(sizeof *jagged_arr * num_rows);

for (size_t i = 0; i < num_rows; i++) {
    jagged_arr[i] = malloc(sizeof *jagged_arr[i] * num_cols);
}

一种可能性是简单地为1d数组分配存储,并从2d数组索引计算该数组的偏移量。这很好,但结果不是二维数组:

size_t num_elems = num_rows * num_cols;
int *simulated_2d_arr = malloc(sizeof *simulated_2d_arr * num_elems);

这不能被索引为2d数组,但是1d索引可以根据列数和2d数组索引计算:

for (size_t i = 0; i < num_rows; i++) {
    for (size_t j = 0; j < num_cols; j++) {
        simulated_2d_arr[i * num_cols + j] = i * num_cols + j;
    }
}

这两种方法都有它们的用途,但是它们的缺点是不能将得到的数组传递给用于2d数组的函数。也就是说,考虑打印二维数组的函数,例如:

void print_2d_arr(size_t rows, size_t cols, int arr[][cols])
{
    for (size_t i = 0; i < rows; i++) {
        for (size_t j = 0; j < cols; j++) {
            printf("%5d", arr[i][j]);
        }
        putchar('\n');
    }
}

此功能适用于:

int real_2d_arr[2][3] = { { 1, 2, 3 },
                          { 4, 5, 6 } };

但它不适用于早期的jagged_arr

  

预期'int(*)[(sizetype)(cols)]'但参数的类型为'int **'

simulated_2d_arr

  

期望'int(*)[(sizetype)(cols)]'但参数的类型为'int *'

在上面的错误消息中可以看到动态分配2d数组时使用的正确类型。对于int s的二维数组,这将是int (*)[]。这是二维数组在大多数表达式中衰减的类型,包括函数调用。因此,要动态分配int的2d数组,这将起作用:

size_t num_rows = 3;
size_t num_cols = 5;
int (*array_2d)[num_cols] = malloc(sizeof *array_2d * num_rows);

这会为num_rows num_cols的{​​{1}}数组分配空间。请注意,这不会创建VLA,但会使用VLA 类型。当然,VGA是在C99中引入的,但在C11中是可选的(尽管仍然得到广泛的支持)。

对于问题的动态类型部分,一个选项是创建一个int来保存类型标识符,并将这些枚举常量之一传递给任何需要它们的函数。这些函数需要接受enum个参数,这些参数将根据类型枚举常量进行适当的转换。这是一个更多的参与,但这是一个示例程序。请注意,(void *)函数既适用于动态分配的数组,也适用于静态大小的数组。另请注意,不需要三重甚至双重间接!

print_array()

节目输出:

#include <stdio.h>
#include <stdlib.h>

enum Type { CHAR,
            INT,
            FLOAT,
            DOUBLE };

void * get_array(enum Type type, size_t rows, size_t cols);
void init_array(enum Type type, size_t rows, size_t cols, void *arr);
void print_array(enum Type type, size_t rows, size_t cols, void *arr);

int main(void)
{
    char (*arr_char)[5] = get_array(CHAR, 4, 5);
    int (*arr_int)[5] = get_array(INT, 4, 5);
    double (*arr_double)[5] = get_array(DOUBLE, 4, 5);

    int arr_static[][3] = { { 1, 2, 3 },
                            { 4, 5, 6 },
                            { 7, 8, 9 } };

    if (arr_char) {                           // check for null pointer
        init_array(CHAR, 4, 5, arr_char);
        puts("4x5 array of char");
        print_array(CHAR, 4, 5, arr_char);
        putchar('\n');
    }

    if (arr_int) {                            // check for null pointer
        init_array(INT, 4, 5, arr_int);
        puts("4x5 array of int");
        print_array(INT, 4, 5, arr_int);
        putchar('\n');
    }

    if (arr_double) {                         // check for null pointer
        init_array(DOUBLE, 4, 5, arr_double);
        puts("4x5 array of double");
        print_array(DOUBLE, 4, 5, arr_double);
        putchar('\n');
    }

    puts("Statically sized 3x3 array of int");
    print_array(INT, 3, 3, arr_static);
    putchar('\n');

    /* Cleanup */
    free(arr_char);
    free(arr_int);
    free(arr_double);

    return 0;
}

/* Returns null pointer on allocation failure */
void *get_array(enum Type type, size_t rows, size_t cols)
{
    size_t array_sz = 0;
    void *ret = NULL;

    switch (type) {
    case CHAR:
        array_sz = sizeof (char) * rows * cols;
        break;
    case INT:
        array_sz = sizeof (int) * rows * cols;
        break;
    case FLOAT:
        array_sz = sizeof (float) * rows * cols;
        break;
    case DOUBLE:
        array_sz = sizeof (double) * rows * cols;
        break;
    default:
        fprintf(stderr, "Unrecognized type in get_array()");
    }

    if (array_sz) {
        ret = malloc(array_sz);
    }

    return ret;
}

void init_array(enum Type type, size_t rows, size_t cols, void *arr)
{
    for (size_t i = 0; i < rows; i++) {
        for (size_t j = 0; j < cols; j++) {
            int offset = i * cols + j;
            switch (type) {
            case CHAR:
            {
                char (*array_char)[cols] = arr;
                array_char[i][j] = 'a' + offset;
                break;
            }
            case INT:
            {
                int (*array_int)[cols] = arr;
                array_int[i][j] = 0 + offset;
                break;
            }
            case FLOAT:
            {
                float (*array_float)[cols] = arr;
                array_float[i][j] = 0.0 + offset;
                break;
            }
            case DOUBLE:
            {
                double (*array_double)[cols] = arr;
                array_double[i][j] = 0.0 + offset;
                break;
            }
            default:
                fprintf(stderr, "Unrecognized type in get_array()");
            }
        }
    }
}

void print_array(enum Type type, size_t rows, size_t cols, void *arr)
{
    for (size_t i = 0; i < rows; i++) {
        for (size_t j = 0; j < cols; j++) {
                switch (type) {
            case CHAR:
            {
                char (*array_char)[cols] = arr;
                printf("%3c", array_char[i][j]);
                break;
            }
            case INT:
            {
                int (*array_int)[cols] = arr;
                printf("%5d", array_int[i][j]);
                break;
            }
            case FLOAT:
            {
                float (*array_float)[cols] = arr;
                printf("%8.2f", array_float[i][j]);
                break;
            }
            case DOUBLE:
            {
                double (*array_double)[cols] = arr;
                printf("%8.2f", array_double[i][j]);
                break;
            }
            default:
                fprintf(stderr, "Unrecognized type in get_array()");
            }
        }
        putchar('\n');
    }
}

答案 2 :(得分:0)

您的基本问题是后缀运算符的优先级高于前缀运算符。所以当你说

*matA[i] = malloc(matSize * sizeof(int));
你正在

*(matA[i]) = malloc(matSize * sizeof(int));

当你想要的是什么

(*matA)[i] = malloc(matSize * sizeof(int));

因此您需要明确的括号才能使其正常工作。同样,而不是

*(int*)matA[i][j] = rand()/RAND_MAX * 10;

你需要

((int**)*matA)[i][j] = rand()/RAND_MAX * 10;

答案 3 :(得分:-1)

你分配内存的方式很奇怪。

  

*matA = malloc(matSize * sizeof(int*));

matA指向哪里,除非你在这个函数之外分配了一些内存并让matA指向它,那么你就是在捣乱你的记忆。

Malloc返回指向已分配内存的指针,它不会在随机指针指向的任何位置分配它。

您也不需要三个级别的间接***matA,两个就足够了。

这是你的代码,我建议的更改 - 免责声明:我实际上并没有尝试这个,但我认为它会起作用 - :

void initMat(int type, int matSize, void ***matA, void ***matB, void ***matC)
{
    int i, j, k;

    switch(type) {
        case 0 :
            *matA = malloc(matSize * sizeof(int*));
            *matB = malloc(matSize * sizeof(int*));
            *matC = malloc(matSize * sizeof(int*));
            for (i = 0; i < matSize; i++) {
                (*matA)[i] = malloc(matSize * sizeof(int));
                (*matB)[i] = malloc(matSize * sizeof(int));
                (*matC)[i] = malloc(matSize * sizeof(int));
                for (j = 0; j < matSize; j++) {
                    (*matA)[i][j] = rand()/RAND_MAX * 10;
                    (*matB)[i][j] = rand()/RAND_MAX * 10;
                    (*matC)[i][j] = 0;
                }
            }
        break;

        case 1 :
        // with float, double, etc.
        break;

        default :
            printf("Invalid case.\n" );
    }
}

并且在致电时:

int type,matSize;
//whatever type you like
Type **matA,**matB,**matC; 
//your code here
initMat(type,matSize,&matA,&matB,&matC);
//the rest of your code here