矩阵函数中指针问题的C指针

时间:2015-12-07 15:04:12

标签: c pointers matrix

我知道有非常相似的问题,但我已经阅读了它们,我不明白出了什么问题,或者我究竟对指针的指针没有理解。

我的老师正在使用“边做边学”的方法指针,这给我留下了大约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来分配不兼容的类型?

4 个答案:

答案 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是一个所谓的二维数组。

  1. 1d数组的示例:字符串,包含字符数组
  2. 二维数组的示例:文本,包含行数组(字符串),它们本身包含字符数组
  3. 一个3d数组的例子:一本拿着一排线的书,自己拿着一排线......
  4. 我希望这个答案可以帮助你,至少可以更好地了解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) {...}迭代指针数组。