首页 > 解决方案 > 在 C 中使用类似函数的宏来访问矩阵元素是否安全?

问题描述

我有一个这样定义的矩阵结构:

struct matrix_double_complex
{
    double complex *data;
    int number_of_rows;
    int number_of_columns;
};

typedef struct matrix_double_complex MatrixDoubleComplex;

我使用一维动态数组来存储矩阵元素,因为我想将它们存储在单个内存块中。

该结构在此函数中初始化:

MatrixDoubleComplex * matrix_double_complex_initialize(int number_of_rows, int number_of_columns)
{
    MatrixDoubleComplex *matrix;

    matrix = (MatrixDoubleComplex *)malloc( sizeof(MatrixDoubleComplex) );

    
    if(matrix == NULL)
    {
        fprintf(stderr, "matrix_double_complex_initialize: Error!\n");
        exit(EXIT_FAILURE);
    }

    
    matrix->data = (double complex *)malloc( (number_of_rows)*(number_of_columns)*sizeof(double complex) );

    
    if( (matrix->data) == NULL )
    {
        fprintf(stderr, "matrix_double_complex_initialize: Error!\n");
        exit(EXIT_FAILURE);
    }


    matrix->number_of_rows = number_of_rows;
    matrix->number_of_columns = number_of_columns;

    return matrix;
}

但我不能使用方便的语法matrix->data[i][j]来访问矩阵元素。

我知道我需要计算 index: matrix->data[ (i - 1)*(matrix->number_of_columns) + (j - 1) ]

但这是丑陋的语法。

我有“set”和“get”函数来访问矩阵元素:

void matrix_double_complex_set_element(MatrixDoubleComplex *matrix, int i, int j, double complex z)
{
    matrix->data[ (i - 1)*(matrix->number_of_columns) + (j - 1) ] = z;
}

double complex matrix_double_complex_get_element(MatrixDoubleComplex *matrix, int i, int j)
{
    return ( matrix->data[ (i - 1)*(matrix->number_of_columns) + (j - 1) ] );
}

但是我不能将这些功能用作左侧。我还需要使用一些额外的变量和函数调用。

void matrix_double_complex_add(MatrixDoubleComplex *result, MatrixDoubleComplex *matrix_1, MatrixDoubleComplex *matrix_2)
{
    if( matrices_double_complex_have_the_same_dimension(result, matrix_1) == false )
    {
        fprintf(stderr, "matrix_double_complex_add: Error!\n");
        exit(EXIT_FAILURE);
    }

    if( matrices_double_complex_have_the_same_dimension(matrix_1, matrix_2) == false )
    {
        fprintf(stderr, "matrix_double_complex_add: Error!\n");
        exit(EXIT_FAILURE);
    }
    
    double complex result_ij;
    double complex matrix_1_ij;
    double complex matrix_2_ij;
        
    for(int i = 1; i <= (result->number_of_rows); i++)
    {
        for(int j = 1; j <= (result->number_of_columns); j++)
        {
            matrix_1_ij = matrix_double_complex_get_element(matrix_1, i, j);
            matrix_2_ij = matrix_double_complex_get_element(matrix_2, i, j);

            result_ij = matrix_1_ij + matrix_2_ij;

            matrix_double_complex_set_element(result, i, j, result_ij);
        }
    }
}

我知道可以使用类似函数的宏:

#define MATRIX_DOUBLE_COMPLEX_ELEMENT(matrix,i,j) ( \
  (matrix)->data[ ((i) - 1)*((matrix)->number_of_columns) + ((j) - 1) ] \
)

有了这个宏,我可以方便地写出这样的东西:

MATRIX_DOUBLE_COMPLEX_ELEMENT(result,i,j) = MATRIX_DOUBLE_COMPLEX_ELEMENT(matrix_1,i,j) + MATRIX_DOUBLE_COMPLEX_ELEMENT(matrix_2,i,j);

MATRIX_DOUBLE_COMPLEX_ELEMENT(result,i,j) = conj( MATRIX_DOUBLE_COMPLEX_ELEMENT(matrix,j,i) );

所以,我的问题是:使用这个宏安全吗?在某些情况下会导致不确定的行为吗?

标签: arrayscmatrix

解决方案


由于您正确地将 and 括i起来j,并且只使用它们一次,因此对于这些参数来说这是安全的。如果以复杂的方式使用参数,理论上它是不安全的,例如在指向矩阵数组的指针上使用带有/的matrix指针算术。在这种情况下,评估的副作用会发生两次,在以下情况下会产生未定义的行为:++--matrix

 MATRIX_DOUBLE_COMPLEX_ELEMENT(*matrixptr++, 0, 1)

被评估(matrixptr++在扩展表达式中出现两次)。如果您正在调用产生参数的非幂等函数或具有副作用的函数,则可能会出现类似问题matrix。只要您坚持简单的用法(matrix参数始终是一个简单的变量名),这将起作用。

如果你想消除这种误用的风险,你可以使用一个函数来检索指向矩阵元素的指针;取消引用结果,它可以用于读取和写入该元素。如果需要,宏可以包装该函数,宏为您执行取消引用,同时保持函数提供的单一评估行为:

double complex* matrix_double_complex_get_element_ptr(MatrixDoubleComplex *matrix, int i, int j)
{
    // Returns address of element, not element value
    return &matrix->data[ (i - 1)*(matrix->number_of_columns) + (j - 1) ];
}

#define MATRIX_DOUBLE_COMPLEX_ELEMENT(matrix,i,j) \
  (*matrix_double_complex_get_element_ptr((matrix), (i), (j)))

您可以像使用原始的、可能不安全的宏一样使用它,而不会有重复评估复杂(如形容词,而不是数据类型)matrix参数的风险。您可以将函数本身声明为显式inline函数以降低开销;编译器应该能够避免取地址/取消引用两步,使代码的性能相当于手写更复杂的表达式。


推荐阅读