Arrays 在C中使用类似宏的函数访问矩阵元素安全吗?
我有一个矩阵结构,定义如下:Arrays 在C中使用类似宏的函数访问矩阵元素安全吗?,arrays,c,matrix,Arrays,C,Matrix,我有一个矩阵结构,定义如下: struct matrix_double_complex { double complex *data; int number_of_rows; int number_of_columns; }; typedef struct matrix_double_complex MatrixDoubleComplex; MATRIX_DOUBLE_COMPLEX_ELEMENT(result,i,j) = MATRIX_DOUBLE_COMPLE
struct matrix_double_complex
{
double complex *data;
int number_of_rows;
int number_of_columns;
};
typedef struct matrix_double_complex MatrixDoubleComplex;
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) );
我使用一维动态数组来存储矩阵元素,因为我想将它们存储在单个内存块中
结构在此函数中初始化:
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]
来访问矩阵元素
我知道我需要计算索引:
矩阵->数据[(i-1)*(矩阵->列数)+(j-1)]
但这是丑陋的语法
我有“设置”和“获取”函数来访问矩阵元素:
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);
}
}
}
使用此宏,我可以方便地编写如下内容:
struct matrix_double_complex
{
double complex *data;
int number_of_rows;
int number_of_columns;
};
typedef struct matrix_double_complex MatrixDoubleComplex;
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) );
所以,我的问题是:使用这个宏安全吗?在某些情况下,它会导致未定义的行为吗?因为您正确地将
i
和j
括起来,并且只使用它们一次,所以这些参数是安全的。如果以复杂的方式使用矩阵
参数,理论上是不安全的,例如,将指针算术与++
/--
一起用于矩阵数组中的指针。在这种情况下,评估矩阵的副作用将发生两次,在以下情况下产生未定义的行为:
MATRIX_DOUBLE_COMPLEX_ELEMENT(*matrixptr++, 0, 1)
计算(因为在展开表达式中出现两次matrixptr++
)。如果调用一个非幂等函数或具有副作用的函数来生成矩阵
参数,则可能会出现类似的问题。只要您坚持简单的用法(矩阵的参数始终是一个简单的变量名),这就行了
如果您想消除这种误用的风险,可以使用一个函数来检索指向矩阵元素的指针;取消对结果的引用,它可以用于读取和写入该元素。如果需要,宏可以包装该函数,其中宏为您执行解引用,同时保持函数提供的单一求值行为:
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
函数,以降低开销;编译器应该能够避免采用地址/取消引用两步,使代码的性能相当于手工编写更复杂的表达式。IMO宏应该避免,只有在绝对必要时才使用。
宏极易出错,尤其是在以下情况下:
不使用括号
您可以使用多个特定参数(#define SQR(x)(x)*(x)
y=SQR(x++);
)
宏内部的函数调用有副作用
如果您担心函数调用开销,那么编译器很可能会自动内联它们。如果你知道编译器,你也可以强制内联
gcc
示例:
inline __attribute__((always_inline)) 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) ];
}
宏没有问题。在预处理器上下文中,没有太多事情可以导致UB,这里也没有。例如,如果矩阵为NULL
,它肯定会导致UB,但这与它是一个宏这一事实并不相关。它是安全的,只需放置大量()是否要使用索引1。。行数
而不是通常的0。。(行数-1)
?这可能会让C程序员感到困惑。博多,我不确定。但是数学家通常把矩阵元素写成a{11},a{12},。。。