使用三重+指针会损害可读性和可维护性。
假设您在这里有一些函数声明:
void fun(int***);
嗯。参数是三维锯齿状数组,还是指向二维锯齿状数组的指针,还是指向数组的指针(例如,函数分配一个数组并在函数内分配一个指向 int 的指针)
让我们将其与以下内容进行比较:
void fun(IntMatrix*);
当然,您可以使用指向 int 的三重指针来对矩阵进行操作。但他们不是这样的。事实上,它们在这里作为三重指针实现的是不相关的给用户。
复杂的数据结构应该是封装的。这是面向对象编程的明显思想之一。即使在 C 语言中,你也可以在某种程度上应用这个原则。将数据结构包装在 struct 中(或者,在 C 中很常见,使用“句柄”,即指向不完整类型的指针 - 这个习惯用法将在后面的答案中解释)。
假设您将矩阵实现为锯齿状数组double
。与连续的二维数组相比,它们在迭代时性能更差(因为它们不属于单个连续内存块),但允许使用数组表示法进行访问,并且每行可以具有不同的大小。
所以现在的问题是您现在无法更改表示形式,因为指针的使用是通过用户代码硬连接的,现在您陷入了较差的实现。
如果将其封装在结构中,这甚至不会成为问题。
typedef struct Matrix_
{
double** data;
} Matrix;
double get_element(Matrix* m, int i, int j)
{
return m->data[i][j];
}
只需更改为
typedef struct Matrix_
{
int width;
double data[]; //C99 flexible array member
} Matrix;
double get_element(Matrix* m, int i, int j)
{
return m->data[i*m->width+j];
}
句柄技术的工作原理如下:在头文件中,声明一个不完整的结构体以及作用于该结构体指针的所有函数:
// struct declaration with no body.
struct Matrix_;
// optional: allow people to declare the matrix with Matrix* instead of struct Matrix*
typedef struct Matrix_ Matrix;
Matrix* create_matrix(int w, int h);
void destroy_matrix(Matrix* m);
double get_element(Matrix* m, int i, int j);
double set_element(Matrix* m, double value, int i, int j);
在源文件中,您声明实际的结构并定义所有函数:
typedef struct Matrix_
{
int width;
double data[]; //C99 flexible array member
} Matrix;
double get_element(Matrix* m, int i, int j)
{
return m->data[i*m->width+j];
}
/* definition of the rest of the functions */
世界其他地方不知道这意味着什么struct Matrix_
包含并且它不知道它的大小。这意味着用户不能直接声明值,而只能使用指向的指针Matrix
和create_matrix
功能。然而,用户不知道大小的事实意味着用户不依赖它 - 这意味着我们可以删除或添加成员struct Matrix_
随意。