【问题标题】:Why does my 2D array have such strange behavior?为什么我的二维数组有这么奇怪的行为?
【发布时间】:2015-03-02 11:04:44
【问题描述】:

编辑:代码已根据其中一个答案进行了更新,但仍然出现同样的问题。
我最近遇到了 C 中的多维数组并将它们作为参数传递的问题。我正在使用指针数组来模拟多维数组。数组中的每个指针都指向另一个数组,该数组包含一行中的值。 我为矩阵创建了一个结构,其中包含指针数组以及行数和列数。指针数组实际上只是一个指向指针的指针。我发现您可以使用任何具有非 void 数据类型的指针并将其用作数组。
主 C 文件非常基本,实际上只有几个函数调用。

#include <stdio.h>
#include <stdlib.h>
#include "matrix.h"

int main() {
  float myData[] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f};

  puts("going to create matrix");
  matrix myMatrix = createMatrix(3, 3, myData);
  puts("matrix created");
  printf("Pointer to array of pointers:\n%p\n", myMatrix.data);

  puts("Pointers to rows:");
  int i;
  for(i = 0; i < myMatrix.rows; i++) {
    printf("%p\n", myMatrix.data[i]);
  }

  puts("going to print matrix");
  printMatrix(myMatrix);
  puts("printed matrix");

  return 0;
 }

实际的矩阵编程在一个头文件中:

//matrix.h
#pragma once

typedef struct matrix {
  int columns;
  int rows;
  float **data;
} matrix;

matrix createMatrix(int columns, int rows, float initialData[]) {
  //allocate struct                                                                                                                                                                                                                                                             
  matrix *newMatrix = malloc(matrix);
  if(newMatrix == NULL) puts("struct allocation failed");
  memset(newMatrix, 0, sizeof(*newMatrix));
  newMatrix -> columns = columns;
  newMatrix -> rows = rows;

  //create initial data if none is given                                                                                                                                                                                                                                        
  if(initialData == NULL) {
    initialData = malloc(sizeof(float) * columns * rows);
    if(initialData == NULL) puts("Array allocation for initial data failed");
    memset(initialData, 0, sizeof(*initialData) * columns * rows);
  }

  //get the elements of each row                                                                                                                                                                                                                                                
  float **rowPointers;
  rowPointers = malloc(sizeof(float) * rows);
  if(rowPointers == NULL) puts("Array allocation for pointers failed");
  memset(rowPointers, 0, sizeof(float) * rows);
  float *rowData;
  int i;
  int j;

  for(i = 0; i < rows; i++) {
    printf("On row: %i\n", i + 1);
    //allocate data to store row data                                                                                                                                                                                                                                           
    rowData = malloc(sizeof(*rowData) * columns); //create array for row and record pointer                                                                                                                                                                                     
    if(rowData == NULL) printf("Array allocation for matrix row %i failed", i + 1);
    memset(rowData, 0, sizeof(*rowData) * columns);
    rowPointers[i] = rowData; //store pointer to row data                                                                                                                                                                                                                       

    for(j = 0; j < columns; j++) {
      rowData[j] = initialData[(i * columns) + j];
      printf("%f ", rowPointers[i][j]);
    }
    printf("\n");
  }

  newMatrix -> data = rowPointers;

  return *newMatrix;
}

void printMatrix(matrix matrix) {
  printf("Confirming pointer to array of pointers:\n%p\n", matrix.data);
  int i;
  puts("Confirming pointers to rows:");
  for(i = 0; i < matrix.rows; i++) {
    printf("%p\n", matrix.data[i]);
  }
  int j;

  for(int i = 0; i < matrix.rows; i++) {
    printf("On row: %i\n", i + 1);
    for(int j = 0; j < matrix.columns; j++) {
      printf("%f ", matrix.data[i][j]);
    }
    printf("\n");
  }
}

到目前为止,几乎一切正常。创建矩阵所涉及的一切都有效。我什至可以打印矩阵中的大部分数据。 大部分数据。运行代码给出输出:

going to create matrix
On row: 1
1.000000 2.000000 3.000000
On row: 2
4.000000 5.000000 6.000000
On row: 3
7.000000 8.000000 9.000000
matrix created
Pointer to array of pointers:
0x7f9a9ad00020
Pointers to rows:
0x7f9a9ad00030
0x7f9a9ad000e0
0x7f9a9ad000f0
going to print matrix
Confirming pointer to array of pointers:
0x7f9a9ad00020
Confirming pointers to rows:
0x7f9a9ad00030
0x7f9a9ad000e0
0x7f9a9ad000f0
On row: 1
-0.000000 0.000000 3.000000
On row: 2
4.000000 5.000000 6.000000
On row: 3
7.000000 8.000000 9.000000
printed matrix

如您所见,第 2 行和第 3 行中的所有内容都可以正确打印,甚至第 1 行中的第 3 列也可以正确打印。我觉得奇怪的是,在第 1 行中,第 1 列和第 2 列不正确,但第 3 列正确。如果您查看指向行的指针,您会注意到在内存中,第 1 行距第 2 行的距离比第 2 行距第 3 行的距离远得多。每次运行时都是这种情况。第 1 行与第 2 行的距离始终为 0x70,第 2 行与第 2 行的距离始终为 0x10。
不一致的是第 1 行第 1 列的数据。它并不总是 -0。有时它是一个非常大的数字,有时是一个非常小的数字。不过,第 1 行第 2 列的数据始终为 0。
其他输出示例:

On row: 1
1704328922398720.000000 0.000000 3.000000
On row: 2
4.000000 5.000000 6.000000
On row: 3
7.000000 8.000000 9.000000
printed matrix

鉴于打印语句中的所有信息,我无法解决此问题,也不知道是什么原因造成的。为什么矩阵中的前两个元素打印错误,为什么第 1 行总是离内存中的第 2 行那么远?

【问题讨论】:

  • @Meninx 我将它定义为一个浮点数**数据,而不是一个浮点数(*数据)[]。我可以像数组一样使用它。执行 data[1] 只是在地址“数据”指向 + 1 个浮点 * 的大小的 ram 中检索浮点 * 大小的数据。至少,我是这么理解的。
  • 使用float**而不是float*指向floats数组和columns * rows元素的任何特殊原因?
  • @PieterWitvoet 我希望能够使用 data[row][column] 而不是 data[row * totalColumns + columns] 访问数据
  • 好吧,如果这值得额外的内存分配和复杂性......无论如何,如果没有给出initialData,为什么只分配它来初始化您为浮动分配的内存?你不是freeing 它,所以这是内存泄漏。如果没有给出初始数据,只需memsetrowData,如果有初始数据,则对其进行初始化(则不需要memset)。
  • 你可以试试matrix *newMatrix = malloc(sizeof(matrix)); 然后newMatrix-&gt;data=malloc(sizeof(float*) * rows)

标签: c pointers matrix multidimensional-array


【解决方案1】:

struct 可能包含成员之间的填充以保持内存对齐。这意味着struct 的大小并不总是与其成员大小的总和相同。

所以,在createMatrix(),这一行:

matrix *newMatrix = malloc((sizeof(int) * 2) + (sizeof(float*) * rows));

...应该是:

matrix *newMatrix = malloc(sizeof(matrix));

另请注意,您的 float ** 已经是 matrix 类型的一部分,并且 它指向的float *数组是稍后动态分配的,所以你不需要 在这里为行指针添加额外的空间。


在传递NULL 时分配initialData 后的memset() 仅将第一行归零- 应将其更改为:

  memset(initialData, 0, sizeof(*initialData) * rows * columns);

当您分配行指针数组时,您计算的大小类型错误。所以这个:

rowPointers = malloc(sizeof(float) * columns);

应该是:

rowPointers = malloc(sizeof(float *) * rows);

以下 memset 应更改为:

memset(rowPointers, 0, sizeof(float *) * rows);

另外,在createMatrix() 中,你应该free(initialData) 如果你在函数中分配了它(即如果NULL 被传递)。请记住,在释放 matrix 对象时,您需要循环遍历每一行 free(),然后是行数组,然后是实际的 matrix 结构——因此您可能希望为此编写一个函数.

createMatrix 内部动态分配matrix 结构后,您还通过按值返回它来泄漏内存——因此malloc()ed 结构被泄漏,尽管内容被复制以供返回。要修复它,您应该返回指向动态分配结构的指针,或者修改函数以使用本地 matrix 变量而不是动态分配的变量。要做到这一点,只需对您的代码进行最少的更改,您可以使用:

matrix newMatrixStruct;
matrix *newMatrix = &newMatrixStruct;

而不是声明和malloc()s newMatrix的行。

【讨论】:

  • 看起来你修好了。我不知道结构中的填充。我知道内存泄漏,而不是导致的一个 initialData。
  • 嗯...更改后仍然出现同样的问题。
  • @Znapi 您是否在matrix.h 之前或之前包括stdio.hstdlib.hstring.h
  • 我将所有这些都包括在内,除了 string.h。
【解决方案2】:

在大量修改代码后,我能够解决自己的问题。
在我意识到矩阵的元素必须由matrix.data[y][x] 而不是matrix.data[x][y] 访问后,我切换了行和列。我发现了一些列和行混淆的地方,但在 3x3 矩阵中,它不会有任何区别。
我改变了整个结构的创建方式。我停止使用 malloc 先分配它,然后用数据填充它,而是这样做:
newMatrix {columns, rows, malloc(sizeof(float*) * columns)};
我之前使用 malloc 来分配结构,因为我在我的结构中为指针数组腾出空间,但是 malloc 会随机分配空间并给我指向它分配内存的位置的指针。我想我对指针和数组之间的界限感到困惑。 我还使用newMatrix.data 来引用指针数组,而不是创建数组rowData[rows],然后将指向行数据的指针存储在newMatrix.data 中。我想我在那里再次混淆了指针和数组。
因为我改变了行和列,数组matrix.data中的指针指向的不是行数据的数组,而是现在的列。
matrix.h修复后变成了这个:

#pragma once

typedef struct matrix {
  int columns;
  int rows;
  float **data;
} matrix;

matrix createMatrix(int columns, int rows, float initialData[]) {
  //create struct struct                                                                                                                                                                                                                                                             
  matrix newMatrix = {
    columns,
    rows,
    malloc(sizeof(float*) * columns) //create array of pointers and store the pointer to it
  };

  if(newMatrix.data == NULL) puts("pointer array allocation failed");
  memset(newMatrix.data, 0, sizeof(float*) * columns);

  //create initial data if none is given                                                                                                                                                                                                                                        
  if(initialData == NULL) {
    initialData = malloc(sizeof(float) * columns * rows);
    if(initialData == NULL) puts("Array allocation for initial data failed");
    memset(initialData, 0, sizeof(float) * columns * rows);
  }

  //get the elements of each column                                                                                                                                                                                                                                                
  float *columnData;
  int i;
  int j;

  for(i = 0; i < columns; i++) {
    printf("On column: %i\n", i + 1);
    //allocate data to store column data                                                                                                                                                                                                                                        
    columnData = malloc(sizeof(float) * rows); //create array for column and record pointer                                                                                                                                                                                     
    if(columnData == NULL) printf("Array allocation for matrix column %i failed", i + 1);
    memset(columnData, 0, sizeof(float) * rows);
    newMatrix.data[i] = columnData; //store pointer to column data                                                                                                                                                                                                              

    for(j = 0; j < rows; j++) {
      columnData[j] = initialData[(j * columns) + i];
      printf("%f ", newMatrix.data[i][j]);
    }
    printf("\n");
  }

  return newMatrix;
}

void printMatrix(matrix matrix) {
  printf("Confirming pointer to array of pointers:\n%p\n", matrix.data);
  int i;
  puts("Confirming pointers to columns:");
  for(i = 0; i < matrix.columns; i++) {
    printf("%p\n", matrix.data[i]);
  }
  int j;

  for(int i = 0; i < matrix.rows; i++) {
    printf("On row: %i\n", i + 1);
    for(int j = 0; j < matrix.columns; j++) {
      printf("%f ", matrix.data[j][i]);
    }
    printf("\n");
  }
}

请注意,如果通过了 NULL,我仍然没有解除分配矩阵并修复在分配 initialData 时引起的内存泄漏,但这不是问题的一部分。
主文件中唯一更改的行是:

for(i = 0; i < myMatrix.rows; i++) {

改为:

for(i = 0; i < myMatrix.columns; i++) {

在内存中,第 1 列与第 2 列的距离仍然比第 2 列与第 3 列的距离更远,我仍然不知道为什么,但这并没有影响我的代码。访问矩阵元素的问题只是我混淆了数组和指针的产物,并在我尝试修复它们时弄得一团糟。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    • 1970-01-01
    • 2018-01-28
    • 2015-08-01
    • 1970-01-01
    相关资源
    最近更新 更多