我知道有非常相似的问题,但我已经阅读了它们,我不明白出了什么问题,或者我究竟对指针的指针没有理解。
我的老师正在使用“边做边学”的方法指针,这给我留下了大约100个问题。有时我只是改变一切,直到它编译,但它真的没有变得更清楚,所以如果有人可以帮助澄清一些事情,我真的很感激。
struct Matrix {
int rows; // number of rows
int cols; // number of columns
double **data;
};
typedef struct Matrix Matrix;
指向指针的指针是这样的,对吧?
double *row1 = (double *) malloc (n_cols*sizeof(double));
double *row2 = (double *) malloc (n_cols*sizeof(double));
double *row3 = (double *) malloc (n_cols*sizeof(double));
double *data[] = { row1, row2, row3};
数据指向行号,该行号指向行中的双精度。
现在我应该创建一个构造函数,使每个位置的0都返回指向Matrix的指针。
Matrix *make_matrix(int n_rows, int n_cols) {
Matrix *m = xmalloc(sizeof(Matrix));
m->rows = n_rows;
m->cols = n_cols;
double **rows_and_columns = xmalloc(n_rows * n_cols * sizeof(double));
memset(rows_and_columns, 0, m->rows * m->cols * sizeof(double));
m->data = *rows_and_columns;
return m;
}
所以我为矩阵做了一个指针,然后我为行和列分配了值。然后我感到困惑(虽然我不确定有多困惑,因为这部分编译)。我做了一个指向结构矩阵的最后一个元素(**data
)的指针。由于**rows_and_columns
必须保存行和列,因此我为其分配了xmalloc(n_rows * n_cols * sizeof(double))
个内存。然后我将整个事物设置为0并将其分配给数据。我认为m->data = rows_and_columns;
表示m
指向数据,因为数据是指针而rows_and_columns
是指针,我们将对齐其地址,因此m->data
也会指向一堆0?或者这是错的?我正在返回m
,因为输出为Matrix *
而m
在输出时会得到*
,是否正确?
下一步是复制矩阵,此时我失去了更多。
Matrix *copy_matrix(double *data, int n_rows, int n_cols) {
Matrix *m = make_matrix(n_rows, n_cols);
double *row = (double *) malloc (n_cols*sizeof(double));
int i = 0;
for (int j = 0; j < n_rows; j++) {
for (; i < n_cols; i++) {
row = (double *) malloc (n_cols*sizeof(double));
row [i] = data[i + j*n_cols];
}
m->data [j] = *row [i];
}
free(row);
return m;
}
所以我们再次返回指向Matrix的指针。输入现在是指向double值的指针,我假设它是行。首先,我做了一个矩阵。然后我为一行有一个n列内存(double *) malloc (n_cols*sizeof(double))
的行做了一个指针。
这是我非常困惑的地方。我一直想象**data
像上面那样(double *data[] = { row1, row2, row3};
)。所以,我想将*data
的每一行复制到*row
,然后将其保存为**data
中的条目,例如data[0] = row0
,但有些内容没有点击指针,因为我不能指定m->data [j] = row [i];
,因为我通过从double *
类型中分配double
来分配不兼容的类型?
答案 0 :(得分:3)
xmalloc()将void *返回到单个内存块。 你需要的是: 1块作为“上排”,持有指向OTHER内存块的指针,持有 实际的双打。方案:
double **columns ==> [double *col0] [double *col1] [double *col2] ...
| | |
V V V
[double col_val0] [double col_val0] ...
[double col_val1] [double col_val1]
[double col_val2] [double col_val2]
[double col_val3] [double col_val3]
[double col_val4] [double col_val4]
.... ...
所以正确的矩阵分配是:(关键字:循环!) 伪C:
// allocate the double pointer array:
double **matrix_rows = xmalloc(sizeof(double *) * column_count);
// iterate over each double-pointer in the double-pointer-array allocated above
for(int i = 0; i < column_count; i++)
{
// allocate a new double-array, and let current double-pointer point to it:
matrix_rows[i] = malloc(sizeof(double) * row_count);
// init matrix cell: (iterate over allocated values above)
for(int cell = 0; cell < row_count; cell++)
{
// you access 'i' col, and 'cell' row in col, then set it to 0:
matrix_rows[i][cell] = 0.0;
}
}
这就是make_matrix函数。 复制函数将对源矩阵的每个单元格进行迭代访问,并将其值复制到目标矩阵中的对应单元格(相同的行和列)。 那是用“两次循环”完成的(再次):
for(int col = 0; col < col_count; col++)
{
for(int row = 0; row < row_count; row++)
{
// Destination matrix: new matrix, where copied values should be stored
// Source matrix: alreay known matrix, where values to be copied are taken
destination_matrix[col][row] = source_matrix[col][row];
}
}
矩阵的双** dptr是一个所谓的二维数组。
我希望这个答案可以帮助你,至少可以更好地了解c-arrays,尽管我(可能)可怕的英语和懒惰写出正确的句子;)
否则,请随意发表评论
YEAH - 这是我在堆叠溢出的第一个答案!
答案 1 :(得分:2)
如果要将矩阵表示为指向行的指针数组,则需要为行和指向行的数组分配内存。将行连续分配为单个块更简单。
typedef struct
{
int n_rows;
int n_cols;
double **rows;
double *data;
} Matrix;
Matrix *matrix_new (int n_rows, int n_cols)
{
// allocate matrix
Matrix *m = malloc (sizeof(Matrix));
m->n_rows = n_rows;
m->n_cols = n_cols;
// allocate m->data
m->data = malloc (n_rows * n_cols * sizeof(double));
// allocate and fill m->rows
m->rows = malloc (n_rows * sizeof(double*));
for (int i = 0; i < n_rows; i++) {
m->rows[i] = &data[i * n_cols];
}
// set the matrix to 0
for (int i = 0; i < n_rows; i++) {
for (int j = 0; j < n_cols; j++) {
m->rows[i][j] = 0.0;
}
}
return m;
}
行数组的目的是为了方便您使用m->rows[i][j]
代替m->data[i * m->n_cols + j]
来引用元素i,j。
要释放矩阵,请执行相反的步骤:
void matrix_free (Matrix *m)
{
free (m->rows);
free (m->data);
free (m);
}
要复制,您只需分配相同大小的矩阵并按元素复制:
Matrix *matrix_copy (Matrix *m1)
{
Matrix *m2 = matrix_new (m1->n_rows, m1->n_cols);
for (int i = 0; i < m1->n_rows; i++) {
for (int j = 0; j < m1->n_cols; j++) {
m2->rows[i][j] = m1->rows[i][j];
}
}
return m2;
}
需要注意的重要一点是,您不得复制行数组,因为它对每个矩阵都是唯一的。
答案 2 :(得分:1)
理解指针指针和多维数组之间的区别非常重要。
令人困惑的是,相同的语法用于引用单个元素:var[i][j]
将引用var
的元素(i,j),而不管var
是否为指针到指针,double **var
或二维数组,double var[22][43]
。
真正发生的事情是不一样的。二维阵列是连续的存储块。指向指针的指针是指向各行的指针数组。
// Two-dimensional array
char var1[X1][X2];
int distance = &var[4][7] - &var[0][0]; // distance = 4*X2+7
// Pointer-to-pointer
char **var2 = malloc(X1 * sizeof(char*)); // Allocate memory for the pointers
for (i=0; i<X1; i++) var2[i] = malloc(X2); // Allocate memory for the actual data
int distance2 = &var2[4][7] - &var[0][0]; // This could result in anything, since the different rows aren't necessarily stored one after another.
distance2
的计算调用未定义的行为,因为C标准没有涵盖指向不同内存块的指针的指针算法。
您想使用指针指针。因此,您需要首先为指针数组分配内存,然后为每个数组分配内存:
Matrix *make_matrix(int n_rows, int n_cols) {
Matrix *m = xmalloc(sizeof(Matrix));
int i, j;
m->rows = n_rows;
m->cols = n_cols;
m->data = xmalloc(n_rows * sizeof(double*));
for (i=0; i < n_; i++) {
m->data[i] = xmalloc(n_cols * sizeof(double));
for (j=0; j < n_cols; j++) {
m->data[i][j] = 0.0;
}
}
return m;
}
不要假设double 0.0将所有位设置为0!
复制矩阵:
Matrix *copy_matrix(Matrix *source) {
Matrix *m = make_matrix(source->n_rows, source->n_cols);
int i, j;
for (j = 0; j < n_rows; j++) {
for (i = 0; i < n_cols; i++) {
m->data[i][j] = source[i][j];
}
}
return m;
}
答案 3 :(得分:0)
我会稍微备份并从基础开始。指针是技术上难以理解的事情之一,但是要求你在我想要了解指针足够让他们沉入其中。你理解一个正常变量(因为缺少更好的单词)只是一个变量,它将直接引用保存到内存中的立即值。
int a = 5;
此处,a
是包含值5
的内存地址的标签。
另一方面,指针不会直接引用 立即值,如5
。相反,指针保存存储5
的存储器地址作为其值。您也可以通过这种方式思考这种差异。普通变量保存一个值,而指针保存可以找到值的地址。
例如,要将指针'b'声明为上面保存5
的内存地址,您可以执行以下操作:
int *b = &a;
或等效地:
int *b = NULL;
b = &a;
b
的地址分配了a
的地址。要返回存储在指针所占地址的值,或直接操作存储在指针所占地址的值,必须取消引用指针。 e.g。
int c = *b; /* c now equals 5 */
*b = 7; /* a - the value at the address pointed to by b, now equals 7 */
现在快进到指向指针到类型的指针并模拟2D基质。当您声明指针指向类型指针(例如int **array = NULL
)时,您声明指向指针的指针 - 到型。要在模拟的2D数组(matricies等)中有用,你必须删除指向指针到类型的指针数组:
int **array = NULL;
...
array = calloc (NUM, sizeof *array); /* creates 'NUM' pointer-to-pointer-to-int. */
你现在有NUM
指向指针到int 的指针,你可以分配内存来保存多少int
个值,你将为内存分配起始地址阻止将这些int
值保存到您之前分配的指针中。例如,假设您要为5
个随机int
值(从1到1000)的数组分配空间到上面分配的每个NUM
指针:
for (i = 0; i < NUM; i++) {
array[i] = calloc (5, sizeof **array);
for (j = 0; j < 5; j++)
array[i][j] = rand() % 1000 + 1;
}
您现在已经为每个NUM
指针( to-pointer-to-int )分配了内存中的起始地址,其中存储了5个随机int
值。所以你的阵列现在已经完成了。现在,每个原始NUM
指针指向指针都指向一个包含5 int
个值的内存块的地址。您可以使用数组表示法访问每个值(例如array[i][j]
或使用指针表示法*(*(array + i) + j)
)
你如何释放记忆?按照相反的顺序分配(值,然后是指针):
for (i = 0; i < NUM; i++)
free (array[i]);
free (array);
注意: calloc
都会分配内存并将内存初始化为0/nul
。当处理数值时,以及处理未知数量的值行时,这对指针和数组特别有用。它不仅可以防止从未初始化的值中无意中读取,而且还允许您使用i = 0; while (array[i] != NULL) {...}
迭代指针数组。