arrays - 在 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) );
所以,我的问题是:使用这个宏安全吗?在某些情况下会导致不确定的行为吗?
解决方案
由于您正确地将 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
函数以降低开销;编译器应该能够避免取地址/取消引用两步,使代码的性能相当于手写更复杂的表达式。
推荐阅读
- java - 使用声明式客户端 Micronaut 将多个文件上传到 MultipartBody
- javascript - 如何将菜单更改为响应式引导菜单
- python - 如何将毫秒 Unix 时间戳转换为可读日期?
- android - Flutter_blue 和 Adafruit Bluefruit BLE 特征错误:找不到特征的 CCCD 描述符
- django - 如何在 django 模板中使用 if else
- android - 使用 share 2.0.0 包我想分享我的应用程序的链接,但我还没有发布我的应用程序来玩商店
- qt - 在 Qt 中使用 CMake 的主项目之前如何构建依赖库?
- visual-studio - 视觉工作室的signtool.exe
- kubernetes - k8s中不同容器之间如何共享netns?
- css - 使用 Tailwind 时如何使用 Webpack 缩小 CSS